/*
 * htmlutil.c - HTML utility functions.
 */


/* Headers
 **********/

#include "all.h"
#pragma hdrstop

#include "htmlutil.h"
#include "wc_html.h"


/* Module Constants
 *******************/

#pragma data_seg(DATA_SEG_READ_ONLY)

PRIVATE_DATA CCHAR s_cszFileProtocol[] = "file:";
// (- 1) for null terminator.
#define FILE_PROTOCOL_LEN                 (sizeof(s_cszFileProtocol) - 1)

#pragma data_seg()


/***************************** Private Functions *****************************/


PRIVATE_CODE BOOL IsFileURL(PCSTR pcszURL)
{
   ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR));

   return(! _strnicmp(pcszURL, s_cszFileProtocol, FILE_PROTOCOL_LEN));
}


/****************************** Public Functions *****************************/


#ifdef DEBUG

PUBLIC_CODE BOOL IsValidElementType(UCHAR uchType)
{
   /* Allow any type value. */

   return(TRUE);
}


PUBLIC_CODE BOOL IsValidPCELEMENT(PCELEMENT pcelem)
{
   /* Allow any structure fields. */

   return(IS_VALID_READ_PTR(pcelem, CELEMENT));
}


PUBLIC_CODE BOOL IsValidPCImageInfo(PCImageInfo pcimginfo)
{
   /* Allow any structure fields. */

   return(IS_VALID_READ_PTR(pcimginfo, CImageInfo));
}


PUBLIC_CODE BOOL IsValidPCMWIN(PCMWIN pcmwin)
{
   /* Allow any structure fields. */

   return(IS_VALID_READ_PTR(pcmwin, CMWIN));
}


PUBLIC_CODE BOOL IsValidElementIndex(PCMWIN pcmwin, int iElem)
{
   return(EVAL(iElem >= 0) &&
          EVAL(iElem < pcmwin->w3doc->elementCount));
}


PUBLIC_CODE BOOL IsValidHTAtom(HTAtom atom)
{
   return(EVAL(atom != -1) &&
          EVAL(HTAtom_name(atom) != NULL));
}


PUBLIC_CODE BOOL IsValidPCPOSITION(PCPOSITION pcpos)
{
   /* Allow any structure fields. */

   return(IS_VALID_READ_PTR(pcpos, CPOSITION));
}


PUBLIC_CODE BOOL IsValidScreenX(int xScreen)
{
   return(EVAL(xScreen < GetSystemMetrics(SM_CXSCREEN)));
}


PUBLIC_CODE BOOL IsValidScreenY(int yScreen)
{
   return(EVAL(yScreen < GetSystemMetrics(SM_CYSCREEN)));
}


#endif   /* DEBUG */


