/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Mozilla OS/2 libraries.
 *
 * The Initial Developer of the Original Code is
 * John Fairhurst, <john_fairhurst@iname.com>.
 * Portions created by the Initial Developer are Copyright (C) 1999
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Pierre Phaneuf <pp@ludusdesign.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "nsGfxDefs.h"
#include "nsIPref.h"
#include "nsIServiceManager.h"
#include "nsILanguageAtomService.h"
#include "nsICharsetConverterManager.h"
#include "nsLocaleCID.h"
#include "nsILocaleService.h"
#include "nsICharRepresentable.h"
#include "nsISaveAsCharset.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsFontMetricsOS2.h"
#include "nsQuickSort.h"
#include "nsTextFormatter.h"
#include "prmem.h"
#include "plhash.h"
#include "prprf.h"
#include "nsReadableUtils.h"
#include "nsUnicodeRange.h"
#include "nsUnicharUtils.h"

#include "nsOS2Uni.h"
#include <math.h>

#ifdef MOZ_MATHML
  #include <math.h>
#endif

#undef USER_DEFINED
#define USER_DEFINED "x-user-def"

#define NS_REPLACEMENT_CHAR  PRUnichar(0x003F) // question mark

static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
static NS_DEFINE_CID(kPrefCID, NS_PREF_CID);
static NS_DEFINE_CID(kLocaleServiceCID, NS_LOCALESERVICE_CID); 

#define NS_MAX_FONT_WEIGHT 900
#define NS_MIN_FONT_WEIGHT 100

#undef CHAR_BUFFER_SIZE
#define CHAR_BUFFER_SIZE 1024


/***** Global variables *****/
nsTHashtable<GlobalFontEntry>* nsFontMetricsOS2::gGlobalFonts = nsnull;
PRBool        nsFontMetricsOS2::gSubstituteVectorFonts = PR_TRUE;
PLHashTable  *nsFontMetricsOS2::gFamilyNames = nsnull;

static nsIPref           *gPref = nsnull;
static nsIAtom           *gUsersLocale = nsnull;
static nsIAtom           *gSystemLocale = nsnull;
static nsIAtom           *gUserDefined = nsnull;
static int                gInitialized = 0;
static ULONG              gSystemCodePage = 0;
static ULONG              gCurrHashValue = 1;

#ifdef USE_FREETYPE
PRBool nsFontMetricsOS2::gUseFTFunctions = PR_FALSE;
static nsISaveAsCharset* gFontSubstituteConverter = nsnull;

static nsIAtom* gJA = nsnull;
static nsIAtom* gKO = nsnull;
static nsIAtom* gZHTW = nsnull;
static nsIAtom* gZHCN = nsnull;
static nsIAtom* gZHHK = nsnull;
#endif /* use_freetype */

/***** Charset structures *****/
enum nsCharset
{
  eCharset_DEFAULT = 0,
  eCharset_ANSI,
  eCharset_EASTEUROPE,
  eCharset_RUSSIAN,
  eCharset_GREEK,
  eCharset_TURKISH,
  eCharset_HEBREW,
  eCharset_ARABIC,
  eCharset_BALTIC,
  eCharset_THAI,
  eCharset_SHIFTJIS,
  eCharset_GB2312,
  eCharset_HANGEUL,
  eCharset_CHINESEBIG5,
  eCharset_JOHAB,
  eCharset_COUNT
};

struct nsCharsetInfo
{
  char*    mName;
/*  USHORT   mMask; */
  PRUint16 mCodePage;
  char*    mLangGroup;
};

static nsCharsetInfo gCharsetInfo[eCharset_COUNT] =
{
  { "DEFAULT",     /* 0,                */  0,    "" },
  { "ANSI",        /* FM_DEFN_LATIN1,   */  1252, "x-western" },
  { "EASTEUROPE",  /* FM_DEFN_LATIN2,   */  1250, "x-central-euro" },
  { "RUSSIAN",     /* FM_DEFN_CYRILLIC, */  1251, "x-cyrillic" },
  { "GREEK",       /* FM_DEFN_GREEK,    */  813,  "el" },
  { "TURKISH",     /* 0,                */  1254, "tr" },
  { "HEBREW",      /* FM_DEFN_HEBREW,   */  1208,  "he" },
  { "ARABIC",      /* FM_DEFN_ARABIC,   */  864,  "ar" },
  { "BALTIC",      /* 0,                */  1257, "x-baltic" },
  { "THAI",        /* FM_DEFN_THAI,     */  874,  "th" },
  { "SHIFTJIS",    /* FM_DEFN_KANA,     */  932,  "ja" },
  { "GB2312",      /* FM_DEFN_KANA,     */  1381, "zh-CN" },
  { "HANGEUL",     /* FM_DEFN_KANA,     */  949,  "ko" },
  { "CHINESEBIG5", /* FM_DEFN_KANA,     */  950,  "zh-TW" },
  { "JOHAB",       /* FM_DEFN_KANA,     */  1361, "ko-XXX", }
};


/**********************************************************
    nsFontMetricsOS2
 **********************************************************/    

static void
FreeGlobals(void)
{
  gInitialized = 0;

  NS_IF_RELEASE(gPref);
  NS_IF_RELEASE(gUsersLocale);
  NS_IF_RELEASE(gSystemLocale);
  NS_IF_RELEASE(gUserDefined);
#ifdef USE_FREETYPE
  if (nsFontMetricsOS2::gUseFTFunctions) {
    // disable the freetype engine
    nsFontMetricsOS2FT::pfnFt2EnableFontEngine(FALSE);

    NS_IF_RELEASE(gJA);
    NS_IF_RELEASE(gKO);
    NS_IF_RELEASE(gZHTW);
    NS_IF_RELEASE(gZHCN);
    NS_IF_RELEASE(gZHHK);
  }
#endif /* use_freetype */

  if (nsFontMetricsOS2::gGlobalFonts) {
    nsFontMetricsOS2::gGlobalFonts->Clear();
    delete nsFontMetricsOS2::gGlobalFonts;
    nsFontMetricsOS2::gGlobalFonts = nsnull;
  }

  if (nsFontMetricsOS2::gFamilyNames) {
    PL_HashTableDestroy(nsFontMetricsOS2::gFamilyNames);
    nsFontMetricsOS2::gFamilyNames = nsnull;
  }
}

class nsFontCleanupObserver : public nsIObserver {
public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSIOBSERVER

  nsFontCleanupObserver() { }
  virtual ~nsFontCleanupObserver() {}
};

NS_IMPL_ISUPPORTS1(nsFontCleanupObserver, nsIObserver)

NS_IMETHODIMP nsFontCleanupObserver::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
{
  if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
    FreeGlobals();
  }
  return NS_OK;
}

static nsFontCleanupObserver *gFontCleanupObserver;

