/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * XPCTL : nsULE.cpp
 *
 * ***** 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 mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Sun Microsystems, Inc.
 * Portions created by the Initial Developer are Copyright (C) 2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Prabhat Hegde (prabhat.hegde@sun.com)
 *   Jungshik Shin (jshin@mailmaps.org)
 *
 * 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 "nsULE.h"
#include "nsString.h"

#include "pango-types.h"
#include "pango-glyph.h"
#include "pango-modules.h"
#include "pango-utils.h"

#define GLYPH_COMBINING 256

/* 
 * To Do:(prabhat-04/01/03) Cache GlyphString
*/

nsULE::nsULE() {
}

nsULE::~nsULE() {
}

NS_IMPL_THREADSAFE_ISUPPORTS1(nsULE, nsILE)

// Default font encoding by code-range
// At the moment pangoLite only supports 2 shapers/scripts
const char*
nsULE::GetDefaultFont(const PRUnichar aString)
{
  if ((aString >= 0x0e01) && (aString <= 0x0e5b))
    return "tis620-2";
  if ((aString >= 0x0901) && (aString <= 0x0970))
    return "sun.unicode.india-0";
  return "iso8859-1";
}

PRInt32
nsULE::GetGlyphInfo(const PRUnichar      *aSrcBuf,
                    PRInt32              aSrcLen,
                    PangoliteGlyphString *aGlyphData,
                    const char           *aFontCharset)
{
  int                  ct=0, start=0, i, index, startgid, lastCluster=0;
  PRBool               sameCtlRun=PR_FALSE;
  PangoliteEngineShape *curShaper=NULL, *prevShaper=NULL;
  PangoliteMap         *pngMap=NULL;
  PangoliteAnalysis    pngAnalysis;
  guint                enginetypeId=0, rendertypeId=0;

  pngAnalysis.aDir = PANGO_DIRECTION_LTR;
 
  // Maybe find a better way to handle font encodings
  if (aFontCharset == NULL)
    pngAnalysis.fontCharset = strdup(GetDefaultFont(aSrcBuf[0]));
  else
    pngAnalysis.fontCharset = strdup(aFontCharset);

  enginetypeId = g_quark_from_static_string(PANGO_ENGINE_TYPE_SHAPE);
  rendertypeId = g_quark_from_static_string(PANGO_RENDER_TYPE_X);
  pngMap = pangolite_find_map("en_US", enginetypeId, rendertypeId);

  for (ct=0; ct < aSrcLen;) {
    start = ct;
    curShaper = (PangoliteEngineShape*)
      pangolite_map_get_engine(pngMap, (PRUint32)aSrcBuf[ct++]);
    sameCtlRun = (curShaper != NULL);
    prevShaper = curShaper;

    if (sameCtlRun) {
      while (sameCtlRun && ct < aSrcLen) {
        curShaper = (PangoliteEngineShape*)
          pangolite_map_get_engine(pngMap, (PRUint32)aSrcBuf[ct]);
        sameCtlRun = ((curShaper != NULL) && (curShaper == prevShaper));
        if (sameCtlRun)
          ct++;
      }
      startgid = aGlyphData->num_glyphs;
      pngAnalysis.shape_engine = curShaper;
      prevShaper->script_shape(pngAnalysis.fontCharset,
                               &aSrcBuf[start], (ct-start),
                               &pngAnalysis, aGlyphData);
      if (lastCluster > 0) {
         for (i=startgid; i < aGlyphData->num_glyphs; i++)
           aGlyphData->log_clusters[i] += lastCluster;
      }
    }
    else {
      while (!sameCtlRun && ct < aSrcLen) {
        curShaper = (PangoliteEngineShape*)
          pangolite_map_get_engine(pngMap, (PRUint32)aSrcBuf[ct]);
        sameCtlRun = (curShaper != NULL);
        if (!sameCtlRun)
          ct++;
      }
      index = aGlyphData->num_glyphs;
      for (i=0; i < (ct-start); i++) {
        pangolite_glyph_string_set_size(aGlyphData, index+1);
        aGlyphData->glyphs[index].glyph = aSrcBuf[start+i];
        aGlyphData->glyphs[index].is_cluster_start = (gint)1;
        aGlyphData->log_clusters[index] = i+lastCluster;
        index++;
      }
    }
    lastCluster = aGlyphData->log_clusters[aGlyphData->num_glyphs-1];
  }
  nsMemory::Free(pngAnalysis.fontCharset);
  return aGlyphData->num_glyphs;
}