PUBLIC_CODE BOOL PositionFromPoint(PCMWIN pcmwin, POINT ptDoc, PPOSITION ppos)
{
   BOOL bResult = FALSE;
#ifdef FEATURE_INTL
   BOOL bDBCS;
#endif

   ASSERT(IS_VALID_STRUCT_PTR(pcmwin, CMWIN));
   ASSERT(IS_VALID_STRUCT_PTR(&ptDoc, CPOINT));
   ASSERT(IS_VALID_WRITE_PTR(ppos, POSITION));

   ppos->elementIndex = -1;
   ppos->offset = -1;

   if (pcmwin &&
       pcmwin->w3doc &&
       pcmwin->w3doc->elementCount > 0)
   {
      int i;
      PCELEMENT pcelem;
	  RECT elRect;

      /*
       * BUGBUG: (Performance) (JCordell 4/1//95) We could iterate through the
       * line list more quickly than the element list here, and then start the
       * element search with the first element on the intersected line.
       */

      for (i = 0; i >= 0; i = pcelem->next)
      {
         pcelem = &(pcmwin->w3doc->aElements[i]);

		 if ( pcelem->type == ELE_FRAME )
		 	continue;

		 FrameToDoc( pcmwin->w3doc, i, &elRect );

         if (PtInRect(&elRect, ptDoc))
         {
            if (pcelem->type == ELE_TEXT)
            {
               int ncOffset;
               PGTRFont pfont;
               HDC hdc;

               ncOffset = ptDoc.x - elRect.left;

               if (IS_FLAG_SET(pcelem->lFlags, ELEFLAG_ANCHOR))
#ifdef FEATURE_INTL
                  pfont = STY_GetCPFont(GETMIMECP(pcmwin->w3doc),
                                 pcmwin->w3doc->pStyles, pcelem->iStyle,
                                 (pcelem->fontBits | gPrefs.cAnchorFontBits),
                                 pcelem->fontSize, pcelem->fontFace, TRUE);
#else
                  pfont = STY_GetFont(
                                 pcmwin->w3doc->pStyles, pcelem->iStyle,
                                 (pcelem->fontBits | gPrefs.cAnchorFontBits),
                                 pcelem->fontSize, pcelem->fontFace, TRUE);
#endif
               else
#ifdef FEATURE_INTL
                  pfont = STY_GetCPFont(GETMIMECP(pcmwin->w3doc), pcmwin->w3doc->pStyles, pcelem->iStyle,
                                      pcelem->fontBits, pcelem->fontSize, pcelem->fontFace, TRUE);
#else
                  pfont = STY_GetFont(pcmwin->w3doc->pStyles, pcelem->iStyle,
                                      pcelem->fontBits, pcelem->fontSize, pcelem->fontFace, TRUE);
#endif
               hdc = GetDC(pcmwin->win);

               if (hdc)
               {
                  HFONT hfontPrev;
                  int ncPos;

                  if (pfont && pfont->hFont)
                     hfontPrev = SelectObject(hdc, pfont->hFont);
                  else
                     hfontPrev = NULL;

                  for (ncPos = 1; ncPos <= pcelem->textLen; ncPos++)
                  {
                     SIZE size;

#ifdef FEATURE_INTL  // We should handle DBCS 1st byte and 2nd byte as one character.
                     if (IsFECodePage(GETMIMECP(pcmwin->w3doc))
                        && (bDBCS = IsDBCSLeadByteEx(GETMIMECP(pcmwin->w3doc), *(pcmwin->w3doc->pool+pcelem->textOffset+ncPos-1))))
                        ++ncPos;

                     if (EVAL(myGetTextExtentPointWithMIME(pcmwin->w3doc->iMimeCharSet, hdc, pcmwin->w3doc->pool + pcelem->textOffset, ncPos, &size)) && size.cx > ncOffset)
                     { 
                        ncPos -= (bDBCS) ? 1 : 0;
                        break;
                     }
#else  
                     if (EVAL(myGetTextExtentPoint(hdc, pcmwin->w3doc->pool +
                                                        pcelem->textOffset,
                                                   ncPos, &size)) &&
                         size.cx > ncOffset)
                        break;
#endif
                  }

                  if (hfontPrev)
                     EVAL(SelectObject(hdc, hfontPrev) != NULL);

                  ReleaseDC(pcmwin->win, hdc);

                  ppos->offset = ncPos - 1;
               }
            }
            else
               ASSERT(ppos->offset == -1);

            ppos->elementIndex = i;
            bResult = TRUE;

            break;
         }
      }
   }

   ASSERT((bResult ||
           (EVAL(ppos->elementIndex == -1) &&
            EVAL(ppos->offset == -1))) &&
          IS_VALID_STRUCT_PTR(ppos, CPOSITION));

   return(bResult);
}


PUBLIC_CODE BOOL MWinHasSelection(PCMWIN pcmwin)
{
   BOOL bHasSelection;

   ASSERT(IS_VALID_STRUCT_PTR(pcmwin, CMWIN));

   bHasSelection = (pcmwin &&
                    pcmwin->w3doc &&
                    pcmwin->w3doc->selStart.elementIndex != -1 &&
                    pcmwin->w3doc->selEnd.elementIndex != -1);

   ASSERT(! bHasSelection ||
          pcmwin->w3doc->elementCount > 0);

   return(bHasSelection);
}