static nsresult
InitGlobals(void)
{
  CallGetService(kPrefCID, &gPref);
  if (!gPref) {
    FreeGlobals();
    return NS_ERROR_FAILURE;
  }

  nsCOMPtr<nsILanguageAtomService> langService;
  langService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
  if (langService) {
    NS_IF_ADDREF(gUsersLocale = langService->GetLocaleLanguageGroup());
  }
  if (!gUsersLocale) {
    gUsersLocale = NS_NewAtom("x-western");
  }
  if (!gUsersLocale) {
    FreeGlobals();
    return NS_ERROR_OUT_OF_MEMORY;
  }

  UINT cp = ::WinQueryCp(HMQ_CURRENT);
  for (int i = 1; i < eCharset_COUNT; ++i) {
    if (gCharsetInfo[i].mCodePage == cp) {
      gSystemLocale = NS_NewAtom(gCharsetInfo[i].mLangGroup);
      break;
    }
  }
  if (!gSystemLocale) {
    gSystemLocale = gUsersLocale;
    NS_ADDREF(gSystemLocale);
  }

  gUserDefined = NS_NewAtom(USER_DEFINED);
  if (!gUserDefined) {
    FreeGlobals();
    return NS_ERROR_OUT_OF_MEMORY;
  }
#ifdef USE_FREETYPE
  if (nsFontMetricsOS2::gUseFTFunctions) {
    // enable the freetype engine
    nsFontMetricsOS2FT::pfnFt2EnableFontEngine(TRUE);

    gJA = NS_NewAtom("ja");
    if (!gJA) {
      FreeGlobals();
      return NS_ERROR_OUT_OF_MEMORY;
    }
    gKO = NS_NewAtom("ko");
    if (!gKO) {
      FreeGlobals();
      return NS_ERROR_OUT_OF_MEMORY;
    }
    gZHCN = NS_NewAtom("zh-CN");
    if (!gZHCN) {
      FreeGlobals();
      return NS_ERROR_OUT_OF_MEMORY;
    }
    gZHTW = NS_NewAtom("zh-TW");
    if (!gZHTW) {
      FreeGlobals();
      return NS_ERROR_OUT_OF_MEMORY;
    }
    gZHHK = NS_NewAtom("zh-HK");
    if (!gZHHK) {
      FreeGlobals();
      return NS_ERROR_OUT_OF_MEMORY;
    }
  }
#endif /* use_freetype */

  // Not all versions of OS/2 support the 1386 and 943 codepages, so by default
  // we use 1381 and 932 (see the declaration of gCharsetInfo above).
  // Here, we check to see if the OS supports the newer codepages, and if so,
  // use them.
  ULONG numCP = ::WinQueryCpList((HAB)0, 0, NULL);
  if (numCP > 0) {
     ULONG * pCPList = (ULONG*)nsMemory::Alloc(numCP*sizeof(ULONG));
     if (::WinQueryCpList((HAB)0, numCP, pCPList)) {
        for (PRUint32 i = 0; i < numCP; i++) {
           if (pCPList[i] == 1386) {
              gCharsetInfo[11].mCodePage = 1386;
           } else if (pCPList[i] == 943) {
              gCharsetInfo[10].mCodePage = 943;
           }
        }
     }
     nsMemory::Free(pCPList);
  }

  gSystemCodePage = ::WinQueryCp(HMQ_CURRENT);

  nsresult res;

  if (!nsFontMetricsOS2::gGlobalFonts) {
    res = nsFontMetricsOS2::InitializeGlobalFonts();
    if (NS_FAILED(res)) {
      FreeGlobals();
      return res;
    }
  }

  res = gPref->GetBoolPref("browser.display.substitute_vector_fonts",
                                    &nsFontMetricsOS2::gSubstituteVectorFonts);
  NS_ASSERTION( NS_SUCCEEDED(res), "Could not get pref 'browser.display.substitute_vector_fonts'" );

  //register an observer to take care of cleanup
  gFontCleanupObserver = new nsFontCleanupObserver();
  NS_ASSERTION(gFontCleanupObserver, "failed to create observer");
  if (gFontCleanupObserver) {
    // register for shutdown
    nsresult rv;
    nsCOMPtr<nsIObserverService> observerService(do_GetService("@mozilla.org/observer-service;1", &rv));
    if (NS_SUCCEEDED(rv)) {
      rv = observerService->AddObserver(gFontCleanupObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
    }    
  }
  gInitialized = 1;

  return NS_OK;
}

#ifdef DEBUG_FONT_STRUCT_ALLOCS
unsigned long nsFontMetricsOS2::mRefCount = 0;
#endif

nsFontMetricsOS2::nsFontMetricsOS2()
{
#ifdef DEBUG_FONT_STRUCT_ALLOCS
  mRefCount++;
  printf("+++ nsFontMetricsOS2 total = %d\n", mRefCount);
#endif
}

nsFontMetricsOS2::~nsFontMetricsOS2()
{
  mFontHandle = nsnull; // released below
  mUnicodeFont = nsnull; // release below
  mWesternFont = nsnull; // release below

  for (PRInt32 i = mLoadedFonts.Count()-1; i >= 0; --i) {
    delete (nsFontOS2*)mLoadedFonts[i];
  }
  mLoadedFonts.Clear();

  if (mDeviceContext) {
    // Notify our device context that owns us so that it can update its font cache
    mDeviceContext->FontMetricsDeleted(this);
    mDeviceContext = nsnull;
  }
#ifdef DEBUG_FONT_STRUCT_ALLOCS
  mRefCount--;
  printf("--- nsFontMetricsOS2 total = %d\n", mRefCount);
#endif
}

NS_IMPL_ISUPPORTS1(nsFontMetricsOS2, nsIFontMetrics)

NS_IMETHODIMP
nsFontMetricsOS2::Init( const nsFont &aFont,  nsIAtom* aLangGroup,
                        nsIDeviceContext *aContext)
{
  nsresult res;
  if (!gInitialized) {
    res = InitGlobals();
    if (NS_FAILED(res)) {
      return res;
    }
  }

  mFont = aFont;
  mLangGroup = aLangGroup;

  //don't addref this to avoid circular refs
  mDeviceContext = (nsDeviceContextOS2 *) aContext;
  return RealizeFont();
}

NS_IMETHODIMP
nsFontMetricsOS2::Destroy()
{
  mDeviceContext = nsnull;
  return NS_OK;
}

// Utility; delete [] when done.
static FONTMETRICS* getMetrics( long &lFonts, PCSZ facename, HPS hps)
{
   LONG lWant = 0;
   lFonts = GFX (::GpiQueryFonts(hps, QF_PUBLIC | QF_PRIVATE,
                                 facename, &lWant, 0, 0),
                 GPI_ALTERROR);
   if (!lFonts) {
      return NULL;
   }
   FONTMETRICS* pMetrics = (FONTMETRICS*)nsMemory::Alloc(lFonts * sizeof(FONTMETRICS));

   GFX (::GpiQueryFonts(hps, QF_PUBLIC | QF_PRIVATE, facename,
                        &lFonts, sizeof (FONTMETRICS), pMetrics),
        GPI_ALTERROR);

   return pMetrics;
}

/* For a vector font, we create a charbox based directly on mFont.size, so
 * we don't need to calculate anything here.  For image fonts, though, we
 * want to find the closest match for the given mFont.size.
 */
nsFontOS2*
nsFontMetricsOS2::SetFontHandle(HPS aPS, GlobalFontEntry* aEntry,
                                nsMiniMetrics* aMetrics, PRBool aDoFakeEffects)
{
  nsFontOS2* font;
#ifdef USE_FREETYPE
  if (gUseFTFunctions) {
    font = new nsFontOS2FT();
  } else
#endif
  {
    font = new nsFontOS2();
  }

  strcpy(font->mFattrs.szFacename, aMetrics->szFacename);
  if (aMetrics->fsDefn & FM_DEFN_OUTLINE ||
      !mDeviceContext->SupportsRasterFonts()) {
    font->mFattrs.fsFontUse = FATTR_FONTUSE_OUTLINE |
                               FATTR_FONTUSE_TRANSFORMABLE;
  }
  if (aMetrics->fsType & FM_TYPE_MBCS)
    font->mFattrs.fsType |= FATTR_TYPE_MBCS;
  if (aMetrics->fsType & FM_TYPE_DBCS)
    font->mFattrs.fsType |= FATTR_TYPE_DBCS;

  if (aDoFakeEffects) {
    // fake the effects
    if (mFont.weight > NS_FONT_WEIGHT_NORMAL)
      font->mFattrs.fsSelection |= FATTR_SEL_BOLD;
    if (mFont.style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE))
      font->mFattrs.fsSelection |= FATTR_SEL_ITALIC;
  }

#ifdef USE_FREETYPE
  // We only want to set the codepage in the freetype case if the codepage for
  // the font is 65400 (a symbol font).
  if (!gUseFTFunctions || aEntry->mCodePage == 65400)
#endif
    font->mFattrs.usCodePage = aEntry->mCodePage;

#ifdef PERF_HASGLYPH_CHAR_MAP
  if (gUseFTFunctions) {
    if (aEntry->mHaveCheckedCharMap == nsnull) {
      aEntry->mHaveCheckedCharMap = (PRUint32*)calloc(UCS2_MAP_LEN, sizeof(PRUint32));
      aEntry->mRepresentableCharMap = (PRUint32*)calloc(UCS2_MAP_LEN, sizeof(PRUint32));
    }
    font->mHaveCheckedCharMap = aEntry->mHaveCheckedCharMap;
    font->mRepresentableCharMap = aEntry->mRepresentableCharMap;
  }
#endif

  float app2dev, reqEmHeight;
  app2dev = mDeviceContext->AppUnitsToDevUnits();
  reqEmHeight = mFont.size * app2dev;

  FATTRS* fattrs = &(font->mFattrs);
  if (fattrs->fsFontUse == 0)  // if image font
  {
    long lFonts = 0;
    FONTMETRICS* pMetrics = getMetrics( lFonts, fattrs->szFacename, aPS);

    int reqPointSize = NSTwipsToIntPoints(mFont.size);
    int browserRes = mDeviceContext->GetDPI();

    int minSize = 99, maxSize = 0, curEmHeight = 0;
    for (int i = 0; i < lFonts; i++)
    {
      // If we are asked for a specific point size for which we have an
      // appropriate font, use it.  This avoids us choosing an incorrect
      // size due to rounding issues
      if (pMetrics[i].sYDeviceRes == browserRes &&
          pMetrics[i].sNominalPointSize / 10 == reqPointSize)
      {
        // image face found fine, set required size in fattrs.
        curEmHeight = pMetrics[i].lEmHeight;
        fattrs->lMaxBaselineExt = pMetrics[i].lMaxBaselineExt;
        fattrs->lAveCharWidth = pMetrics[i].lAveCharWidth;
        minSize = maxSize = pMetrics[i].lEmHeight;
        break;
      }
      else
      {
        if (fabs(pMetrics[i].lEmHeight - reqEmHeight) < 
            fabs(curEmHeight - reqEmHeight))
        {
          curEmHeight = pMetrics[i].lEmHeight;
          fattrs->lMaxBaselineExt = pMetrics[i].lMaxBaselineExt;
          fattrs->lAveCharWidth = pMetrics[i].lAveCharWidth;
        }
        else if (fabs(pMetrics[i].lEmHeight - reqEmHeight) == 
                 fabs(curEmHeight - reqEmHeight))
        {
          if ((pMetrics[i].lEmHeight) > curEmHeight)
          {
            curEmHeight = pMetrics[i].lEmHeight;
            fattrs->lMaxBaselineExt = pMetrics[i].lMaxBaselineExt;
            fattrs->lAveCharWidth = pMetrics[i].lAveCharWidth;
          }
        }
      }
      
      // record the min/max point size available for given font
      if (pMetrics[i].lEmHeight > maxSize)
        maxSize = pMetrics[i].lEmHeight;
      if (pMetrics[i].lEmHeight < minSize)
        minSize = pMetrics[i].lEmHeight;
    }

    nsMemory::Free(pMetrics);
    
    // Enable font substitution if the requested size is outside of the range
    //  of available sizes by more than 3
    if (reqEmHeight < minSize - 3 ||
        reqEmHeight > maxSize + 3)
    {
      // If the browser.display.substitute_vector_fonts pref is set, then we
      //  exchange Times New Roman for Tms Rmn and Helvetica for Helv if the 
      //  requested points size is less than the minimum or more than the 
      //  maximum point size available for Tms Rmn and Helv.
      nsAutoString alias;
      if (gSubstituteVectorFonts &&
          GetVectorSubstitute(aPS, aEntry->GetKey(), alias))
      {
        strcpy(fattrs->szFacename, NS_LossyConvertUCS2toASCII(alias).get());
        fattrs->fsFontUse = FATTR_FONTUSE_OUTLINE | FATTR_FONTUSE_TRANSFORMABLE;
        fattrs->fsSelection &= ~(FM_SEL_BOLD | FM_SEL_ITALIC);
      }
    }
    
    if (fattrs->fsFontUse == 0) {    // if still image font
      reqEmHeight = curEmHeight;
    }
  }

   // Add effects
  if (mFont.decorations & NS_FONT_DECORATION_UNDERLINE) {
    fattrs->fsSelection |= FATTR_SEL_UNDERSCORE;
  }
  if (mFont.decorations & NS_FONT_DECORATION_LINE_THROUGH) {
    fattrs->fsSelection |= FATTR_SEL_STRIKEOUT;
  }

#ifdef USE_FREETYPE
  if (!gUseFTFunctions)
#endif
  {
     // Encoding:
     //  There doesn't seem to be any encoding stuff yet, so guess.
     //  (XXX unicode hack; use same codepage as converter!)
    const char* langGroup;
    mLangGroup->GetUTF8String(&langGroup);
    for (int j=0; j < eCharset_COUNT; j++)
    {
      if (langGroup[0] == gCharsetInfo[j].mLangGroup[0])
      {
        if (!strcmp(langGroup, gCharsetInfo[j].mLangGroup))
        {
          mConvertCodePage = gCharsetInfo[j].mCodePage;
          break;
        }
      }
    }
  
    // Symbols fonts must be created with codepage 65400,
    // so use 65400 for the fattrs codepage. We still do
    // conversions with the charset codepage
    if (fattrs->usCodePage != 65400) {
      fattrs->usCodePage = mConvertCodePage;
    }
  }

  // set up the charbox;  set for image fonts also, in case we need to
  //  substitute a vector font later on (for UTF-8, etc)
  long lFloor = NSToIntFloor(reqEmHeight); 
  font->mCharbox.cx = MAKEFIXED(lFloor, (reqEmHeight - (float)lFloor) * 65536.0f);
  font->mCharbox.cy = font->mCharbox.cx;

  return font;
}

/* aName is a font family name.  see if fonts of that family exist
 *  if so, return font structure with family name
 */
nsFontOS2*
nsFontMetricsOS2::LoadFont(HPS aPS, const nsAString& aFontname)
{
  nsFontOS2* font = nsnull;

   // set style flags
  PRBool bBold = mFont.weight > NS_FONT_WEIGHT_NORMAL;
  PRBool bItalic = (mFont.style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE));
  USHORT flags = bBold ? FM_SEL_BOLD : 0;
  flags |= bItalic ? FM_SEL_ITALIC : 0;

  // always pass vector fonts to the printer
  nsAutoString fontptr;
  if (mDeviceContext->SupportsRasterFonts() ||
      !GetVectorSubstitute(aPS, aFontname, fontptr))
  {
    fontptr = aFontname;
  }

  GlobalFontEntry* globalEntry = gGlobalFonts->GetEntry(fontptr);
  if (globalEntry) {
    nsMiniMetrics* metrics = globalEntry->mMetrics;
    nsMiniMetrics* plainFont = nsnull;
    while (metrics) {
      if ((metrics->fsSelection & (FM_SEL_ITALIC | FM_SEL_BOLD)) == flags) {
        font = SetFontHandle(aPS, globalEntry, metrics, PR_FALSE);
        break;
      }

      // Save ref to 'plain' font (non-bold, non-italic)
      if (!plainFont && !(metrics->fsSelection & (FM_SEL_ITALIC | FM_SEL_BOLD)))
        plainFont = metrics;

      metrics = metrics->mNext;
    }

    // A font of family "familyname" and with the applied effects (bold &
    // italic) was not found.  Therefore, just look for a 'regular' font
    // (that is not italic and not bold), and then have the
    // system simulate the appropriate effects (see RealizeFont()).
    if (!font && plainFont) {
      font = SetFontHandle(aPS, globalEntry, plainFont, PR_TRUE);
    }
  }

  if (!font) {
    // If a font was not found, then maybe "familyname" is really a face name.
    // See if a font with that facename exists on system and load the font.
    long lFonts = 0;
    nsAutoCharBuffer facename;
    PRInt32 len;
    WideCharToMultiByte(0, PromiseFlatString(aFontname).get(),
                        aFontname.Length(), facename, len);
    FONTMETRICS* pMetrics = getMetrics(lFonts, facename.get(), aPS);

    if (lFonts > 0) {
      nsAutoChar16Buffer familyname;
      MultiByteToWideChar(0, pMetrics[0].szFamilyname,
                          strlen(pMetrics[0].szFamilyname), familyname, len);
      nsAutoString name(familyname.get());
      GlobalFontEntry* globalEntry = gGlobalFonts->GetEntry(name);
      if (globalEntry) {
        // Look through metrics for one that matches given facename
        nsMiniMetrics* metrics = globalEntry->mMetrics;
        while (metrics) {
          if (stricmp(metrics->szFacename, facename.get()) == 0) {
            font = SetFontHandle(aPS, globalEntry, metrics, PR_TRUE);
            break;
          }
          metrics = metrics->mNext;
        }
      }
    }
    nsMemory::Free(pMetrics);
  }

  if (font) {
    mLoadedFonts.AppendElement(font);
  }

  return font;
}

typedef struct nsFontFamilyName
{
  char* mName;
  char* mWinName;
} nsFontFamilyName;

