/*
 *	PostScript driver text functions
 *
 *	Copyright 1998  Huw D M Davies
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <math.h>

#include "windef.h"
#include "wingdi.h"
#include "psdrv.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(psdrv);

typedef struct tagRun {
    INT start;
    BOOL vertical;
    INT x;
    INT y;
}Run;

static BOOL PSDRV_Text(PHYSDEV dev, INT x, INT y, UINT flags,
		       LPCWSTR str, UINT count,
		       BOOL bDrawBackground, const INT *lpDx);

extern const unsigned short vertical_orientation_table[];

static BOOL check_unicode_tategaki(WCHAR uchar)
{
    unsigned short orientation = vertical_orientation_table[vertical_orientation_table[vertical_orientation_table[uchar >> 8]+((uchar >> 4) & 0x0f)]+ (uchar & 0xf)];

    /* Type: U or Type: Tu */
    /* TODO Type: Tr,  Normally the logic for Tr would be that if
       Typographical substitution occurs, then do not rotate. However
       we have no facility at present to determine if GetGlyphIndices is
       successfully performing substitutions (well formed font) or not.
       Thus we are erroring on the side of the font being well formed,
       doing typographical substitution,  and so we are not doing rotation */
    return (orientation ==  1 || orientation == 2 || orientation == 3);
}

static Run* build_vertical_runs(PHYSDEV dev, UINT flags, LPCWSTR str, UINT count, INT *run_count)
{
    BOOL last_vert;
    INT start, end;
    INT array_size = 5;
    Run *run = HeapAlloc(GetProcessHeap(),0,sizeof(Run)*array_size);
    int index = 0;
    LOGFONTW lf;

    if (count && str && (!(flags & ETO_GLYPH_INDEX)) && GetObjectW( GetCurrentObject(dev->hdc, OBJ_FONT), sizeof(lf), &lf ) && (lf.lfFaceName[0] == '@'))
    {
        last_vert = check_unicode_tategaki(str[0]);
        start = end = 0;
        while (start < count)
        {
            int offset = 0;

            while (end < count && check_unicode_tategaki(str[end]) == last_vert)
                end++;

            run[index].start = start;
            run[index].vertical = last_vert;
            run[index].x = 0;
            run[index].y = 0;

            if (run[index].vertical)
            {
                TEXTMETRICW tm;
                GetTextMetricsW(dev->hdc, &tm);
                offset += PSDRV_XWStoDS(dev, tm.tmAscent - tm.tmInternalLeading);
            }

            if (start > 0)
            {
                SIZE size;
                GetTextExtentPointW(dev->hdc, str, start, &size);
                offset += PSDRV_XWStoDS(dev, size.cx);
            }

            if (offset)
            {
                double angle;
                angle = (lf.lfEscapement / 10.0) * M_PI / 180.0;
                run[index].y = -offset * sin(angle);
                run[index].x = -offset * cos(angle);
            }

            index ++;
            if (index >= array_size)
            {
                array_size *=2;
                run = HeapReAlloc(GetProcessHeap(), 0, run, sizeof(Run)*array_size);
            }
            start = end;
            if (start < count)
                last_vert = check_unicode_tategaki(str[end]);
        }
    }
    else
    {
        run[0].start = 0;
        run[0].vertical = 0;
        run[0].x = 0;
        run[0].y = 0;
        index = 1;
    }
    *run_count = index;
    return run;
}

/***********************************************************************
 *           PSDRV_ExtTextOut
 */
