/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** 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
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * 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 "nsPangoLineBreaker.h"

#include <glib.h>
#include <gdk/gdkx.h>
#include <pango/pangoxft.h>

#include "nsUnicharUtils.h"

class nsPangoLogAttrList
{
public:
    nsPangoLogAttrList(PangoContext *pPangoContext, const PRUnichar* aText,
                       PRUint32 aLen);
    ~nsPangoLogAttrList();

    const PangoLogAttr *GetLogAttr(PRInt32 pos);

private:
    GList  *mItems;
    GList  *mLogAttrs;
};

nsPangoLogAttrList::nsPangoLogAttrList(
    PangoContext *pPangoContext, const PRUnichar* aText, PRUint32 aLen)
    : mItems(nsnull), mLogAttrs(nsnull)
{
    gchar *text = g_utf16_to_utf8(aText, aLen, NULL, NULL, NULL);

    PangoAttrList *al = pango_attr_list_new();
    mItems = pango_itemize(pPangoContext, text, 0, strlen(text), al, NULL);
    pango_attr_list_unref(al);

    for (GList *p = mItems; p; p = p->next) {
        PangoItem *pItem = (PangoItem *)p->data;
        PangoLogAttr *pLogAttrs = g_new(PangoLogAttr, pItem->num_chars + 1);
        pango_break(text + pItem->offset, pItem->length, &pItem->analysis,
                    pLogAttrs, pItem->num_chars + 1);
        mLogAttrs = g_list_append(mLogAttrs, pLogAttrs);
    }

    g_free(text);
}

nsPangoLogAttrList::~nsPangoLogAttrList()
{
    for (GList *p = mItems; p; p = p->next)
        pango_item_free((PangoItem *)p->data);
    g_list_free(mItems);

    for (GList *p = mLogAttrs; p; p = p->next)
        g_free(p->data);
    g_list_free(mLogAttrs);
}

const PangoLogAttr *nsPangoLogAttrList::GetLogAttr(PRInt32 pos)
{
    // Find item covering the pos
    PRInt32 coveredChars = 0;
    GList *pLogAttr = mLogAttrs;
    for (GList *p = mItems;
         p && pLogAttr;
         p = p->next, pLogAttr = pLogAttr->next)
    {
        PangoItem *pItem = (PangoItem *)p->data;
        if (coveredChars <= pos && pos < coveredChars + pItem->num_chars)
            break;
        coveredChars += pItem->num_chars;
    }

    if (!pLogAttr)
        return nsnull;
    return &((PangoLogAttr *)pLogAttr->data)[pos - coveredChars];
}


nsPangoLineBreaker::nsPangoLineBreaker()
    : mPangoCtx(nsnull)
{
}

nsPangoLineBreaker::~nsPangoLineBreaker()
{
    if (mPangoCtx)
        g_object_unref(mPangoCtx);
}

NS_IMPL_ISUPPORTS1(nsPangoLineBreaker, nsILineBreaker)


PangoContext *nsPangoLineBreaker::GetPangoContext()
{
    if (!mPangoCtx)
        mPangoCtx = pango_xft_get_context(GDK_DISPLAY(), 0);
    return mPangoCtx;
}


PRBool nsPangoLineBreaker::BreakInBetween(
    const PRUnichar* aText1 , PRUint32 aTextLen1,
    const PRUnichar* aText2 , PRUint32 aTextLen2)
{
    if (!aText1 || !aText2 || (0 == aTextLen1) || (0 == aTextLen2) ||
        IS_HIGH_SURROGATE(aText1[aTextLen1-1]) && 
        IS_LOW_SURROGATE(aText2[0]) )  //Do not separate a surrogate pair
    {
       return PR_FALSE;
    }

    PRBool result = PR_FALSE;

    PRUnichar *pConcat = new PRUnichar[aTextLen1 + aTextLen2];
    for (PRUint32 i = 0; i < aTextLen1; i++)
        pConcat[i] = aText1[i];
    for (PRUint32 i = 0; i < aTextLen2; i++)
        pConcat[aTextLen1 + i] = aText2[i];

    nsPangoLogAttrList logAttrs(GetPangoContext(), pConcat,
                                aTextLen1 + aTextLen2);
    const PangoLogAttr *pLogAttr = logAttrs.GetLogAttr(aTextLen1);
    if (pLogAttr && pLogAttr->is_line_break)
        result = PR_TRUE;

    delete pConcat;

    return result;
}


PRInt32 nsPangoLineBreaker::Next(
    const PRUnichar* aText, PRUint32 aLen, PRUint32 aPos) 
{
    NS_ASSERTION(aText, "aText shouldn't be null");
    NS_ASSERTION(aLen > aPos, "Illegal value (length > position)");

    nsPangoLogAttrList logAttrs(GetPangoContext(), aText, aLen);
    const PangoLogAttr *pLogAttr;
    while (++aPos < aLen && nsnull != (pLogAttr = logAttrs.GetLogAttr(aPos))) {
        if (pLogAttr->is_line_break)
            return aPos;
    }
    return NS_LINEBREAKER_NEED_MORE_TEXT;
}


PRInt32 nsPangoLineBreaker::Prev( 
    const PRUnichar* aText, PRUint32 aLen, PRUint32 aPos) 
{
    NS_ASSERTION(aText, "aText shouldn't be null");
    NS_ASSERTION(aLen > aPos, "Illegal value (length > position)");

    nsPangoLogAttrList logAttrs(GetPangoContext(), aText, aLen);
    const PangoLogAttr *pLogAttr;
    PRInt32 iPos = aPos;
    while (--iPos >= 0 && nsnull != (pLogAttr = logAttrs.GetLogAttr(iPos))) {
        if (pLogAttr->is_line_break)
            return aPos;
    }
    return NS_LINEBREAKER_NEED_MORE_TEXT;
}