static nsFontFamilyName gBadDBCSFontMapping[] =
{
  { "\xB7\xC2\xCB\xCE\xB3\xA3\xB9\xE6", "IBM Fang Song SC"},
  { "\xBA\xDA\xCC\xE5\xB3\xA3\xB9\xE6", "IBM Hei SC"},
  { "\xBF\xAC\xCC\xE5\xB3\xA3\xB9\xE6", "IBM Kai SC"},
  { "\x5B\x8B\x4F\x53\x5E\x38\x89\xC4", "IBM Song SC"},
  { "\x91\x76\x91\xCC\x8F\xED",         "IBM Song SC"},

  { "@\xB7\xC2\xCB\xCE\xB3\xA3\xB9\xE6", "@IBM Fang Song SC"},
  { "@\xBA\xDA\xCC\xE5\xB3\xA3\xB9\xE6", "@IBM Hei SC"},
  { "@\xBF\xAC\xCC\xE5\xB3\xA3\xB9\xE6", "@IBM Kai SC"},
  { "@\x5B\x8B\x4F\x53\x5E\x38\x89\xC4", "@IBM Song SC"},
  { "@\x91\x76\x91\xCC\x8F\xED",         "@IBM Song SC"},

  { "\xAC\xD1\xFA\x80\xFA\xBD\x8F\x82",  "MOEKai TC"},
  { "\xBC\xD0\xB7\xC7\xB7\xA2\xC5\xE9",  "MOEKai TC"},
  { "\xAC\xD1\xFA\x80\x15\xA7\x8F\x82",  "MOESung TC"},
  { "\xBC\xD0\xB7\xC7\xA7\xBA\xC5\xE9",  "MOESung TC"},
  { "\xCF\xCF\x14\xB6\x8F\x82",          "Hei TC"},
  { "\xA4\xA4\xB6\xC2\xC5\xE9",          "Hei TC"},

  { "@\xAC\xD1\xFA\x80\xFA\xBD\x8F\x82",  "@MOEKai TC"},
  { "@\xBC\xD0\xB7\xC7\xB7\xA2\xC5\xE9",  "@MOEKai TC"},
  { "@\xAC\xD1\xFA\x80\x15\xA7\x8F\x82",  "@MOESong TC"},
  { "@\xBC\xD0\xB7\xC7\xA7\xBA\xC5\xE9",  "@MOESong TC"},
  { "@\xCF\xCF\x14\xB6\x8F\x82",          "@Hei TC"},
  { "@\xA4\xA4\xB6\xC2\xC5\xE9",          "@Hei TC"},
  { nsnull, nsnull }
};

#ifdef DEBUG
PR_STATIC_CALLBACK(PLDHashOperator)
DebugOutputEnumFunc(GlobalFontEntry* aEntry, void* aData)
{
  printf("---------------------------------------------------------------------\n");
  printf(" [[]] %s\n", NS_LossyConvertUCS2toASCII(aEntry->GetKey()).get());
  nsMiniMetrics* metrics = aEntry->mMetrics;
  while (metrics) {
    printf("  %32s", metrics->szFacename);

    if (metrics->fsDefn & FM_DEFN_OUTLINE)
      printf(" : vector");
    else
      printf(" : bitmap");
      
    if (metrics->fsSelection & FM_SEL_BOLD)
      printf(" : bold");
    else
      printf(" :     ");
      
    if (metrics->fsSelection & FM_SEL_ITALIC)
      printf(" : italic");
    else
      printf(" :       ");

    if (metrics->fsType & FM_TYPE_DBCS ||
        metrics->fsType & FM_TYPE_MBCS)
      printf(" : M/DBCS");
    else
      printf(" :       ");

    printf(" : cp=%5d\n", aEntry->mCodePage );
    
    metrics = metrics->mNext;
  }

  return PL_DHASH_NEXT;
}
#endif

nsresult
nsFontMetricsOS2::InitializeGlobalFonts()
{
  gGlobalFonts = new nsTHashtable<GlobalFontEntry>;
  if (!gGlobalFonts->Init(64))
    return NS_ERROR_OUT_OF_MEMORY;

  HPS ps = ::WinGetScreenPS(HWND_DESKTOP);
  LONG lRemFonts = 0, lNumFonts;
  lNumFonts = GFX (::GpiQueryFonts(ps, QF_PUBLIC, NULL, &lRemFonts, 0, 0),
                   GPI_ALTERROR);
  FONTMETRICS* pFontMetrics = (PFONTMETRICS) nsMemory::Alloc(lNumFonts * sizeof(FONTMETRICS));
  lRemFonts = GFX (::GpiQueryFonts(ps, QF_PUBLIC, NULL, &lNumFonts,
                                   sizeof (FONTMETRICS), pFontMetrics),
                   GPI_ALTERROR);
  ::WinReleasePS(ps);

  for (int i = 0; i < lNumFonts; i++) {
    FONTMETRICS* font = &(pFontMetrics[i]);

    // The discrepencies between the Courier bitmap and outline fonts are
    // too much to deal with, so we only use the outline font
    if (strcmp(font->szFamilyname, "Courier") == 0 &&
        !(font->fsDefn & FM_DEFN_OUTLINE)) {
      continue;
    }

    // The facenames for Roman are "Tms Rmn...", for Swiss "Helv...".  This
    // conflicts with the actual "Tms Rmn" and "Helv" font families.  So, we
    // skip over these.
    if (strcmp(pFontMetrics[i].szFamilyname, "Roman") == 0 ||
        strcmp(pFontMetrics[i].szFamilyname, "Swiss") == 0) {
      continue;
    }

     // Problem:  OS/2 has many non-standard fonts that do not follow the
     //           normal Family-name/Face-name conventions (i.e. 'foo',
     //           'foo bold', 'foo italic', 'foo bold italic').  This is
     //           especially true for DBCS fonts (i.e. the 'WarpSans' family
     //           can contain the 'WarpSans', 'WarpSans Bold', and 'WarpSans
     //           Combined' faces).
     // Solution: Unfortunately, there is no perfect way to handle this.  After
     //           many attempts, we will attempt to remedy the situation by
     //           searching the Facename for certain indicators ('bold',
     //           'italic', 'oblique', 'regular').  If the Facename contains
     //           one of these indicators, then we will create the sort key
     //           based on the Familyname.  Otherwise, use the Facename.
    char* f;
    if (PL_strcasestr(font->szFacename, "bold") != nsnull ||
        PL_strcasestr(font->szFacename, "italic") != nsnull ||
        PL_strcasestr(font->szFacename, "oblique") != nsnull ||
        PL_strcasestr(font->szFacename, "regular") != nsnull ||
        PL_strcasestr(font->szFacename, "-normal") != nsnull)
    {
      f = NS_STATIC_CAST(char*, font->szFamilyname);
    } else {
      f = NS_STATIC_CAST(char*, font->szFacename);
    }
    nsAutoChar16Buffer fontname;
    PRInt32 len;
    MultiByteToWideChar(0, f, strlen(f), fontname, len);
    nsAutoString fontptr(fontname.get());

    // The fonts in gBadDBCSFontMapping do not display well in non-Chinese
    //   systems.  Map them to a more intelligible name.
    if (font->fsType & FM_TYPE_DBCS)
    {
      if ((gSystemCodePage != 1386) &&
          (gSystemCodePage != 1381) &&
          (gSystemCodePage != 950))
      {
        for (int i = 0; gBadDBCSFontMapping[i].mName != nsnull; i++) {
          if (strcmp(f, gBadDBCSFontMapping[i].mName) == 0)
          {
            CopyASCIItoUCS2(nsDependentCString(gBadDBCSFontMapping[i].mWinName),
                            fontptr);
            break;
          }
        }
      }
    }

    // Create entry for family name, or retrieve already created entry
    GlobalFontEntry* globalEntry = gGlobalFonts->PutEntry(fontptr);
    if (!globalEntry)
      return NS_ERROR_OUT_OF_MEMORY;

    // Init the nsMiniMetrics structure...
    nsMiniMetrics* metrics = new nsMiniMetrics;
    strcpy(metrics->szFacename, font->szFacename);
    metrics->fsType = font->fsType;
    metrics->fsDefn = font->fsDefn;
    metrics->fsSelection = font->fsSelection;
     // Set the FM_SEL_BOLD flag in fsSelection.  This makes the check for
     // bold and italic much easier in LoadFont
    if (font->usWeightClass > 5)
      metrics->fsSelection |= FM_SEL_BOLD;

    // The 'Lucida' set of fonts does not set the bold flag correctly on OS/2,
    // so we'll just set it properly.
    if (strncmp(font->szFamilyname, "Lucida", 6) == 0 &&
        PL_strcasestr(font->szFacename, "bold") != nsnull) {
      metrics->fsSelection |= FM_SEL_BOLD;
    }

    // ... and add it to globalEntry
    metrics->mNext = globalEntry->mMetrics;
    globalEntry->mMetrics = metrics;
    globalEntry->mCodePage = font->usCodePage;
  }

#ifdef DEBUG_pedemonte
  gGlobalFonts->EnumerateEntries(DebugOutputEnumFunc, nsnull);
  fflush(stdout);
#endif

  nsMemory::Free(pFontMetrics);

  return NS_OK;
}

nsFontOS2*
nsFontMetricsOS2::FindGlobalFont(HPS aPS, PRUint32 aChar)
{
  nsFontOS2* fh = nsnull;
  nsAutoString fontname;
  if (!IsDBCS())
    fontname.AssignLiteral("Helv");
  else
    fontname.AssignLiteral("Helv Combined");
  fh = LoadFont(aPS, fontname);
  NS_ASSERTION(fh, "Couldn't load default font - BAD things are happening");
  return fh;
}

nsFontOS2*
nsFontMetricsOS2::FindUserDefinedFont(HPS aPS, PRUint32 aChar)
{
  if (mIsUserDefined) {
    // the user-defined font is always loaded as the first font
    nsFontOS2* font = LoadFont(aPS, mUserDefined);
    mIsUserDefined = PR_FALSE;
    if (font && font->HasGlyph(aPS, aChar)) {
      return font;
    }
  }
  return nsnull;
}

static nsFontFamilyName gFamilyNameTable[] =
{
#ifdef MOZ_MATHML
  { "-moz-math-text",   "Times New Roman" },
  { "-moz-math-symbol", "Symbol" },
#endif
  { "times",           "Times New Roman" },
  { "times roman",     "Times New Roman" },

  { nsnull, nsnull }
};

static nsFontFamilyName gFamilyNameTableDBCS[] =
{
#ifdef MOZ_MATHML
  { "-moz-math-text",   "Times New Roman" },
  { "-moz-math-symbol", "Symbol" },
#endif
  { "times",           "Times New Roman" },
  { "times roman",     "Times New Roman" },
  { "warpsans",        "WarpSans Combined" },

  { nsnull, nsnull }
};

/*-----------------------
** Hash table allocation
**----------------------*/
static PLHashNumber PR_CALLBACK
HashKey(const void* aString)
{
  const nsString* str = (const nsString*)aString;
  return (PLHashNumber) nsCRT::HashCode(str->get());
}

static PRIntn PR_CALLBACK
CompareKeys(const void* aStr1, const void* aStr2)
{
  return nsCRT::strcmp(((const nsString*) aStr1)->get(),
    ((const nsString*) aStr2)->get()) == 0;
}

PR_STATIC_CALLBACK(void*) familyname_AllocTable(void *pool, size_t size)
{
  return nsMemory::Alloc(size);
}

PR_STATIC_CALLBACK(void) familyname_FreeTable(void *pool, void *item)
{
  nsMemory::Free(item);
}

PR_STATIC_CALLBACK(PLHashEntry*) familyname_AllocEntry(void *pool, const void *key)
{
  return PR_NEW(PLHashEntry);
}

PR_STATIC_CALLBACK(void) familyname_FreeEntry(void *pool, PLHashEntry *he, PRUint32 flag)
{
  delete (nsString *) (he->value);

  if (flag == HT_FREE_ENTRY)  {
    delete (nsString *) (he->key);
    nsMemory::Free(he);
  }
}

PLHashAllocOps familyname_HashAllocOps = {
  familyname_AllocTable, familyname_FreeTable,
  familyname_AllocEntry, familyname_FreeEntry
};

PLHashTable*
nsFontMetricsOS2::InitializeFamilyNames(void)
{
  if (!gFamilyNames) {
    gFamilyNames = PL_NewHashTable( 0, HashKey, CompareKeys, nsnull,
                                    &familyname_HashAllocOps, nsnull );
    if (!gFamilyNames) {
      return nsnull;
    }

    nsFontFamilyName* f;
    if (!IsDBCS()) {
      f = gFamilyNameTable;
    } else {
      f = gFamilyNameTableDBCS;
    }

    while (f->mName) {
      nsString* name = new nsString;
      nsString* winName = new nsString;
      if (name && winName) {
        name->AssignWithConversion(f->mName);
        winName->AssignWithConversion(f->mWinName);
        if (PL_HashTableAdd(gFamilyNames, name, (void*) winName)) { 
          ++f;
          continue;
        }
      }
      // if we reach here, no FamilyName was added to the hashtable
      if (name) delete name;
      if (winName) delete winName;
      return nsnull;
    }
  }

  return gFamilyNames;
}

