/*
 *	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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include <string.h>
#include "gdi.h"
#include "psdrv.h"
#include "wine/debug.h"
#include "winspool.h"

WINE_DEFAULT_DEBUG_CHANNEL(psdrv);

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

/***********************************************************************
 *           PSDRV_ExtTextOut
 */
BOOL PSDRV_ExtTextOut( PSDRV_PDEVICE *physDev, INT x, INT y, UINT flags,
		       const RECT *lprect, LPCWSTR str, UINT count,
		       const INT *lpDx )
{
    BOOL bResult = TRUE;
    BOOL bClipped = FALSE;
    BOOL bOpaque = FALSE;
    RECT rect;

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

    /* write font if not already written */
    PSDRV_SetFont(physDev);

    /* set clipping and/or draw background */
    if ((flags & (ETO_CLIPPED | ETO_OPAQUE)) && (lprect != NULL))
    {
        rect = *lprect;
        LPtoDP( physDev->hdc, (POINT *)&rect, 2 );
	PSDRV_WriteGSave(physDev);
	PSDRV_WriteRectangle(physDev, rect.left, rect.top, rect.right - rect.left,
			     rect.bottom - rect.top);

	if (flags & ETO_OPAQUE)
	{
	    bOpaque = TRUE;
	    PSDRV_WriteGSave(physDev);
	    PSDRV_WriteSetColor(physDev, &physDev->bkColor);
	    PSDRV_WriteFill(physDev);
	    PSDRV_WriteGRestore(physDev);
	}

	if (flags & ETO_CLIPPED)
	{
	    bClipped = TRUE;
	    PSDRV_WriteClip(physDev);
	}

	bResult = PSDRV_Text(physDev, x, y, flags, str, count, !(bClipped && bOpaque), lpDx);
	PSDRV_WriteGRestore(physDev);
    }
    else
    {
	bResult = PSDRV_Text(physDev, x, y, flags, str, count, TRUE, lpDx);
    }

    return bResult;
}

/***********************************************************************
 *           PSDRV_Text
 */