NS_IMETHODIMP
nsULE::NeedsCTLFix(const PRUnichar *aString,
                   const PRInt32   aBeg,
                   const PRInt32   aEnd,
                   PRBool          *aCTLNeeded)
{
  PangoliteEngineShape *BegShaper=NULL, *EndShaper=NULL;
  PangoliteMap         *pngMap=NULL;
  guint                enginetypeId=0, rendertypeId=0;

  enginetypeId = g_quark_from_static_string(PANGO_ENGINE_TYPE_SHAPE);
  rendertypeId = g_quark_from_static_string(PANGO_RENDER_TYPE_X);
  pngMap = pangolite_find_map("en_US", enginetypeId, rendertypeId);

  *aCTLNeeded = PR_FALSE;
  if (aBeg >= 0)
    BegShaper = (PangoliteEngineShape*)
      pangolite_map_get_engine(pngMap, (PRUint32)aString[aBeg]);

  if (!BegShaper) {

    if ((aEnd < 0) && ((aBeg+aEnd) >= 0)) {
      EndShaper = (PangoliteEngineShape*)
        pangolite_map_get_engine(pngMap, (PRUint32)aString[aBeg+aEnd]);
    }
    else {
      EndShaper = (PangoliteEngineShape*)
        pangolite_map_get_engine(pngMap, (PRUint32)aString[aEnd]);
    }
  }

  if (BegShaper || EndShaper)
    *aCTLNeeded = PR_TRUE;
  return NS_OK;
}

NS_IMETHODIMP
nsULE::GetPresentationForm(const PRUnichar *aString,
                           PRUint32        aLength,
                           const char      *aFontCharset,
                           char            *aGlyphs,
                           PRSize          *aOutLength,
                           PRBool          aIsWide)
{
  PangoliteGlyphString *tmpGlyphs=pangolite_glyph_string_new();

  GetGlyphInfo(aString, aLength, tmpGlyphs, aFontCharset);

  if (tmpGlyphs->num_glyphs > 0) {
    gint i=0, glyphCt=0;
    for (i=0; i < tmpGlyphs->num_glyphs; i++, glyphCt++) {
      if (aIsWide)
         aGlyphs[glyphCt++]=(unsigned char)
                            ((tmpGlyphs->glyphs[i].glyph & 0xFF00) >> 8);
      aGlyphs[glyphCt]=(unsigned char)(tmpGlyphs->glyphs[i].glyph & 0x00FF);
    }
    *aOutLength = (PRSize)glyphCt;
  }
  pangolite_glyph_string_free(tmpGlyphs);
  return NS_OK;
}

// This routine returns the string index of the next cluster
// corresponding to the cluster at string index 'aIndex'
NS_IMETHODIMP
nsULE::NextCluster(const PRUnichar *aString,
                   PRUint32        aLength,
                   const PRInt32   aIndex,
                   PRInt32         *aNextOffset)
{
  int mStart, mEnd;

  if (aIndex < 0) {
    *aNextOffset = 0;
    return NS_OK;
  }

  if (PRUint32(aIndex) >= aLength) {
    *aNextOffset = aLength;
    return NS_OK;
  }
  this->GetRangeOfCluster(aString, aLength, aIndex, &mStart, &mEnd);
  *aNextOffset = mEnd;
  return NS_OK;
}

// This routine returns the end-offset of the previous block
// corresponding to string index 'aIndex'
NS_IMETHODIMP
nsULE::PrevCluster(const PRUnichar *aString,
                   PRUint32        aLength,
                   const PRInt32   aIndex,
                   PRInt32         *aPrevOffset)
{
  int                  gCt, pCluster, cCluster;
  PangoliteGlyphString *GlyphInfo=pangolite_glyph_string_new();

  if (aIndex <= 1) {
    *aPrevOffset = 0;
    return NS_OK;
  }
  pCluster=cCluster=0;
  GetGlyphInfo(aString, aLength, GlyphInfo, NULL);
  for (gCt=0; gCt < GlyphInfo->num_glyphs; gCt++) {

    if (GlyphInfo->glyphs[gCt].is_cluster_start != GLYPH_COMBINING)
       cCluster += GlyphInfo->glyphs[gCt].is_cluster_start;

    if (cCluster >= aIndex) {
       *aPrevOffset = pCluster;
       pangolite_glyph_string_free(GlyphInfo);
       return NS_OK;
    }
    pCluster = cCluster;
  }
  *aPrevOffset = pCluster;
  pangolite_glyph_string_free(GlyphInfo);
  return NS_OK;
}

// This routine gets the bounds of a cluster given a index
NS_IMETHODIMP
nsULE::GetRangeOfCluster(const PRUnichar *aString,
                         PRUint32        aLength,
                         const PRInt32   aIndex,
                         PRInt32         *aStart,
                         PRInt32         *aEnd)
{
  PangoliteGlyphString *GlyphInfo=pangolite_glyph_string_new();
  int                  gCt=0;

  GetGlyphInfo(aString, aLength, GlyphInfo, NULL);

  *aStart=*aEnd=0;
  for (gCt=0; gCt < GlyphInfo->num_glyphs; gCt++) {

    if (GlyphInfo->glyphs[gCt].is_cluster_start != GLYPH_COMBINING)
       *aEnd += GlyphInfo->glyphs[gCt].is_cluster_start;

    if (*aEnd >= aIndex+1) {
      pangolite_glyph_string_free(GlyphInfo);
      return NS_OK;
    }
    *aStart = *aEnd;
  }
  *aEnd = aLength;
  pangolite_glyph_string_free(GlyphInfo);
  return NS_OK;
}