nsFontOS2*
nsFontMetricsOS2::FindLocalFont(HPS aPS, PRUint32 aChar)
{
  if (!gFamilyNames) {
    if (!InitializeFamilyNames()) {
      return nsnull;
    }
  }
  while (mFontsIndex < mFonts.Count()) {
    if (mFontsIndex == mGenericIndex) {
      return nsnull;
    }

    nsString* name = mFonts.StringAt(mFontsIndex++);
    nsAutoString low(*name);
    ToLowerCase(low);
    nsString* winName = (nsString*) PL_HashTableLookup(gFamilyNames, &low);
    if (!winName) {
      winName = name;
    }
#ifdef DEBUG_FONT_SELECTION
    printf(" FindLocalFont(): attempting to load %s\n",
           NS_LossyConvertUCS2toASCII(*winName).get());
#endif
    nsFontOS2* font = LoadFont(aPS, *winName);
    if (font && font->HasGlyph(aPS, aChar)) {
      return font;
    }
  }
  return nsnull;
}

nsFontOS2*
nsFontMetricsOS2::LoadGenericFont(HPS aPS, PRUint32 aChar, const nsAString& aName)
{
  for (int i = mLoadedFonts.Count()-1; i >= 0; --i) {
    // woah, this seems bad
    const nsACString& fontName =
      nsDependentCString(((nsFontOS2*)mLoadedFonts[i])->mFattrs.szFacename);
    if (aName.Equals(NS_ConvertASCIItoUCS2(fontName),
                     nsCaseInsensitiveStringComparator()))
      return nsnull;

  }
#ifdef DEBUG_FONT_SELECTION
  printf(" LoadGenericFont(): attempting to load %s\n",
         NS_LossyConvertUCS2toASCII(aName).get());
#endif
  nsFontOS2* font = LoadFont(aPS, aName);
  if (font && font->HasGlyph(aPS, aChar)) {
    return font;
  }
  return nsnull;
}

struct GenericFontEnumContext
{
  HPS               mPS;        // IN
  PRUint32          mChar;      // IN
  nsFontOS2        *mFont;      // OUT
  nsFontMetricsOS2 *mMetrics;   // IN
};

static PRBool
GenericFontEnumCallback(const nsString& aFamily, PRBool aGeneric, void* aData)
{
  GenericFontEnumContext* context = (GenericFontEnumContext*)aData;
  HPS ps = context->mPS;
  PRUint32 ch = context->mChar;
  nsFontMetricsOS2* metrics = context->mMetrics;
  context->mFont = metrics->LoadGenericFont(ps, ch, aFamily);
  if (context->mFont) {
    return PR_FALSE; // stop enumerating the list
  }
  return PR_TRUE; // don't stop
}

#define MAKE_FONT_PREF_KEY(_pref, _s0, _s1) \
 _pref.Assign(_s0); \
 _pref.Append(_s1);

static void 
AppendGenericFontFromPref(nsString& aFontname,
                          const char* aLangGroup,
                          const char* aGeneric)
{
  nsresult res;
  nsCAutoString pref;
  nsXPIDLString value;
  nsCAutoString generic_dot_langGroup;

  generic_dot_langGroup.Assign(aGeneric);
  generic_dot_langGroup.Append('.');
  generic_dot_langGroup.Append(aLangGroup);

  // font.name.[generic].[langGroup]
  // the current user' selected font, it gives the first preferred font
  MAKE_FONT_PREF_KEY(pref, "font.name.", generic_dot_langGroup);
  res = gPref->CopyUnicharPref(pref.get(), getter_Copies(value));      
  if (NS_SUCCEEDED(res)) {
    if(!aFontname.IsEmpty())
      aFontname.Append((PRUnichar)',');
    aFontname.Append(value);
  }

  // font.name-list.[generic].[langGroup]
  // the pre-built list of default fonts, it gives alternative fonts
  MAKE_FONT_PREF_KEY(pref, "font.name-list.", generic_dot_langGroup);
  res = gPref->CopyUnicharPref(pref.get(), getter_Copies(value));      
  if (NS_SUCCEEDED(res)) {
    if(!aFontname.IsEmpty())
      aFontname.Append((PRUnichar)',');
    aFontname.Append(value);
  }
}

nsFontOS2*
nsFontMetricsOS2::FindGenericFont(HPS aPS, PRUint32 aChar)
{
  if (mTriedAllGenerics) {
    // don't bother anymore because mLoadedFonts[] already has all our generic fonts
    return nsnull;
  }

  // This is a nifty hook that we will use to just iterate over
  // the list of names using the callback mechanism of nsFont...
  nsFont font("", 0, 0, 0, 0, 0);

  if (mLangGroup) {
    const char* langGroup;
    mLangGroup->GetUTF8String(&langGroup);
  
    // x-unicode pseudo-langGroup should be the last resort to turn to.
    // That is, it should be refered to only when we don't  recognize 
    // |langGroup| specified by the authors of documents and  the 
    // determination of |langGroup| based  on Unicode range also fails 
    // in |FindPrefFont|. 

    if (!strcmp(langGroup, "x-unicode")) {
      mTriedAllGenerics = 1;
      return nsnull;
    }

    AppendGenericFontFromPref(font.name, langGroup, 
                              NS_ConvertUCS2toUTF8(mGeneric).get());
  }

  // Iterate over the list of names using the callback mechanism of nsFont...
  GenericFontEnumContext context = {aPS, aChar, nsnull, this};
  font.EnumerateFamilies(GenericFontEnumCallback, &context);
  if (context.mFont) { // a suitable font was found
    return context.mFont;
  }

  mTriedAllGenerics = 1;
  return nsnull;
}

nsFontOS2*
nsFontMetricsOS2::FindPrefFont(HPS aPS, PRUint32 aChar)
{
  if (mTriedAllPref) {
    // don't bother anymore because mLoadedFonts[] already has all our pref fonts
    return nsnull;
  }
  nsFont font("", 0, 0, 0, 0, 0);
  // Try the pref of the user's ui lang group
  // For example, if the ui language is Japanese, try pref from "ja"
  // Make localized build work better on other OS
  if (gUsersLocale != mLangGroup) {
    nsAutoString langGroup;
    gUsersLocale->ToString(langGroup);
    AppendGenericFontFromPref(font.name, 
                              NS_ConvertUCS2toUTF8(langGroup).get(), 
                              NS_ConvertUCS2toUTF8(mGeneric).get());
  }
  // Try the pref of the user's system lang group
  // For example, if the os language is Simplified Chinese, 
  // try pref from "zh-CN"
  // Make English build work better on other OS
  if ((gSystemLocale != mLangGroup) && (gSystemLocale != gUsersLocale)) {
    nsAutoString langGroup;
    gSystemLocale->ToString(langGroup);
    AppendGenericFontFromPref(font.name, 
                              NS_ConvertUCS2toUTF8(langGroup).get(), 
                              NS_ConvertUCS2toUTF8(mGeneric).get());
  }

  // Also try all the default pref fonts enlisted from other languages
  for (int i = 1; i < eCharset_COUNT; ++i) {
    nsIAtom* langGroup = NS_NewAtom(gCharsetInfo[i].mLangGroup); 
    if((gUsersLocale != langGroup) && (gSystemLocale != langGroup)) {
      AppendGenericFontFromPref(font.name, gCharsetInfo[i].mLangGroup, 
                                NS_ConvertUCS2toUTF8(mGeneric).get());
    }
    NS_IF_RELEASE(langGroup);
  }
  GenericFontEnumContext context = {aPS, aChar, nsnull, this};
  font.EnumerateFamilies(GenericFontEnumCallback, &context);
  if (context.mFont) { // a suitable font was found
    return context.mFont;
  }
  mTriedAllPref = 1;
  return nsnull;
}

// returns family name of font that can display given char
nsFontOS2*
nsFontMetricsOS2::FindFont(HPS aPS, PRUint32 aChar)
{
  nsFontOS2* font = FindUserDefinedFont(aPS, aChar);
  if (!font) {
    font = FindLocalFont(aPS, aChar);
    if (!font) {
      font = FindGenericFont(aPS, aChar);
      if (!font) {
        font = FindPrefFont(aPS, aChar);
        if (!font) {
          font = FindGlobalFont(aPS, aChar);
#ifdef USE_FREETYPE
          if (!font) {
            font = FindSubstituteFont(aPS, aChar);
          }
#endif
        }
      }
    }
  }
#ifdef DEBUG_FONT_SELECTION
  if (font) {
    printf(" FindFont(): found font %s for char 0x%04x\n",
           font->mFattrs.szFacename, aChar);
  }
#endif
  return font;
}

static PRBool
FontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
{
  nsFontMetricsOS2* metrics = (nsFontMetricsOS2*) aData;

#ifdef USE_FREETYPE
  if (!nsFontMetricsOS2::gUseFTFunctions)
#endif
  {
    /* Hack for Truetype on OS/2 - if it's Arial and not 1252 or 0, just get another font */
    if (aFamily.Find("Arial", IGNORE_CASE) != -1) {
       if (metrics->mConvertCodePage != 1252) {
          if ((metrics->mConvertCodePage == 0) &&
              (gSystemCodePage != 850) &&
              (gSystemCodePage != 437)) {
             return PR_TRUE; // don't stop
          }
       }
    }
  }

  metrics->mFonts.AppendString(aFamily);
  if (aGeneric) {
    metrics->mGeneric.Assign(aFamily);
    ToLowerCase(metrics->mGeneric);
    return PR_FALSE; // stop
  }
  ++metrics->mGenericIndex;

  return PR_TRUE; // don't stop
}

PRBool
nsFontMetricsOS2::GetVectorSubstitute(HPS aPS, const nsAString& aFamilyname,
                                      nsAString& aAlias)
{
  if (aFamilyname.EqualsLiteral("Tms Rmn")) {
    aAlias.AssignLiteral("Times New Roman");
  } else if (aFamilyname.EqualsLiteral("Helv")) {
    aAlias.AssignLiteral("Helvetica");
  }

  // When printing, substitute vector fonts for these common bitmap fonts
  if (!mDeviceContext->SupportsRasterFonts()) {
    if (aFamilyname.EqualsLiteral("System Proportional") ||
        aFamilyname.EqualsLiteral("WarpSans"))
    {
      aAlias.AssignLiteral("Helvetica");
    } else if (aFamilyname.EqualsLiteral("System Monospaced") ||
               aFamilyname.EqualsLiteral("System VIO"))
    {
      aAlias.AssignLiteral("Courier");
    }
  }

  if (aAlias.IsEmpty())
    return PR_FALSE;
  else
    return PR_TRUE;
}