static BOOL PSDRV_Text(PSDRV_PDEVICE *physDev, INT x, INT y, UINT flags, LPCWSTR str,
		       UINT count, BOOL bDrawBackground, const INT *lpDx)
{
    SIZE sz;
    TEXTMETRICW tm;
    POINT pt;
    INT ascent, descent;
    WORD *glyphs = NULL;
    DC *dc = physDev->dc;
    UINT align = GetTextAlign( physDev->hdc );

    if (!count)
	return TRUE;


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

    if(align & TA_UPDATECP) {
	x = dc->CursPosX;
	y = dc->CursPosY;
    }

    pt.x = x;
    pt.y = y;
    LPtoDP(physDev->hdc, &pt, 1);
    x = pt.x;
    y = pt.y;

    if(physDev->font.fontloc == Download)
        GetTextExtentPointI(physDev->hdc, glyphs, count, &sz);
    else
        GetTextExtentPoint32W(physDev->hdc, str, count, &sz);

    if(lpDx) {
        SIZE tmpsz;
	INT i;
	/* Get the width of the last char and add on all the offsets */
	if(physDev->font.fontloc == Download)
	    GetTextExtentPointI(physDev->hdc, glyphs + count - 1, 1, &tmpsz);
	else
	    GetTextExtentPoint32W(physDev->hdc, str + count - 1, 1, &tmpsz);
	for(i = 0; i < count-1; i++)
	    tmpsz.cx += lpDx[i];
	sz.cx = tmpsz.cx; /* sz.cy remains untouched */
    }

    sz.cx = INTERNAL_XWSTODS(dc, sz.cx);
    sz.cy = INTERNAL_YWSTODS(dc, sz.cy);

    GetTextMetricsW(physDev->hdc, &tm);
    ascent = INTERNAL_YWSTODS(dc, tm.tmAscent);
    descent = INTERNAL_YWSTODS(dc, tm.tmDescent);

    TRACE("textAlign = %x\n", align);
    switch(align & (TA_LEFT | TA_CENTER | TA_RIGHT) ) {
    case TA_LEFT:
        if(align & TA_UPDATECP)
        {
            POINT pt;
            pt.x = x + sz.cx;
            pt.y = y;
            DPtoLP( physDev->hdc, &pt, 1 );
            dc->CursPosX = pt.x;
	}
	break;

    case TA_CENTER:
	x -= sz.cx/2;
	break;

    case TA_RIGHT:
	x -= sz.cx;
	if(align & TA_UPDATECP)
        {
            POINT pt;
            pt.x = x;
            pt.y = y;
            DPtoLP( physDev->hdc, &pt, 1 );
            dc->CursPosX = pt.x;
	}
	break;
    }

    switch(align & (TA_TOP | TA_BASELINE | TA_BOTTOM) ) {
    case TA_TOP:
        y += ascent;
	break;

    case TA_BASELINE:
	break;

    case TA_BOTTOM:
        y -= descent;
	break;
    }

    if ((GetBkMode( physDev->hdc ) != TRANSPARENT) && bDrawBackground)
    {
	PSDRV_WriteGSave(physDev);
	PSDRV_WriteNewPath(physDev);
	PSDRV_WriteRectangle(physDev, x, y - ascent, sz.cx,
			     ascent + descent);
	PSDRV_WriteSetColor(physDev, &physDev->bkColor);
	PSDRV_WriteFill(physDev);
	PSDRV_WriteGRestore(physDev);
    }

    PSDRV_WriteMoveTo(physDev, x, y);

    if(!lpDx) {
        if(physDev->font.fontloc == Download)
	    PSDRV_WriteDownloadGlyphShow(physDev, glyphs, count);
	else
	    PSDRV_WriteBuiltinGlyphShow(physDev, str, count);
    }
    else {
        INT i;
	float dx = 0.0, dy = 0.0;
	float cos_theta = cos(physDev->font.escapement * M_PI / 1800.0);
	float sin_theta = sin(physDev->font.escapement * M_PI / 1800.0);
        for(i = 0; i < count-1; i++) {
	    TRACE("lpDx[%d] = %d\n", i, lpDx[i]);
	    if(physDev->font.fontloc == Download)
	        PSDRV_WriteDownloadGlyphShow(physDev, glyphs + i, 1);
	    else
	        PSDRV_WriteBuiltinGlyphShow(physDev, str + i, 1);
	    dx += lpDx[i] * cos_theta;
	    dy -= lpDx[i] * sin_theta;
	    PSDRV_WriteMoveTo(physDev, x + INTERNAL_XWSTODS(dc, dx),
			      y + INTERNAL_YWSTODS(dc, dy));
	}
	if(physDev->font.fontloc == Download)
	    PSDRV_WriteDownloadGlyphShow(physDev, glyphs + i, 1);
	else
	    PSDRV_WriteBuiltinGlyphShow(physDev, str + i, 1);
    }

    /*
     * Underline and strikeout attributes.
     */
    if ((tm.tmUnderlined) || (tm.tmStruckOut)) {

        /* Get the thickness and the position for the underline attribute */
        /* We'll use the same thickness for the strikeout attribute       */

        INT escapement =  physDev->font.escapement;

        /* Do the underline */

        if (tm.tmUnderlined) {
            PSDRV_WriteNewPath(physDev); /* will be closed by WriteRectangle */
            if (escapement != 0)  /* rotated text */
            {
                PSDRV_WriteGSave(physDev);  /* save the graphics state */
                PSDRV_WriteMoveTo(physDev, x, y); /* move to the start */

                /* temporarily rotate the coord system */
                PSDRV_WriteRotate(physDev, -escapement/10);

                /* draw the underline relative to the starting point */
                PSDRV_WriteRRectangle(physDev, 0, -physDev->font.underlinePosition,
				      sz.cx, physDev->font.underlineThickness);
            }
            else
                PSDRV_WriteRectangle(physDev, x, y - physDev->font.underlinePosition,
				     sz.cx, physDev->font.underlineThickness);

            PSDRV_WriteFill(physDev);

            if (escapement != 0)  /* rotated text */
                PSDRV_WriteGRestore(physDev);  /* restore the graphics state */
        }

        /* Do the strikeout */

        if (tm.tmStruckOut) {
            PSDRV_WriteNewPath(physDev); /* will be closed by WriteRectangle */
            if (escapement != 0)  /* rotated text */
            {
                PSDRV_WriteGSave(physDev);  /* save the graphics state */
                PSDRV_WriteMoveTo(physDev, x, y); /* move to the start */

                /* temporarily rotate the coord system */
                PSDRV_WriteRotate(physDev, -escapement/10);

                /* draw the line relative to the starting point */
                PSDRV_WriteRRectangle(physDev, 0, -physDev->font.strikeoutPosition,
				      sz.cx, physDev->font.strikeoutThickness);
            }
            else
                PSDRV_WriteRectangle(physDev, x, y - physDev->font.strikeoutPosition,
				     sz.cx, physDev->font.strikeoutThickness);

            PSDRV_WriteFill(physDev);

            if (escapement != 0)  /* rotated text */
                PSDRV_WriteGRestore(physDev);  /* restore the graphics state */
        }
    }

    if(glyphs && glyphs != str) HeapFree(GetProcessHeap(), 0, glyphs);
    return TRUE;
}