PUBLIC_CODE BOOL IsPositionInSelection(PCMWIN pcmwin, PCPOSITION pcpos)
{
   BOOL bInSelection = FALSE;

   if (MWinHasSelection(pcmwin))
   {
      int i;

      ASSERT(pcmwin->w3doc->elementCount > 0);

      i = pcmwin->w3doc->selStart.elementIndex;

      do
      {
         /* Entering selection? */

         if (i == pcmwin->w3doc->selStart.elementIndex)
         {
            /* Yes. */

            if (i == pcpos->elementIndex)
               bInSelection = (pcmwin->w3doc->selStart.offset == -1 ||
                               pcpos->offset == -1 ||
                               pcmwin->w3doc->selStart.offset <= pcpos->offset);
            else
               bInSelection = TRUE;
         }

         /* Leaving selection? */

         if (i == pcmwin->w3doc->selEnd.elementIndex)
         {
            /* Yes. */

            if (i == pcpos->elementIndex)
               bInSelection = (bInSelection &&
                               (pcmwin->w3doc->selStart.offset == -1 ||
                                pcpos->offset == -1 ||
                                pcmwin->w3doc->selEnd.offset > pcpos->offset));
            else
               bInSelection = FALSE;

            break;
         }

         /* Found wholly selected element? */

         if (i == pcpos->elementIndex)
            /* Yes. */
            break;
         else
            i = pcmwin->w3doc->aElements[i].next;
      } while (i != -1);
   }

   return(bInSelection);
}


PUBLIC_CODE BOOL FullyQualifyURL(PCSTR pcszURL, PCSTR pcszBaseURL,
                                 PSTR *ppszFullURL)
{
   ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR));
   ASSERT(IS_VALID_STRING_PTR(pcszBaseURL, CSTR));
   ASSERT(IS_VALID_WRITE_PTR(ppszFullURL, PSTR));

   *ppszFullURL = HTParse(pcszURL, pcszBaseURL, (PARSE_PUNCTUATION |
                                                 PARSE_ACCESS |
                                                 PARSE_HOST |
                                                 PARSE_PATH |
                                                 PARSE_ANCHOR));

   ASSERT((*ppszFullURL &&
           IS_VALID_STRING_PTR(*ppszFullURL, STR)) ||
          EVAL(! *ppszFullURL));

   return(*ppszFullURL != NULL);
}


PUBLIC_CODE HRESULT GetURLFileSystemPath(PCSTR pcszURL, PCSTR pcszBaseURL,
                                         PSTR szFullPath,
                                         UINT ucFullPathBufLen)
{
   HRESULT hr;

   ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR));
   ASSERT(IS_VALID_STRING_PTR(pcszBaseURL, CSTR));
   ASSERT(IS_VALID_WRITE_BUFFER_PTR(szFullPath, STR, ucFullPathBufLen));

   if (ucFullPathBufLen > 0)
   {
      // Get fully qualified file: URL.

      if (IsFileURL(pcszURL))
      {
         PSTR pszFullURL;

         if (FullyQualifyURL(pcszURL, pcszBaseURL, &pszFullURL))
         {
            ASSERT(IsFileURL(pszFullURL));

            hr = FullyQualifyPath(pszFullURL + FILE_PROTOCOL_LEN, szFullPath,
                                  ucFullPathBufLen);

            if (hr == S_OK)
               TRACE_OUT(("GetURLFileSystemPath(): File system path for URL %s is %s.",
                          pszFullURL,
                          szFullPath));

            GTR_FREE(pszFullURL);
            pszFullURL = NULL;
         }
         else
            hr = E_OUTOFMEMORY;
      }
      else
      {
         PSTR pszCachePath;

         // Get full path to cached copy of referent.

         if (pszCachePath = PszGetDCachePath(pcszURL, NULL, NULL))
         {
            ASSERT(IS_VALID_STRING_PTR(pszCachePath, STR));

            if (lstrlen(pszCachePath) < ucFullPathBufLen)
            {
               lstrcpy(szFullPath, pszCachePath);
               hr = S_OK;

               TRACE_OUT(("GetURLFileSystemPath(): Cache path for URL %s is %s.",
                          pcszURL,
                          szFullPath));
            }
            else
               hr = S_FALSE;

            GTR_FREE(pszCachePath);
            pszCachePath = NULL;
         }
         else
         {
            hr = E_OUTOFMEMORY;
         }
      }
   }
   else
      hr = S_FALSE;

   if (hr != S_OK &&
       ucFullPathBufLen > 0)
      *szFullPath = '\0';

   ASSERT((hr == S_OK &&
           IS_VALID_STRING_PTR(szFullPath, STR) &&
           EVAL(lstrlen(szFullPath) < ucFullPathBufLen)) ||
          (hr != S_OK &&
           EVAL(! ucFullPathBufLen ||
                ! *szFullPath)));

   return(hr);
}