nsresult
nsFontMetricsOS2::RealizeFont()
{
  nsresult  rv;
  HPS       ps = NULL;

  if (mDeviceContext->mPrintDC){
    ps = mDeviceContext->mPrintPS;
  } else {
    HWND win = (HWND)mDeviceContext->mWidget;
    ps = ::WinGetPS(win);
    if (!ps) {
      ps = ::WinGetPS(HWND_DESKTOP);
    }
  }

  mFont.EnumerateFamilies(FontEnumCallback, this);

  nsCAutoString pref;
  nsXPIDLString value;

  // set a fallback generic font if the font-family list didn't have one
  if (mGeneric.IsEmpty()) {
    const char* langGroup = nsnull;
    mLangGroup->GetUTF8String(&langGroup);
    pref.Assign("font.default.");
    pref.Append(langGroup);
    rv = gPref->CopyUnicharPref(pref.get(), getter_Copies(value));
    if (NS_SUCCEEDED(rv)) {
      mGeneric.Assign(value);
    }
    else {
      mGeneric.AssignLiteral("serif");
    }
  }

  if (mLangGroup.get() == gUserDefined) {
    // See if this is a special user-defined font encoding by checking:
    // font.name.[generic].x-user-def
    pref.Assign("font.name.");
    pref.AppendWithConversion(mGeneric);
    pref.Append(".x-user-def");
    rv = gPref->CopyUnicharPref(pref.get(), getter_Copies(value));
    if (NS_SUCCEEDED(rv)) {
      mUserDefined.Assign(value);
      mIsUserDefined = 1;
    }
  }

  nsFontOS2* font = FindFont(ps, 'a');
  NS_ASSERTION(font, "FindFont() returned null.  THIS IS BAD!");
  if (!font) {
    if (mDeviceContext->mPrintDC == NULL) {
      ::WinReleasePS(ps);
    }
    return NS_ERROR_FAILURE;
  }

  font->mConvertCodePage = mConvertCodePage;

   // Record font handle & record various font metrics to cache
  mFontHandle = font;
  CHK_SUCCESS (::GpiCreateLogFont(ps, 0, 1, &(font->mFattrs)), FONT_MATCH);
  font->SelectIntoPS( ps, 1 );

  FONTMETRICS fm;
  GFX (::GpiQueryFontMetrics(ps, sizeof(fm), &fm), FALSE);
  /* Due to a bug in OS/2 MINCHO, need to cast lInternalLeading */
  fm.lInternalLeading = (signed short)fm.lInternalLeading;

  float dev2app;
  dev2app = mDeviceContext->DevUnitsToAppUnits();
  
  mMaxAscent  = NSToCoordRound( (fm.lMaxAscender-1) * dev2app );
  mMaxDescent = NSToCoordRound( (fm.lMaxDescender+1) * dev2app );
  mFontHandle->mMaxAscent = mMaxAscent;
  mFontHandle->mMaxDescent = mMaxDescent;

  mInternalLeading = NSToCoordRound( fm.lInternalLeading * dev2app );
  mExternalLeading = NSToCoordRound( fm.lExternalLeading * dev2app );
  
  /* These two values aren't really used by mozilla */
  mEmAscent = mMaxAscent - mInternalLeading;
  mEmDescent  = mMaxDescent;

  mMaxHeight  = mMaxAscent + mMaxDescent;
  mEmHeight = mEmAscent + mEmDescent;

  mMaxAdvance = NSToCoordRound( fm.lMaxCharInc * dev2app );
  mXHeight    = NSToCoordRound( fm.lXHeight * dev2app );

  nscoord onePixel = NSToCoordRound(1 * dev2app);

   // Not all fonts specify these two values correctly, and some not at all
  mSuperscriptYOffset = mXHeight;
  mSubscriptYOffset   = NSToCoordRound( mXHeight / 3.0f );

   // Using lStrikeoutPosition puts the strikeout too high
   // Use 50% of lXHeight instead
  mStrikeoutPosition  = NSToCoordRound( mXHeight / 2.0f);
  mStrikeoutSize      = PR_MAX(onePixel, NSToCoordRound(fm.lStrikeoutSize * dev2app));

#if 1
  // Can't trust the fonts to give us good results for the underline position
  //  and size.  This calculation, though, gives very good results.
  float height = fm.lMaxAscender + fm.lMaxDescender;
  mUnderlinePosition = -PR_MAX(onePixel, NSToIntRound(floor(0.1 * height + 0.5) * dev2app));
  mUnderlineSize = PR_MAX(onePixel, NSToIntRound(floor(0.05 * height + 0.5) * dev2app));
#else
  mUnderlinePosition  = -NSToCoordRound( fm.lUnderscorePosition * dev2app );
  mUnderlineSize      = PR_MAX(onePixel, NSToCoordRound(fm.lUnderscoreSize * dev2app));
#endif

  mAveCharWidth       = PR_MAX(1, NSToCoordRound(fm.lAveCharWidth * dev2app));

   // Cache the width of a single space.
  SIZEL  size;
  GetTextExtentPoint32(ps, " ", 1, &size);
  mSpaceWidth = NSToCoordRound(float(size.cx) * dev2app);

   // 10) Clean up
  GFX (::GpiSetCharSet (ps, LCID_DEFAULT), FALSE);
  GFX (::GpiDeleteSetId (ps, 1), FALSE);
  if (mDeviceContext->mPrintDC == NULL)
    ::WinReleasePS(ps);

  return NS_OK;
}

NS_IMETHODIMP nsFontMetricsOS2::GetSpaceWidth(nscoord &aSpaceWidth)
{
  aSpaceWidth = mSpaceWidth;
  return NS_OK;
}

// Other metrics
NS_IMETHODIMP nsFontMetricsOS2::GetXHeight( nscoord &aResult)
{
  aResult = mXHeight;
  return NS_OK;
}

NS_IMETHODIMP nsFontMetricsOS2::GetSuperscriptOffset(nscoord& aResult)
{
  aResult = mSuperscriptYOffset;
  return NS_OK;
}

NS_IMETHODIMP nsFontMetricsOS2::GetSubscriptOffset(nscoord& aResult)
{
  aResult = mSubscriptYOffset;
  return NS_OK;
}

NS_IMETHODIMP nsFontMetricsOS2::GetStrikeout(nscoord& aOffset, nscoord& aSize)
{
  aOffset = mStrikeoutPosition;
  aSize = mStrikeoutSize;
  return NS_OK;
}

NS_IMETHODIMP nsFontMetricsOS2::GetUnderline(nscoord& aOffset, nscoord& aSize)
{
  aOffset = mUnderlinePosition;
  aSize = mUnderlineSize;
  return NS_OK;
}

NS_IMETHODIMP nsFontMetricsOS2::GetHeight( nscoord &aHeight)
{
  aHeight = mMaxHeight;
  return NS_OK;
}

#ifdef FONT_LEADING_APIS_V2
NS_IMETHODIMP
nsFontMetricsOS2::GetInternalLeading(nscoord &aLeading)
{
  aLeading = mInternalLeading;
  return NS_OK;
}

NS_IMETHODIMP
nsFontMetricsOS2::GetExternalLeading(nscoord &aLeading)
{
  aLeading = mExternalLeading;
  return NS_OK;
}
#else
NS_IMETHODIMP nsFontMetricsOS2::GetLeading( nscoord &aLeading)
{
   aLeading = mInternalLeading;
   return NS_OK;
}

NS_IMETHODIMP
nsFontMetricsOS2::GetNormalLineHeight(nscoord &aHeight)
{
  aHeight = mEmHeight + mInternalLeading;
  return NS_OK;
}
#endif //FONT_LEADING_APIS_V2

NS_IMETHODIMP nsFontMetricsOS2::GetMaxAscent( nscoord &aAscent)
{
  aAscent = mMaxAscent;
  return NS_OK;
}

NS_IMETHODIMP nsFontMetricsOS2::GetMaxDescent( nscoord &aDescent)
{
  aDescent = mMaxDescent;
  return NS_OK;
}

NS_IMETHODIMP nsFontMetricsOS2::GetMaxAdvance( nscoord &aAdvance)
{
  aAdvance = mMaxAdvance;
  return NS_OK;
}

NS_IMETHODIMP nsFontMetricsOS2::GetFontHandle( nsFontHandle &aHandle)
{
  aHandle = mFontHandle;
  return NS_OK;
}

NS_IMETHODIMP nsFontMetricsOS2::GetLangGroup(nsIAtom** aLangGroup)
{
  NS_ENSURE_ARG_POINTER(aLangGroup);
  *aLangGroup = mLangGroup;
  NS_IF_ADDREF(*aLangGroup);
  return NS_OK;
}

NS_IMETHODIMP
nsFontMetricsOS2::GetEmHeight(nscoord &aHeight)
{
  aHeight = mEmHeight;
  return NS_OK;
}

NS_IMETHODIMP
nsFontMetricsOS2::GetEmAscent(nscoord &aAscent)
{
  aAscent = mEmAscent;
  return NS_OK;
}

NS_IMETHODIMP
nsFontMetricsOS2::GetEmDescent(nscoord &aDescent)
{
  aDescent = mEmDescent;
  return NS_OK;
}

NS_IMETHODIMP
nsFontMetricsOS2::GetMaxHeight(nscoord &aHeight)
{
  aHeight = mMaxHeight;
  return NS_OK;
}

NS_IMETHODIMP
nsFontMetricsOS2::GetAveCharWidth(nscoord &aAveCharWidth)
{
  aAveCharWidth = mAveCharWidth;
  return NS_OK;
}

nsFontOS2*
nsFontMetricsOS2::LoadUnicodeFont(HPS aPS, const nsAString& aName)
{
#ifdef DEBUG_FONT_SELECTION
  printf(" LoadUnicodeFont(): attempting to load %s\n",
         NS_LossyConvertUCS2toASCII(aName).get());
#endif
  nsFontOS2* font = LoadFont(aPS, aName);
  if (font) {
    return font;
  }
  return nsnull;
}

struct UnicodeFontEnumContext
{
  HPS               mPS;        // IN
  nsFontOS2*        mFont;      // OUT
  nsFontMetricsOS2* mMetrics;   // IN
};

static PRBool
UnicodeFontEnumCallback(const nsString& aFamily, PRBool aGeneric, void* aData)
{
  UnicodeFontEnumContext* context = (UnicodeFontEnumContext*)aData;
  HPS ps = context->mPS;
  nsFontMetricsOS2* metrics = context->mMetrics;
  context->mFont = metrics->LoadUnicodeFont(ps, aFamily);
  if (context->mFont) {
    return PR_FALSE; // stop enumerating the list
  }
  return PR_TRUE; // don't stop
}

void
nsFontMetricsOS2::FindUnicodeFont(HPS aPS)
{
  nsresult res;
  nsCAutoString pref, generic;
  nsXPIDLString value;

  generic.Assign(NS_ConvertUCS2toUTF8(mGeneric));

  pref.Assign("font.name.");
  pref.Append(generic);
  pref.Append(".x-unicode");
   
  res = gPref->CopyUnicharPref(pref.get(), getter_Copies(value));
  if (NS_FAILED(res))
    return;

  nsAutoString fontname;
  fontname.Assign(value);

  nsFontOS2* fh = nsnull;
  fh = LoadUnicodeFont(aPS, fontname);
  if (!fh)
  {
     // User defined unicode font did not load.  Fall back to font
     // specified in font.name-list.%.x-unicode
    pref.Assign("font.name-list.");
    pref.Append(generic);
    pref.Append(".x-unicode");

    res = gPref->CopyUnicharPref(pref.get(), getter_Copies(value));
    if (NS_FAILED(res))
      return;

    nsFont font("", 0, 0, 0, 0, 0);
    font.name.Assign(value);

     // Iterate over the list of names using the callback mechanism of nsFont
    UnicodeFontEnumContext context = {aPS, nsnull, this};
    font.EnumerateFamilies(UnicodeFontEnumCallback, &context);
    fh = context.mFont;
  }

  if (fh) {   // a suitable font was found
    fh->mFattrs.usCodePage = 1208;
    fh->mConvertCodePage = 1208;
    mUnicodeFont = fh;
#ifdef DEBUG_FONT_SELECTION
    printf(" FindUnicodeFont(): found new Unicode font %s\n",
           mUnicodeFont->mFattrs.szFacename);
#endif
  } else {
    NS_ERROR("Could not find a Unicode font");
  }
  return;
}

void
nsFontMetricsOS2::FindWesternFont()
{
  // Create a 'western' font by making a copy of the currently selected font
  // and changing the codepage 1252
  nsFontOS2* font = new nsFontOS2();
  font->mFattrs = mFontHandle->mFattrs;
  font->mFattrs.usCodePage = 1252;
  font->mCharbox = mFontHandle->mCharbox;
  font->mMaxAscent = mFontHandle->mMaxAscent;
  font->mMaxDescent = mFontHandle->mMaxDescent;
  font->mConvertCodePage = 1252;
  mLoadedFonts.AppendElement(font);
  mWesternFont = font;
}

#define IS_SPECIAL_WO_ELLIPSE(x) \
                      ((x == 0x20AC) ||  /* euro */  \
                       (x == 0x2022) ||  /* bull */  \
                       (x == 0x201C) ||  /* ldquo */ \
                       (x == 0x201D) ||  /* rdquo */ \
                       (x == 0x2018) ||  /* lsquo */ \
                       (x == 0x2019) ||  /* rsquo */ \
                       (x == 0x2013) ||  /* ndash */ \
                       (x == 0x2014))    /* mdash */

#define IS_SPECIAL(x) \
                       (IS_SPECIAL_WO_ELLIPSE(x) ||  \
                       (x == 0x2026)) /* hellip */

