/*
 *	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 <stdarg.h>
#include <stdlib.h>
#include <math.h>

#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "psdrv.h"
#include "winspool.h"
#include "wine/debug.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, INT breakExtra )
{
    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);

    PSDRV_SetClip(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);
    }

    PSDRV_ResetClip(physDev);
    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;
    UINT align = GetTextAlign( physDev->hdc );
    INT char_extra;
    INT *deltas = NULL;
    double cosEsc, sinEsc;
    LOGFONTW lf;

    if (!count)
	return TRUE;

    GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
    if(lf.lfEscapement != 0) {
        cosEsc = cos(lf.lfEscapement * M_PI / 1800);
        sinEsc = sin(lf.lfEscapement * M_PI / 1800);
    } else {
        cosEsc = 1;
        sinEsc = 0;
    }

    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);
	}
    }

    pt.x = x;
    pt.y = y;
    if(align & TA_UPDATECP) GetCurrentPositionEx( physDev->hdc, &pt );
    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((char_extra = GetTextCharacterExtra(physDev->hdc)) != 0) {
        UINT i;
	SIZE tmpsz;

        deltas = HeapAlloc(GetProcessHeap(), 0, count * sizeof(INT));
	for(i = 0; i < count; i++) {
	    if(lpDx)
	        deltas[i] = lpDx[i] + char_extra;
	    else {
	        if(physDev->font.fontloc == Download)
		    GetTextExtentPointI(physDev->hdc, glyphs + i, 1, &tmpsz);
		else
		    GetTextExtentPoint32W(physDev->hdc, str + i, 1, &tmpsz);
	        deltas[i] = tmpsz.cx;
	    }
	}
    } else if(lpDx)
        deltas = (INT*)lpDx;

    if(deltas) {
        SIZE tmpsz;
	UINT 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 += deltas[i];
	sz.cx = tmpsz.cx; /* sz.cy remains untouched */
    }

    sz.cx = PSDRV_XWStoDS(physDev, sz.cx);
    sz.cy = PSDRV_YWStoDS(physDev, sz.cy);

    GetTextMetricsW(physDev->hdc, &tm);
    ascent = abs(PSDRV_YWStoDS(physDev, tm.tmAscent));
    descent = abs(PSDRV_YWStoDS(physDev, 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 * cosEsc;
            pt.y = y - sz.cx * sinEsc;
            DPtoLP( physDev->hdc, &pt, 1 );
            MoveToEx( physDev->hdc, pt.x, pt.y, NULL );
	}
	break;

    case TA_CENTER:
        x -= sz.cx * cosEsc / 2;
        y += sz.cx * sinEsc / 2;
	break;

    case TA_RIGHT:
        x -= sz.cx * cosEsc;
        y += sz.cx * sinEsc;
	if(align & TA_UPDATECP)
        {
            POINT pt;
            pt.x = x;
            pt.y = y;
            DPtoLP( physDev->hdc, &pt, 1 );
            MoveToEx( physDev->hdc, pt.x, pt.y, NULL );
	}
	break;
    }

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

    case TA_BASELINE:
	break;

    case TA_BOTTOM:
        y -= descent * cosEsc;
        x -= descent * sinEsc;
	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(!deltas) {
        if(physDev->font.fontloc == Download)
	    PSDRV_WriteDownloadGlyphShow(physDev, glyphs, count);
	else
	    PSDRV_WriteBuiltinGlyphShow(physDev, str, count);
    }
    else {
        UINT 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, deltas[i]);
	    if(physDev->font.fontloc == Download)
	        PSDRV_WriteDownloadGlyphShow(physDev, glyphs + i, 1);
	    else
	        PSDRV_WriteBuiltinGlyphShow(physDev, str + i, 1);
	    dx += deltas[i] * cos_theta;
	    dy -= deltas[i] * sin_theta;
	    PSDRV_WriteMoveTo(physDev, x + PSDRV_XWStoDS(physDev, dx),
			      y + PSDRV_YWStoDS(physDev, dy));
	}
	if(physDev->font.fontloc == Download)
	    PSDRV_WriteDownloadGlyphShow(physDev, glyphs + i, 1);
	else
	    PSDRV_WriteBuiltinGlyphShow(physDev, str + i, 1);
	if(deltas != lpDx)
	    HeapFree(GetProcessHeap(), 0, deltas);
    }

    /*
     * 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;
}