PUBLIC_CODE HRESULT GetURLFromHREF(PCMWIN pcmwin, int iElem, PSTR *ppszURL)
{
   HRESULT hr;
   PCELEMENT pcelem;

   *ppszURL = NULL;

   pcelem = &(pcmwin->w3doc->aElements[iElem]);

   if (EVAL(IS_FLAG_SET(pcelem->lFlags, ELEFLAG_ANCHOR)) &&
       EVAL(pcelem->hrefLen > 0))
   {
      PSTR pszHREF;

      // (+ 1) for null terminator.
      if (AllocateMemory(pcelem->hrefLen + 1, &pszHREF))
      {
         // (+ 1) for null terminator.
         MyLStrCpyN(pszHREF, &(pcmwin->w3doc->pool[pcelem->hrefOffset]),
                    pcelem->hrefLen + 1);

         ASSERT(IS_VALID_STRING_PTR(pcmwin->w3doc->szActualURL, CSTR));

         hr = FullyQualifyURL(pszHREF, pcmwin->w3doc->szActualURL, ppszURL)
              ? S_OK : E_OUTOFMEMORY;

         if (hr == S_OK)
            TRACE_OUT(("GetURLFromHREF(): Element %d's URL is %s.",
                       iElem,
                       *ppszURL));

         GTR_FREE(pszHREF);
         pszHREF = NULL;
      }
      else
         hr = E_OUTOFMEMORY;
   }
   else
   {
      WARNING_OUT(("GetURLFromHREF(): Unable to retrieve URL for element %d.  No HREF.",
                   iElem));

      hr = S_FALSE;
   }

   ASSERT((hr == S_OK &&
           IS_VALID_STRING_PTR(*ppszURL, STR)) ||
          (hr != S_OK &&
           ! *ppszURL));

   return(hr);
}


PUBLIC_CODE HRESULT GetElementText(PCMWIN pcmwin, int iElem, PSTR *ppszName)
{
   HRESULT hr;
   PCELEMENT pcelem;

   *ppszName = NULL;

   pcelem = &(pcmwin->w3doc->aElements[iElem]);

   if (pcelem->textLen > 0)
   {
      // (+ 1) for null terminator.
      if (AllocateMemory(pcelem->textLen + 1, ppszName))
      {
         // (+ 1) for null terminator.
         MyLStrCpyN(*ppszName, &(pcmwin->w3doc->pool[pcelem->textOffset]),
                    pcelem->textLen + 1);

         hr = S_OK;

         TRACE_OUT(("GetElementText(): Element %d's text is \"%s\".",
                    iElem,
                    *ppszName));
      }
      else
         hr = E_OUTOFMEMORY;
   }
   else
   {
      WARNING_OUT(("GetElementText(): No text for element %d.",
                   iElem));

      hr = S_FALSE;
   }

   ASSERT((hr == S_OK &&
           IS_VALID_STRING_PTR(*ppszName, STR)) ||
          (hr != S_OK &&
           ! *ppszName));

   return(hr);
}