nsresult
nsFontMetricsOS2::ResolveForwards(HPS                  aPS,
                                  const PRUnichar*     aString,
                                  PRUint32             aLength,
                                  nsFontSwitchCallback aFunc, 
                                  void*                aData)
{
  NS_ASSERTION(aString || !aLength, "invalid call");
  const PRUnichar* firstChar = aString;
  const PRUnichar* currChar = firstChar;
  const PRUnichar* lastChar  = aString + aLength;
  PRBool running = PR_TRUE;

  nsFontSwitch fontSwitch;
  fontSwitch.mFont = 0;

  if (mConvertCodePage == 1252)
  {
    while (running && firstChar < lastChar)
    {
      if ((*currChar > 0x00FF) && !IS_SPECIAL(*currChar))
      {
        if (!mUnicodeFont) {
          FindUnicodeFont(aPS);
          if (!mUnicodeFont) {
            mUnicodeFont = FindGlobalFont(aPS, *currChar);
          }
        }
        fontSwitch.mFont = mUnicodeFont;
        while( ++currChar < lastChar ) {
          if (( *currChar <= 0x00FF ) || IS_SPECIAL(*currChar))
            break;
        }
        running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
      } else {
        // Use currently selected font
        fontSwitch.mFont = mFontHandle;
        while( ++currChar < lastChar ) {
          if (( *currChar > 0x00FF ) && !IS_SPECIAL(*currChar))
            break;
        }
        running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
      }
      firstChar = currChar;
    }
  }
  else
  {
    while (running && firstChar < lastChar)
    {
      if ((*currChar >= 0x0080 && *currChar <= 0x00FF) || IS_SPECIAL_WO_ELLIPSE(*currChar))
      { 
        if (!mWesternFont) {
          FindWesternFont();
        }
        fontSwitch.mFont = mWesternFont;
        while( ++currChar < lastChar ) {
          if ((*currChar < 0x0080 || *currChar > 0x00FF) && !IS_SPECIAL_WO_ELLIPSE(*currChar))
            break;
        }
        running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
      } else {
        // Use currently selected font
        fontSwitch.mFont = mFontHandle;
        while( ++currChar < lastChar ) {
          if ((*currChar >= 0x0080 && *currChar <= 0x00FF) || IS_SPECIAL_WO_ELLIPSE(*currChar))
            break;
        }
        running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
      }
      firstChar = currChar;
    }
  }
  return NS_OK;
}

nsresult
nsFontMetricsOS2::ResolveBackwards(HPS                  aPS,
                                   const PRUnichar*     aString,
                                   PRUint32             aLength,
                                   nsFontSwitchCallback aFunc, 
                                   void*                aData)
{
  NS_ASSERTION(aString || !aLength, "invalid call");
  const PRUnichar* firstChar = aString + aLength - 1;
  const PRUnichar* lastChar  = aString - 1;
  const PRUnichar* currChar  = firstChar;
  PRBool running = PR_TRUE;

  nsFontSwitch fontSwitch;
  fontSwitch.mFont = 0;
  
  if (mConvertCodePage == 1252)
  {
    while (running && firstChar > lastChar)
    {
      if ((*currChar > 0x00FF) && !IS_SPECIAL(*currChar))
      {
        if (!mUnicodeFont) {
          FindUnicodeFont(aPS);
          if (!mUnicodeFont) {
            mUnicodeFont = FindGlobalFont(aPS, *currChar);
          }
        }
        fontSwitch.mFont = mUnicodeFont;
        while( --currChar > lastChar ) {
          if (( *currChar <= 0x00FF ) || IS_SPECIAL(*currChar))
            break;
        }
        running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
      }
      else
      {
         // Use currently selected font
        fontSwitch.mFont = mFontHandle;
        while( --currChar > lastChar ) {
          if (( *currChar > 0x00FF ) && !IS_SPECIAL(*currChar))
            break;
        }
        running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
      }
      firstChar = currChar;
    }
  }
  else
  {
    while (running && firstChar > lastChar)
    {
      if ((*currChar >= 0x0080 && *currChar <= 0x00FF) || IS_SPECIAL(*currChar))
      { 
        if (!mWesternFont) {
          FindWesternFont();
        }
        fontSwitch.mFont = mWesternFont;
        while( --currChar > lastChar ) {
          if ((*currChar < 0x0080 || *currChar > 0x00FF) && !IS_SPECIAL(*currChar))
            break;
        }
        running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
      }
      else
      {
         // Use currently selected font
        fontSwitch.mFont = mFontHandle;
        while( --currChar > lastChar ) {
          if ((*currChar >= 0x0080 && *currChar <= 0x00FF) || IS_SPECIAL(*currChar))
            break;
        }
        running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
      }
      firstChar = currChar;
    }
  }
  return NS_OK;
}


/**********************************************************
    nsFontOS2
 **********************************************************/
#ifdef DEBUG_FONT_STRUCT_ALLOCS
unsigned long nsFontOS2::mRefCount = 0;
#endif

nsFontOS2::nsFontOS2(void)
{
  mFattrs.usRecordLength = sizeof(mFattrs);
  mHashMe = gCurrHashValue;
  gCurrHashValue++;
#ifdef DEBUG_FONT_STRUCT_ALLOCS
  mRefCount++;
  printf("+++ nsFontOS2 total = %d\n", mRefCount);
#endif
}

nsFontOS2::~nsFontOS2(void)
{
#ifdef DEBUG_FONT_STRUCT_ALLOCS
  mRefCount--;
  printf("--- nsFontOS2 total = %d\n", mRefCount);
#endif
}

void
nsFontOS2::SelectIntoPS( HPS aPS, long aLcid )
{
   GFX (::GpiSetCharSet(aPS, aLcid), FALSE);
   GFX (::GpiSetCharBox(aPS, &mCharbox), FALSE);
}

PRInt32
nsFontOS2::GetWidth(HPS aPS, const char* aString, PRUint32 aLength)
{
  SIZEL size;
  GetTextExtentPoint32(aPS, aString, aLength, &size);
  return size.cx;
}

PRInt32
nsFontOS2::GetWidth(HPS aPS, const PRUnichar* aString, PRUint32 aLength)
{
  nsAutoCharBuffer buffer;
  PRInt32 destLength = aLength;
  WideCharToMultiByte(mConvertCodePage, aString, aLength, buffer, destLength);

  SIZEL size;
  GetTextExtentPoint32(aPS, buffer.get(), destLength, &size);

  return size.cx;
}

void
nsFontOS2::DrawString(HPS aPS, nsDrawingSurfaceOS2* aSurface,
                      PRInt32 aX, PRInt32 aY,
                      const char* aString, PRUint32 aLength, INT* aDx0)
{
  POINTL ptl = { aX, aY };
  aSurface->NS2PM(&ptl, 1);
  ExtTextOut(aPS, ptl.x, ptl.y, 0, NULL, aString, aLength, aDx0);
}

void
nsFontOS2::DrawString(HPS aPS, nsDrawingSurfaceOS2* aSurface,
                      PRInt32 aX, PRInt32 aY,
                      const PRUnichar* aString, PRUint32 aLength)
{
  nsAutoCharBuffer buffer;
  PRInt32 destLength = aLength;
  WideCharToMultiByte(mConvertCodePage, aString, aLength, buffer, destLength);

  POINTL ptl = { aX, aY };
  aSurface->NS2PM (&ptl, 1);

  ExtTextOut(aPS, ptl.x, ptl.y, 0, NULL, buffer.get(), destLength, NULL);
}


#ifdef USE_FREETYPE
/**********************************************************
    nsFontMetricsOS2FT
 **********************************************************/
Ft2EnableFontEngine nsFontMetricsOS2FT::pfnFt2EnableFontEngine = NULL;

#define IsCJKLangGroupAtom(a)  ((a)==gJA || (a)==gKO || (a)==gZHCN || \
                                (a)==gZHTW || (a) == gZHHK)

nsFontMetricsOS2FT::~nsFontMetricsOS2FT()
{
  mSubstituteFont = nsnull; // released in ~nsFontMetricsOS2()
}

nsFontOS2*
nsFontMetricsOS2FT::FindPrefFont(HPS aPS, PRUint32 aChar)
{
  if (mTriedAllPref) {
    // don't bother anymore because mLoadedFonts[] already has all our pref fonts
    return nsnull;
  }
  nsFont font("", 0, 0, 0, 0, 0);

  // Sometimes we could not find the font in doc's suggested langGroup,(this usually means  
  // the language specified by doc is incorrect). The characters can, to a certain degree, 
  // tell us what language it is. This allows us to quickly locate and use a more appropriate 
  // font as indicated by user's preference. In some situations a set of possible languages may
  // be identified instead of a single language (eg. CJK and latin). In this case we have to 
  // try every language in the set. gUserLocale and gSystemLocale provide some hints about 
  // which one should be tried first. This is important for CJK font, since the glyph for single 
  // char varies dramatically in different langauges. For latin languages, their glyphs are 
  // similar. In fact, they almost always share identical fonts. It will be a waste of time to 
  // figure out which one comes first. As a final fallback, unicode preference is always tried. 

  PRUint32 unicodeRange = FindCharUnicodeRange(aChar);
  if (unicodeRange < kRangeSpecificItemNum) {
    // a single language is identified
    AppendGenericFontFromPref(font.name, LangGroupFromUnicodeRange(unicodeRange), 
                              NS_ConvertUCS2toUTF8(mGeneric).get());
  } else if (kRangeSetLatin == unicodeRange) { 
    // Character is from a latin language set, so try western and central european
    // If mLangGroup is western or central european, this most probably will not be
    // used, but is here as a fallback scenario.    
    AppendGenericFontFromPref(font.name, "x-western",
                              NS_ConvertUCS2toUTF8(mGeneric).get());
    AppendGenericFontFromPref(font.name, "x-central-euro",
                              NS_ConvertUCS2toUTF8(mGeneric).get());
  } else if (kRangeSetCJK == unicodeRange) { 
    // CJK, we have to be careful about the order, use locale info as hint
    
    // then try user locale first, if it is CJK
    if ((gUsersLocale != mLangGroup) && IsCJKLangGroupAtom(gUsersLocale)) {
      nsCAutoString usersLocaleLangGroup;
      gUsersLocale->ToUTF8String(usersLocaleLangGroup);
      AppendGenericFontFromPref(font.name, usersLocaleLangGroup.get(), 
                                NS_ConvertUCS2toUTF8(mGeneric).get());
    }
    
    // then system locale (os language)
    if ((gSystemLocale != mLangGroup) && (gSystemLocale != gUsersLocale) && IsCJKLangGroupAtom(gSystemLocale)) {
      nsCAutoString systemLocaleLangGroup;
      gSystemLocale->ToUTF8String(systemLocaleLangGroup);
      AppendGenericFontFromPref(font.name, systemLocaleLangGroup.get(), 
                                NS_ConvertUCS2toUTF8(mGeneric).get());
    }

    // try all other languages in this set.
    if (mLangGroup != gJA && gUsersLocale != gJA && gSystemLocale != gJA)
      AppendGenericFontFromPref(font.name, "ja",
                                NS_ConvertUCS2toUTF8(mGeneric).get());
    if (mLangGroup != gZHCN && gUsersLocale != gZHCN && gSystemLocale != gZHCN)
      AppendGenericFontFromPref(font.name, "zh-CN",
                                NS_ConvertUCS2toUTF8(mGeneric).get());
    if (mLangGroup != gZHTW && gUsersLocale != gZHTW && gSystemLocale != gZHTW)
      AppendGenericFontFromPref(font.name, "zh-TW",
                                NS_ConvertUCS2toUTF8(mGeneric).get());
    if (mLangGroup != gZHHK && gUsersLocale != gZHHK && gSystemLocale != gZHHK)
      AppendGenericFontFromPref(font.name, "zh-HK",
                                NS_ConvertUCS2toUTF8(mGeneric).get());
    if (mLangGroup != gKO && gUsersLocale != gKO && gSystemLocale != gKO)
      AppendGenericFontFromPref(font.name, "ko",
                                NS_ConvertUCS2toUTF8(mGeneric).get());
  } 

  // always try unicode as fallback
  AppendGenericFontFromPref(font.name, "x-unicode",
                            NS_ConvertUCS2toUTF8(mGeneric).get());
  
  // use the font list to find font
  GenericFontEnumContext context = {aPS, aChar, nsnull, this};
  font.EnumerateFamilies(GenericFontEnumCallback, &context);
  if (context.mFont) { // a suitable font was found
    return context.mFont;
  }
  mTriedAllPref = 1;
  return nsnull;
}

struct FindGlobalFontData
{
  HPS               ps;       // IN
  PRUint32          ch;       // IN - looking for font that has this char
  nsFontOS2FT*      font;     // IN - scratch object
  GlobalFontEntry*  entry;    // OUT - set to matching global font entry
};