BOOL PSDRV_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags, const RECT *lprect, LPCWSTR str, UINT count,
                       const INT *lpDx )
{
    PSDRV_PDEVICE *physDev = get_psdrv_dev( dev );
    BOOL bResult = TRUE;
    BOOL bClipped = FALSE;
    BOOL bOpaque = FALSE;
    Run *runs = NULL;
    int run_count = 0;
    int i = 0;

    TRACE("(x=%d, y=%d, flags=0x%08x, str=%s, count=%d, lpDx=%p)\n", x, y,
	  flags, debugstr_wn(str, count), count, lpDx);

    if(physDev->job.id == 0) return FALSE;

    runs = build_vertical_runs(dev, flags, str, count, &run_count);

    /* set draw background */
    if ((flags & ETO_OPAQUE) && (lprect != NULL))
    {
        PSDRV_SetClip(dev);
        PSDRV_WriteGSave(dev);
        PSDRV_WriteRectangle(dev, lprect->left, lprect->top, lprect->right - lprect->left,
                     lprect->bottom - lprect->top);

        bOpaque = TRUE;
        PSDRV_WriteSetColor(dev, &physDev->bkColor);
        PSDRV_WriteFill(dev);

        PSDRV_WriteGRestore(dev);
        PSDRV_ResetClip(dev);
    }

    while (i < run_count)
    {
        int cnt;

        if (i != run_count - 1)
            cnt = runs[i+1].start- runs[i].start;
        else
            cnt = count - runs[i].start;

        PSDRV_SetFont(dev, runs[i].vertical);

        PSDRV_SetClip(dev);

        /* set clipping */
        if ((flags & ETO_CLIPPED) && (lprect != NULL))
        {
            PSDRV_WriteGSave(dev);

            PSDRV_WriteRectangle(dev, lprect->left, lprect->top, lprect->right - lprect->left,
                     lprect->bottom - lprect->top);

            bClipped = TRUE;
            PSDRV_WriteClip(dev);

            bResult = PSDRV_Text(dev, runs[i].x+x, runs[i].y+y, flags, &str[runs[i].start], cnt, !(bClipped && bOpaque), (lpDx)?&lpDx[runs[i].start]:NULL);

            PSDRV_WriteGRestore(dev);
        }
        else
            bResult = PSDRV_Text(dev, runs[i].x+x, runs[i].y+y, flags, &str[runs[i].start], cnt, TRUE, (lpDx)?&lpDx[runs[i].start]:NULL);

        i++;
        PSDRV_ResetClip(dev);
    }

    HeapFree(GetProcessHeap(),0,runs);
    return bResult;
}

/***********************************************************************
 *           PSDRV_Text
 */
static BOOL PSDRV_Text(PHYSDEV dev, INT x, INT y, UINT flags, LPCWSTR str,
		       UINT count, BOOL bDrawBackground, const INT *lpDx)
{
    PSDRV_PDEVICE *physDev = get_psdrv_dev( dev );
    WORD *glyphs = NULL;

    if (!count)
	return TRUE;

    if(physDev->font.fontloc == Download && !(flags & ETO_GLYPH_INDEX))
    {
        glyphs = HeapAlloc( GetProcessHeap(), 0, count * sizeof(WORD) );
        GetGlyphIndicesW( dev->hdc, str, count, glyphs, 0 );
        str = glyphs;
    }

    PSDRV_WriteMoveTo(dev, x, y);

    if(!lpDx) {
        if(physDev->font.fontloc == Download)
	    PSDRV_WriteDownloadGlyphShow(dev, str, count);
	else
	    PSDRV_WriteBuiltinGlyphShow(dev, str, count);
    }
    else {
        UINT i;
	POINT offset = {0, 0};

        for(i = 0; i < count-1; i++) {
	    if(physDev->font.fontloc == Download)
	        PSDRV_WriteDownloadGlyphShow(dev, str + i, 1);
	    else
	        PSDRV_WriteBuiltinGlyphShow(dev, str + i, 1);
            if(flags & ETO_PDY)
            {
                offset.x += lpDx[i * 2];
                offset.y += lpDx[i * 2 + 1];
            }
            else
                offset.x += lpDx[i];
	    PSDRV_WriteMoveTo(dev, x + offset.x, y + offset.y);
	}
	if(physDev->font.fontloc == Download)
	    PSDRV_WriteDownloadGlyphShow(dev, str + i, 1);
	else
	    PSDRV_WriteBuiltinGlyphShow(dev, str + i, 1);
    }

    HeapFree( GetProcessHeap(), 0, glyphs );
    return TRUE;
}