PUBLIC_CODE int OpenLink(PMWIN pmwin, int iElem, DWORD dwFlags)
{
   int nResult;
   PSTR pszURL;

   ASSERT(IS_VALID_STRUCT_PTR(pmwin, CMWIN));
   ASSERT(IsValidElementIndex(pmwin, iElem));
   ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_OPENLINK_FLAGS));

   if (GetURLFromHREF(pmwin, iElem, &pszURL) == S_OK)
   {
      if (IS_FLAG_SET(dwFlags, OPENLINK_FL_NEW_WINDOW))
         nResult = GTR_NewWindow(pszURL, pmwin->request->referer, 0, 0, 0, NULL,
                                 NULL);
      else
         nResult = TW_LoadDocument(pmwin, pszURL, TW_LD_FL_RECORD, NULL,
                                   pmwin->request->referer);

      GTR_FREE(pszURL);
      pszURL = NULL;
   }

   return(nResult);
}


PUBLIC_CODE BOOL ElementIsImagePlaceHolder(PCELEMENT pcelem)
{
   ASSERT(IS_VALID_STRUCT_PTR(pcelem, CELEMENT));

   return((pcelem->type == ELE_IMAGE ||
           pcelem->type == ELE_FORMIMAGE) &&
          EVAL(pcelem->myImage != NULL) &&
          IS_FLAG_SET(pcelem->myImage->flags, (IMG_ERROR | IMG_MISSING | IMG_NOTLOADED)) &&
          EVAL(IS_FLAG_CLEAR(pcelem->myImage->flags, IMG_LOADING)));
}


PUBLIC_CODE BOOL ElementIsValidImage(PCELEMENT pcelem)
{
    BOOL bResult;

    ASSERT(IS_VALID_STRUCT_PTR(pcelem, CELEMENT));

    bResult = ((pcelem->type == ELE_IMAGE ||
                pcelem->type == ELE_FORMIMAGE) &&
               EVAL(pcelem->myImage != NULL) &&
               IS_FLAG_CLEAR(pcelem->myImage->flags, (IMG_ERROR |
                                                      IMG_MISSING |
                                                      IMG_NOTLOADED |
                                                      IMG_LOADING)));

    TRACE_OUT(("ElementIsValidImage(): Element %s a valid image.",
               bResult ? "is" : "is not"));

    return(bResult);
}


PUBLIC_CODE BOOL LoadImageFromPlaceholder(PMWIN pmwin, int iElem)
{
   BOOL bResult = FALSE;
   PCELEMENT pcelem;

   ASSERT(IS_VALID_STRUCT_PTR(pmwin, CMWIN));
   ASSERT(IS_VALID_STRUCT_PTR(pmwin, CMWIN));
   ASSERT(IsValidElementIndex(pmwin, iElem));

   pcelem = &(pmwin->w3doc->aElements[iElem]);

   if (EVAL(ElementIsImagePlaceHolder(pcelem)))
   {
      struct Params_Image_LoadAll *pila;

      if (AllocateMemory(sizeof(*pila), &pila))
      {

#ifdef FEATURE_IMG_THREADS

         pila->tw = pmwin;
         pila->bLocalOnly = FALSE;
         pila->bNoImageCache = FALSE;
         pila->decoderObject = NULL;
         pila->parentThread = NULL;
         pila->bJustOne = TRUE;
         pila->nEl = iElem;

         /* Image_LoadAll_Async() tolerates pila->tw not being GIMGMASTER. */
         bResult = (Async_StartThread(Image_LoadAll_Async, pila, pmwin)
                    != NULL);

#else

         pila->tw = pmwin;
         pil->bLocalOnly = FALSE;
         pil->nEl = iElem;

         bResult = (Async_StartThread(Image_LoadOneImage_Async, pila, pmwin)
                    != NULL);

#endif   /* FEATURE_IMG_THREADS */

         BHBar_SetStatusField(pmwin, NULL);
         TBar_UpdateTBar(pmwin);
      }
   }

#ifdef FEATURE_VRML
   if (!pmwin->w3doc->aElements[iElem].pVrml) {
     SetFocus(pmwin->hWndFrame);
   }
#else
     SetFocus(pmwin->hWndFrame);
#endif

   return(bResult);
}