PR_STATIC_CALLBACK(PLDHashOperator)
FindGlobalFontEnumFunc(GlobalFontEntry* aEntry, void* aData)
{
  FindGlobalFontData* data = NS_STATIC_CAST(FindGlobalFontData*, aData);
  strcpy(data->font->mFattrs.szFacename, aEntry->mMetrics->szFacename);
#ifdef PERF_HASGLYPH_CHAR_MAP
  if (aEntry->mHaveCheckedCharMap == nsnull) {
    aEntry->mHaveCheckedCharMap = (PRUint32*)calloc(UCS2_MAP_LEN, sizeof(PRUint32));
    aEntry->mRepresentableCharMap = (PRUint32*)calloc(UCS2_MAP_LEN, sizeof(PRUint32));
  }
  data->font->mHaveCheckedCharMap = aEntry->mHaveCheckedCharMap;
  data->font->mRepresentableCharMap = aEntry->mRepresentableCharMap;
#endif

  if (data->font->HasGlyph(data->ps, data->ch)) {
    data->entry = aEntry;
    return PL_DHASH_STOP;
  } else {
    return PL_DHASH_NEXT;
  }
}

nsFontOS2*
nsFontMetricsOS2FT::FindGlobalFont(HPS aPS, PRUint32 aChar)
{
  //now try global font
  if (!gGlobalFonts) {
    if (!InitializeGlobalFonts()) {
      return nsnull;
    }
  }

  FindGlobalFontData data = {aPS, aChar, nsnull, nsnull};
  data.font = new nsFontOS2FT();
  gGlobalFonts->EnumerateEntries(FindGlobalFontEnumFunc, &data);
  delete data.font;
  if (data.entry) {
    return LoadFont(aPS, data.entry->GetKey());
  }
  return nsnull;
}

nsFontOS2*
nsFontMetricsOS2FT::FindSubstituteFont(HPS aPS, PRUint32 c)
{
  /*
  When this function is called, it means all other alternatives have
  been unsuccessfully tried! So the idea is this:
  
  See if the "substitute font" is already loaded?
  a/ if yes, ADD_GLYPH(c), to record that the font should be used
     to render this char from now on. 
  b/ if no, load the font, and ADD_GLYPH(c)
  */

  if (mSubstituteFont) {
    // Make the char representable so that we don't have to go over all fonts
    // before fallback to subsituteFont.
    ((nsFontOS2Substitute*)mSubstituteFont)->SetRepresentable(c);
    return mSubstituteFont;
  }

  // The "substitute font" has not yet been created... 
  // The first font that has the
  // replacement char is taken and placed as the substitute font.

  // Try local/loaded fonts first
  int i, count = mLoadedFonts.Count();
  for (i = 0; i < count; ++i) {
    nsFontOS2* font = (nsFontOS2*)mLoadedFonts[i];
    if (font->HasGlyph(aPS, NS_REPLACEMENT_CHAR)) {
      nsFontOS2Substitute* subFont = new nsFontOS2Substitute(font);
      mLoadedFonts.AppendElement((nsFontOS2*)subFont);
      subFont->SetRepresentable(c);
      mSubstituteFont = subFont;
      return subFont;
    }
  }

  // Try global fonts
  // Since we reach here after FindGlobalFont() is called, we have already
  // scanned the global list of fonts and have set the attributes of interest
  FindGlobalFontData data = {aPS, NS_REPLACEMENT_CHAR, nsnull, nsnull};
  data.font = new nsFontOS2FT();
  gGlobalFonts->EnumerateEntries(FindGlobalFontEnumFunc, &data);
  delete data.font;
  if (data.entry) {
    nsFontOS2* font = LoadFont(aPS, data.entry->GetKey());
    if (font) {
      nsFontOS2Substitute* subFont = new nsFontOS2Substitute(font);
      // LoadFont() appends given font to end of mLoadedFonts.  Just replace
      //  font with our substitute font.
      mLoadedFonts.ReplaceElementAt((nsFontOS2*)subFont, mLoadedFonts.Count());
      subFont->SetRepresentable(c);
      mSubstituteFont = subFont;
      return subFont;
    }
  }

  NS_ERROR("Could not provide a substititute font");
  return nsnull;
}

nsFontOS2*
nsFontMetricsOS2FT::LocateFont(HPS aPS, PRUint32 aChar, PRInt32 & aCount)
{
  nsFontOS2* font;
  PRInt32 i;

  // see if one of our loaded fonts can represent the character
  for (i = 0; i < aCount; ++i) {
    font = (nsFontOS2*)mLoadedFonts[i];
    if (font->HasGlyph(aPS, aChar))
      return font;
  }

  font = FindFont(aPS, aChar);
  aCount = mLoadedFonts.Count(); // update since FindFont() can change it
  NS_ASSERTION(font && mLoadedFonts.IndexOf(font) >= 0,
               "Could not find a font");
  return font;
}

nsresult
nsFontMetricsOS2FT::ResolveForwards(HPS                  aPS,
                                    const PRUnichar*     aString,
                                    PRUint32             aLength,
                                    nsFontSwitchCallback aFunc, 
                                    void*                aData)
{
  NS_ASSERTION(aString || !aLength, "invalid call");
  const PRUnichar* firstChar = aString;
  const PRUnichar* currChar = firstChar;
  const PRUnichar* lastChar  = aString + aLength;
  nsFontOS2* currFont;
  nsFontOS2* nextFont;
  PRInt32 count;
  nsFontSwitch fontSwitch;

  if (firstChar == lastChar)
    return NS_OK;

  count = mLoadedFonts.Count();

  if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) {
    currFont = LocateFont(aPS, SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count);
    currChar += 2;
  }
  else {
    currFont = LocateFont(aPS, *currChar, count);
    ++currChar;
  }

  //This if block is meant to speedup the process in normal situation, when
  //most characters can be found in first font
  if (currFont == mLoadedFonts[0]) {
    while (currChar < lastChar && (currFont->HasGlyph(aPS, *currChar)))
      ++currChar;
    fontSwitch.mFont = currFont;
    if (!(*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData))
      return NS_OK;
    if (currChar == lastChar)
      return NS_OK;
    // continue with the next substring, re-using the available loaded fonts
    firstChar = currChar;
    if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) {
      currFont = LocateFont(aPS, SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count);
      currChar += 2;
    }
    else {
      currFont = LocateFont(aPS, *currChar, count);
      ++currChar;
    }
  }

  // see if we can keep the same font for adjacent characters
  PRInt32 lastCharLen;
  while (currChar < lastChar) {
    if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) {
      nextFont = LocateFont(aPS, SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count);
      lastCharLen = 2;
    }
    else {
      nextFont = LocateFont(aPS, *currChar, count);
      lastCharLen = 1;
    }
    if (nextFont != currFont) {
      // We have a substring that can be represented with the same font, and
      // we are about to switch fonts, it is time to notify our caller.
      fontSwitch.mFont = currFont;
      if (!(*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData))
        return NS_OK;
      // continue with the next substring, re-using the available loaded fonts
      firstChar = currChar;

      currFont = nextFont; // use the font found earlier for the char
    }
    currChar += lastCharLen;
  }

  //do it for last part of the string
  fontSwitch.mFont = currFont;
  (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
  return NS_OK;
}

nsresult
nsFontMetricsOS2FT::ResolveBackwards(HPS                  aPS,
                                     const PRUnichar*     aString,
                                     PRUint32             aLength,
                                     nsFontSwitchCallback aFunc, 
                                     void*                aData)
{
  NS_ASSERTION(aString || !aLength, "invalid call");
  const PRUnichar* firstChar = aString + aLength - 1;
  const PRUnichar* lastChar  = aString - 1;
  const PRUnichar* currChar  = firstChar;
  nsFontOS2* currFont;
  nsFontOS2* nextFont;
  PRInt32 count;
  nsFontSwitch fontSwitch;

  if (firstChar == lastChar)
    return NS_OK;

  count = mLoadedFonts.Count();

  // see if one of our loaded fonts can represent the current character
  if (IS_LOW_SURROGATE(*currChar) && (currChar-1) > lastChar && IS_HIGH_SURROGATE(*(currChar-1))) {
    currFont = LocateFont(aPS, SURROGATE_TO_UCS4(*(currChar-1), *currChar), count);
    currChar -= 2;
  }
  else {
    currFont = LocateFont(aPS, *currChar, count);
    --currChar;
  }

  //This if block is meant to speedup the process in normal situation, when
  //most characters can be found in first font
  if (currFont == mLoadedFonts[0]) {
    while (currChar > lastChar && (currFont->HasGlyph(aPS, *currChar)))
      --currChar;
    fontSwitch.mFont = currFont;
    if (!(*aFunc)(&fontSwitch, currChar+1, firstChar - currChar, aData))
      return NS_OK;
    if (currChar == lastChar)
      return NS_OK;
    // continue with the next substring, re-using the available loaded fonts
    firstChar = currChar;
    if (IS_LOW_SURROGATE(*currChar) && (currChar-1) > lastChar && IS_HIGH_SURROGATE(*(currChar-1))) {
      currFont = LocateFont(aPS, SURROGATE_TO_UCS4(*(currChar-1), *currChar), count);
      currChar -= 2;
    }
    else {
      currFont = LocateFont(aPS, *currChar, count);
      --currChar;
    }
  }

  // see if we can keep the same font for adjacent characters
  PRInt32 lastCharLen;
  PRUint32 codepoint;

  while (currChar > lastChar) {
    if (IS_LOW_SURROGATE(*currChar) && (currChar-1) > lastChar && IS_HIGH_SURROGATE(*(currChar-1))) {
      codepoint =  SURROGATE_TO_UCS4(*(currChar-1), *currChar);
      nextFont = LocateFont(aPS, codepoint, count);
      lastCharLen = 2;
    }
    else {
      codepoint = *currChar;
      nextFont = LocateFont(aPS, codepoint, count);
      lastCharLen = 1;
    }
    if (nextFont != currFont ||
        /* render right-to-left characters outside the BMP one by one, because
           OS/2 doesn't reorder them. */
        codepoint > 0xFFFF) {
      // We have a substring that can be represented with the same font, and
      // we are about to switch fonts, it is time to notify our caller.
      fontSwitch.mFont = currFont;
      if (!(*aFunc)(&fontSwitch, currChar+1, firstChar - currChar, aData))
        return NS_OK;
      // continue with the next substring, re-using the available loaded fonts
      firstChar = currChar;
      currFont = nextFont; // use the font found earlier for the char
    }
    currChar -= lastCharLen;
  }

  //do it for last part of the string
  fontSwitch.mFont = currFont;
  (*aFunc)(&fontSwitch, currChar+1, firstChar - currChar, aData);

  return NS_OK;
}


/**********************************************************
    nsFontOS2FT
 **********************************************************/
Ft2FontSupportsUnicodeChar1 nsFontOS2FT::pfnFt2FontSupportsUnicodeChar1 = NULL;
#ifdef USE_EXPANDED_FREETYPE_FUNCS
Ft2QueryTextBoxW nsFontOS2FT::pfnFt2QueryTextBoxW = NULL;
Ft2CharStringPosAtW nsFontOS2FT::pfnFt2CharStringPosAtW = NULL;
#endif /* use_expanded_freetype_funcs */

#ifdef DEBUG_FONT_STRUCT_ALLOCS
unsigned long nsFontOS2FT::mRefCount = 0;
#endif

nsFontOS2FT::nsFontOS2FT(void) : nsFontOS2()
{
  mFattrs.usCodePage = 1208;
#ifdef DEBUG_FONT_STRUCT_ALLOCS
  mRefCount++;
  printf("+++ nsFontOS2FT total = %d\n", mRefCount);
#endif
}

nsFontOS2FT::~nsFontOS2FT(void)
{
#ifdef DEBUG_FONT_STRUCT_ALLOCS
  mRefCount--;
  printf("--- nsFontOS2FT total = %d\n", mRefCount);
#endif
}

PRBool
nsFontOS2FT::HasGlyph(HPS aPS, PRUint32 aChar)
{
#ifdef PERF_HASGLYPH_CHAR_MAP
  if (IS_IN_BMP(aChar) && IS_REPRESENTABLE(mHaveCheckedCharMap, aChar)) {
    // we have already checked this char for this font
    return IS_REPRESENTABLE(mRepresentableCharMap, aChar);
  } else
#endif
  {
    // If not in Plane 0, OS/2 can't render it.
    if (!IS_IN_BMP(aChar)) {
      return PR_FALSE;
    }

    PRBool rc = pfnFt2FontSupportsUnicodeChar1(0, &mFattrs, PR_TRUE, aChar);

#ifdef PERF_HASGLYPH_CHAR_MAP
    // set that we have checked this char
    SET_REPRESENTABLE(mHaveCheckedCharMap, aChar);
    if (rc) {
      SET_REPRESENTABLE(mRepresentableCharMap, aChar);
    }
#endif

    return rc;
  }
}

PRInt32
nsFontOS2FT::GetWidth(HPS aPS, const PRUnichar* aString, PRUint32 aLength)
{
  USHORT rc;
  SIZEL size;

  if (!IsSymbolFont()) {
#ifdef USE_EXPANDED_FREETYPE_FUNCS
    POINTL ptls[5];
    rc = pfnFt2QueryTextBoxW(aPS, aLength, (LPWSTR)aString, 5, ptls);
    size.cx = ptls[TXTBOX_CONCAT].x;
    
#ifdef DEBUG
    if (rc == FALSE) {
      // We only expect Ft2QueryTextBoxW() to fail with
      // PMERR_FUNCTION_NOT_SUPPORTED.  If it fails with any other error, then
      // print out a warning message
      USHORT errorCode = ERRORIDERROR (::WinGetLastError(0));
      if (errorCode != PMERR_FUNCTION_NOT_SUPPORTED) {
        printf("GFX_Err: pfnFt2QueryTextBoxW = 0x%X, 0x%X (%s - %s,  line %d)\n",
               rc, errorCode, __FILE__, __FUNCTION__, __LINE__);
      }
    }
#endif /* debug */

    // Sometimes, Ft2QueryTextBoxW will fail with FALSE (WinGetLastError
    // will return PMERR_FUNCTION_NOT_SUPPORTED), particularly when printing
    // (this function is not supported when printing). So if it fails, fall back
    // to the straight GPI call case.
    if (rc == FALSE)
#endif /* use_expanded_freetype_funcs */
    {
      NS_ConvertUCS2toUTF8 str(Substring(aString, aString + aLength));
      rc = GetTextExtentPoint32(aPS, (const char*)str.get(), str.Length(),
                                &size);
    }
  } else {
    nsAutoCharBuffer buffer;
    PRInt32 destLength = aLength;
    WideCharToMultiByte(1252, aString, aLength, buffer, destLength);
    rc = GetTextExtentPoint32(aPS, buffer.get(), destLength, &size);
  }

  if (rc == TRUE) {
    return size.cx;
  } else {
    return 0;
  }
}

void
nsFontOS2FT::DrawString(HPS aPS, nsDrawingSurfaceOS2* aSurface,
                        PRInt32 aX, PRInt32 aY,
                        const PRUnichar* aString, PRUint32 aLength)
{
  USHORT rc;
  POINTL ptl = { aX, aY };
  aSurface->NS2PM(&ptl, 1);

  if (!IsSymbolFont()) {
#ifdef USE_EXPANDED_FREETYPE_FUNCS
    rc = pfnFt2CharStringPosAtW(aPS, &ptl, NULL, 0, aLength,
                                (LPWSTR)aString, NULL, NULL);

#ifdef DEBUG
    if (rc == GPI_ERROR) {
      // We only expect Ft2CharStringPosAtW() to fail with
      // PMERR_FUNCTION_NOT_SUPPORTED.  If it fails with any other error, then
      // print out a warning message
      USHORT errorCode = ERRORIDERROR (::WinGetLastError(0));
      if (errorCode != PMERR_FUNCTION_NOT_SUPPORTED) {
        printf("GFX_Err: pfnFt2CharStringPosAtW = 0x%X, 0x%X (%s - %s,  line %d)\n",
               rc, errorCode, __FILE__, __FUNCTION__, __LINE__);
      }
    }
#endif /* debug */

    // Sometimes, Ft2CharStringPosAtW will fail with GPI_ERROR (WinGetLastError
    // will return PMERR_FUNCTION_NOT_SUPPORTED), particularly when printing
    // (this function is not supported when printing). So if it fails, fall back
    // to the straight GPI call case.
    if (rc == GPI_ERROR)
#endif /* use_expanded_freetype_funcs */
    {
      NS_ConvertUCS2toUTF8 str(Substring(aString, aString + aLength));
      ExtTextOut(aPS, ptl.x, ptl.y, 0, NULL, (const char*)str.get(),
                 str.Length(), NULL);
    }
  } else {
    nsAutoCharBuffer buffer;
    PRInt32 destLength = aLength;
    WideCharToMultiByte(1252, aString, aLength, buffer, destLength);
    ExtTextOut(aPS, ptl.x, ptl.y, 0, NULL, buffer.get(), destLength, NULL);
  }
}

/**********************************************************
    nsFontOS2Substitute
 **********************************************************/
nsFontOS2Substitute::nsFontOS2Substitute(nsFontOS2* aFont)
{
  mHashMe = gCurrHashValue;
  gCurrHashValue++;

  mFattrs = aFont->mFattrs;
  mCharbox = aFont->mCharbox;
  mMaxAscent = aFont->mMaxAscent;
  mMaxDescent = aFont->mMaxDescent;
  mConvertCodePage = aFont->mConvertCodePage;

  memset(mRepresentableCharMap, 0, sizeof(mRepresentableCharMap));
#ifdef DEBUG_FONT_STRUCT_ALLOCS
  mRefCount++;
  printf("+++ nsFontOS2Substitute total = %d\n", mRefCount);
#endif
}

nsFontOS2Substitute::~nsFontOS2Substitute(void)
{
#ifdef DEBUG_FONT_STRUCT_ALLOCS
  mRefCount--;
  printf("--- nsFontOS2Substitute total = %d\n", mRefCount);
#endif
}

static nsresult
SubstituteChars(const PRUnichar*    aString, 
                PRUint32            aLength,
                nsAutoChar16Buffer& aResult,
                PRUint32*           aCount)
{
  nsresult res;
  if (!gFontSubstituteConverter) {
    CallCreateInstance(NS_SAVEASCHARSET_CONTRACTID, &gFontSubstituteConverter);
    if (gFontSubstituteConverter) {
      res = gFontSubstituteConverter->Init("ISO-8859-1",
                              nsISaveAsCharset::attr_EntityAfterCharsetConv +
                              nsISaveAsCharset::attr_FallbackQuestionMark +
                              nsISaveAsCharset::attr_IgnoreIgnorables,
                              nsIEntityConverter::transliterate);
      if (NS_FAILED(res)) {
        NS_RELEASE(gFontSubstituteConverter);
      }
    }
  }

  // do the transliteration if we have a converter
  PRUnichar* result; 
  if (gFontSubstituteConverter) {
    nsXPIDLCString conv;
    nsAutoString tmp(aString, aLength); // we need to pass a null-terminated string
    res = gFontSubstituteConverter->Convert(tmp.get(), getter_Copies(conv));
    if (NS_SUCCEEDED(res)) {
      *aCount = conv.Length();
      if (*aCount > 0) {
        if (!aResult.EnsureElemCapacity(*aCount)) {
          return NS_ERROR_OUT_OF_MEMORY;
        }
        result = aResult.get();
        PRUnichar* u = result;
        const char* c = conv.get();
        for (; *c; ++c, ++u) {
          *u = *c;
        }
      }
      return NS_OK;
    }
  }

  // we reach here if we couldn't transliterate, so fallback to question marks 
  if (!aResult.EnsureElemCapacity(aLength)) return NS_ERROR_OUT_OF_MEMORY;
  result = aResult.get();
  for (PRUint32 i = 0; i < aLength; i++) {
    result[i] = NS_REPLACEMENT_CHAR;
  }
  *aCount = aLength;
  return NS_OK;
}

PRInt32
nsFontOS2Substitute::GetWidth(HPS aPS, const PRUnichar* aString,
                              PRUint32 aLength)
{
  nsAutoChar16Buffer buffer;
  nsresult rv = SubstituteChars(aString, aLength, buffer, &aLength);
  if (NS_FAILED(rv) || !aLength) return 0;

  SIZEL size;
  PRUnichar* string = buffer.get();
  NS_ConvertUCS2toUTF8 str(Substring(string, string + aLength));
  BOOL rc = GetTextExtentPoint32(aPS, (const char*)str.get(), str.Length(),
                                 &size);
  if (rc == TRUE) {
    return size.cx;
  } else {
    return 0;
  }
}

void
nsFontOS2Substitute::DrawString(HPS aPS, nsDrawingSurfaceOS2* aSurface,
                                PRInt32 aX, PRInt32 aY,
                                const PRUnichar* aString, PRUint32 aLength)
{
  nsAutoChar16Buffer buffer;
  nsresult rv = SubstituteChars(aString, aLength, buffer, &aLength);
  if (NS_FAILED(rv) || !aLength) return;

  POINTL ptl = { aX, aY };
  aSurface->NS2PM(&ptl, 1);
  PRUnichar* string = buffer.get();
  NS_ConvertUCS2toUTF8 str(Substring(string, string + aLength));
  ExtTextOut(aPS, ptl.x, ptl.y, 0, NULL, (const char*)str.get(), str.Length(), NULL);
}
#endif /* use_freetype */


/**********************************************************
    nsFontEnumeratorOS2
 **********************************************************/
nsFontEnumeratorOS2::nsFontEnumeratorOS2()
{
}

NS_IMPL_ISUPPORTS1(nsFontEnumeratorOS2, nsIFontEnumerator)

// We want vertical fonts (those whose name start with '@') to appear
// immediately following the non-vertical font of the same name.
static int
CompareFontNames(const void* aArg1, const void* aArg2, void* aClosure)
{
  const PRUnichar* str1 = *((const PRUnichar**) aArg1);
  const PRUnichar* str2 = *((const PRUnichar**) aArg2);

  int rc = 9;
  if ((char)str1[0] == '@') {
    str1++;
    rc = nsCRT::strcmp(str1, str2);
    if (rc == 0)
      return 1;
  }
  if ((char)str2[0] == '@') {
    str2++;
    rc = nsCRT::strcmp(str1, str2);
    if (rc == 0)
      return -1;
  }

  if (rc == 9)
    rc = nsCRT::strcmp(str1, str2);
  return rc;
}

NS_IMETHODIMP
nsFontEnumeratorOS2::EnumerateFonts(const char* aLangGroup,
  const char* aGeneric, PRUint32* aCount, PRUnichar*** aResult)
{
  return EnumerateAllFonts(aCount, aResult);
}

struct EnumerateAllFontsData
{
  PRUnichar** array;      // OUT - array of family names
  int count;              // IN/OUT - running count of names in array
};

PR_STATIC_CALLBACK(PLDHashOperator)
EnumerateAllFontsCallback(GlobalFontEntry* aEntry, void* aData)
{
  EnumerateAllFontsData* data = NS_STATIC_CAST(EnumerateAllFontsData*, aData);
  data->array[data->count++] = ToNewUnicode(aEntry->GetKey());
  return PL_DHASH_NEXT;
}

NS_IMETHODIMP
nsFontEnumeratorOS2::EnumerateAllFonts(PRUint32* aCount, PRUnichar*** aResult)
{
  NS_ENSURE_ARG_POINTER(aCount);
  NS_ENSURE_ARG_POINTER(aResult);

  if (!nsFontMetricsOS2::gGlobalFonts) {
    nsresult res = nsFontMetricsOS2::InitializeGlobalFonts();
    if (NS_FAILED(res)) {
      FreeGlobals();
      return res;
    }
  }

  *aCount = nsFontMetricsOS2::gGlobalFonts->Count();
  PRUnichar** array = (PRUnichar**)nsMemory::Alloc(*aCount * sizeof(PRUnichar*));
  NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);

  EnumerateAllFontsData data = {array, 0};
  nsFontMetricsOS2::gGlobalFonts->EnumerateEntries(EnumerateAllFontsCallback,
                                                  &data);

  NS_QuickSort(array, *aCount, sizeof(PRUnichar*), CompareFontNames, nsnull);

  *aResult = array;

  return NS_OK;
}

NS_IMETHODIMP
nsFontEnumeratorOS2::HaveFontFor(const char* aLangGroup, PRBool* aResult)
{
  NS_ENSURE_ARG_POINTER(aLangGroup);
  NS_ENSURE_ARG_POINTER(aResult);
  *aResult = PR_FALSE;

  // XXX stub
  NS_ASSERTION( 0, "HaveFontFor is not implemented" );

  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP
nsFontEnumeratorOS2::GetDefaultFont(const char *aLangGroup, 
  const char *aGeneric, PRUnichar **aResult)
{
  // aLangGroup=null or ""  means any (i.e., don't care)
  // aGeneric=null or ""  means any (i.e, don't care)

  NS_ENSURE_ARG_POINTER(aResult);
  *aResult = nsnull;

  return NS_OK;
}

NS_IMETHODIMP
nsFontEnumeratorOS2::UpdateFontList(PRBool *updateFontList)
{
  *updateFontList = PR_FALSE; // always return false for now
  NS_ASSERTION( 0, "UpdateFontList is not implemented" );
  return NS_ERROR_NOT_IMPLEMENTED;
}

