/*
 * GDI mapping mode functions
 *
 * Copyright 1993 Alexandre Julliard
 *
 * 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 <stdarg.h>

#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "wownt32.h"
#include "gdi_private.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(dc);


/***********************************************************************
 *           MAPPING_FixIsotropic
 *
 * Fix viewport extensions for isotropic mode.
 */
static void MAPPING_FixIsotropic( DC * dc )
{
    double xdim = fabs((double)dc->vportExtX * GetDeviceCaps( dc->hSelf, HORZSIZE ) /
                  (GetDeviceCaps( dc->hSelf, HORZRES ) * dc->wndExtX));
    double ydim = fabs((double)dc->vportExtY * GetDeviceCaps( dc->hSelf, VERTSIZE ) /
                  (GetDeviceCaps( dc->hSelf, VERTRES ) * dc->wndExtY));

    if (xdim > ydim)
    {
        INT mincx = (dc->vportExtX >= 0) ? 1 : -1;
        dc->vportExtX = floor(dc->vportExtX * ydim / xdim + 0.5);
        if (!dc->vportExtX) dc->vportExtX = mincx;
    }
    else
    {
        INT mincy = (dc->vportExtY >= 0) ? 1 : -1;
        dc->vportExtY = floor(dc->vportExtY * xdim / ydim + 0.5);
        if (!dc->vportExtY) dc->vportExtY = mincy;
    }
}


/***********************************************************************
 *           DPtoLP    (GDI.67)
 */
BOOL16 WINAPI DPtoLP16( HDC16 hdc, LPPOINT16 points, INT16 count )
{
    DC * dc = get_dc_ptr( HDC_32(hdc) );
    if (!dc) return FALSE;

    while (count--)
    {
        points->x = MulDiv( points->x - dc->vportOrgX, dc->wndExtX, dc->vportExtX ) + dc->wndOrgX;
        points->y = MulDiv( points->y - dc->vportOrgY, dc->wndExtY, dc->vportExtY ) + dc->wndOrgY;
        points++;
    }
    release_dc_ptr( dc );
    return TRUE;
}


/***********************************************************************
 *           DPtoLP    (GDI32.@)
 */
BOOL WINAPI DPtoLP( HDC hdc, LPPOINT points, INT count )
{
    DC * dc = get_dc_ptr( hdc );
    if (!dc) return FALSE;

    if (dc->vport2WorldValid)
    {
        while (count--)
        {
            double x = points->x;
            double y = points->y;
            points->x = floor( x * dc->xformVport2World.eM11 +
                               y * dc->xformVport2World.eM21 +
                               dc->xformVport2World.eDx + 0.5 );
            points->y = floor( x * dc->xformVport2World.eM12 +
                               y * dc->xformVport2World.eM22 +
                               dc->xformVport2World.eDy + 0.5 );
            points++;
        }
    }
    release_dc_ptr( dc );
    return (count < 0);
}


/***********************************************************************
 *           LPtoDP    (GDI.99)
 */
BOOL16 WINAPI LPtoDP16( HDC16 hdc, LPPOINT16 points, INT16 count )
{
    DC * dc = get_dc_ptr( HDC_32(hdc) );
    if (!dc) return FALSE;

    while (count--)
    {
        points->x = MulDiv( points->x - dc->wndOrgX, dc->vportExtX, dc->wndExtX ) + dc->vportOrgX;
        points->y = MulDiv( points->y - dc->wndOrgY, dc->vportExtY, dc->wndExtY ) + dc->vportOrgY;
        points++;
    }
    release_dc_ptr( dc );
    return TRUE;
}


/***********************************************************************
 *           LPtoDP    (GDI32.@)
 */
BOOL WINAPI LPtoDP( HDC hdc, LPPOINT points, INT count )
{
    DC * dc = get_dc_ptr( hdc );
    if (!dc) return FALSE;

    while (count--)
    {
        double x = points->x;
        double y = points->y;
        points->x = floor( x * dc->xformWorld2Vport.eM11 +
                           y * dc->xformWorld2Vport.eM21 +
                           dc->xformWorld2Vport.eDx + 0.5 );
        points->y = floor( x * dc->xformWorld2Vport.eM12 +
                           y * dc->xformWorld2Vport.eM22 +
                           dc->xformWorld2Vport.eDy + 0.5 );
        points++;
    }
    release_dc_ptr( dc );
    return TRUE;
}


/***********************************************************************
 *           SetMapMode    (GDI32.@)
 */
INT WINAPI SetMapMode( HDC hdc, INT mode )
{
    INT ret;
    INT horzSize, vertSize, horzRes, vertRes;

    DC * dc = get_dc_ptr( hdc );
    if (!dc) return 0;
    if (dc->funcs->pSetMapMode)
    {
        if((ret = dc->funcs->pSetMapMode( dc->physDev, mode )) != TRUE)
	{
	    if(ret == GDI_NO_MORE_WORK)
	        ret = TRUE;
	    goto done;
	}
    }

    TRACE("%p %d\n", hdc, mode );

    ret = dc->MapMode;

    if (mode == dc->MapMode && (mode == MM_ISOTROPIC || mode == MM_ANISOTROPIC))
        goto done;

    horzSize = GetDeviceCaps( hdc, HORZSIZE );
    vertSize = GetDeviceCaps( hdc, VERTSIZE );
    horzRes  = GetDeviceCaps( hdc, HORZRES );
    vertRes  = GetDeviceCaps( hdc, VERTRES );
    switch(mode)
    {
    case MM_TEXT:
        dc->wndExtX   = 1;
        dc->wndExtY   = 1;
        dc->vportExtX = 1;
        dc->vportExtY = 1;
        break;
    case MM_LOMETRIC:
    case MM_ISOTROPIC:
        dc->wndExtX   = horzSize * 10;
        dc->wndExtY   = vertSize * 10;
        dc->vportExtX = horzRes;
        dc->vportExtY = -vertRes;
        break;
    case MM_HIMETRIC:
        dc->wndExtX   = horzSize * 100;
        dc->wndExtY   = vertSize * 100;
        dc->vportExtX = horzRes;
        dc->vportExtY = -vertRes;
        break;
    case MM_LOENGLISH:
        dc->wndExtX   = MulDiv(1000, horzSize, 254);
        dc->wndExtY   = MulDiv(1000, vertSize, 254);
        dc->vportExtX = horzRes;
        dc->vportExtY = -vertRes;
        break;
    case MM_HIENGLISH:
        dc->wndExtX   = MulDiv(10000, horzSize, 254);
        dc->wndExtY   = MulDiv(10000, vertSize, 254);
        dc->vportExtX = horzRes;
        dc->vportExtY = -vertRes;
        break;
    case MM_TWIPS:
        dc->wndExtX   = MulDiv(14400, horzSize, 254);
        dc->wndExtY   = MulDiv(14400, vertSize, 254);
        dc->vportExtX = horzRes;
        dc->vportExtY = -vertRes;
        break;
    case MM_ANISOTROPIC:
        break;
    default:
        goto done;
    }
    dc->MapMode = mode;
    DC_UpdateXforms( dc );
 done:
    release_dc_ptr( dc );
    return ret;
}


/***********************************************************************
 *           SetViewportExtEx    (GDI32.@)
 */
BOOL WINAPI SetViewportExtEx( HDC hdc, INT x, INT y, LPSIZE size )
{
    INT ret = TRUE;
    DC * dc = get_dc_ptr( hdc );
    if (!dc) return FALSE;
    if (dc->funcs->pSetViewportExt)
    {
        if((ret = dc->funcs->pSetViewportExt( dc->physDev, x, y )) != TRUE)
	{
	    if(ret == GDI_NO_MORE_WORK)
	        ret = TRUE;
	    goto done;
	}
    }
    if (size)
    {
	size->cx = dc->vportExtX;
	size->cy = dc->vportExtY;
    }
    if ((dc->MapMode != MM_ISOTROPIC) && (dc->MapMode != MM_ANISOTROPIC))
	goto done;
    if (!x || !y)
    {
        ret = FALSE;
        goto done;
    }
    dc->vportExtX = x;
    dc->vportExtY = y;
    if (dc->MapMode == MM_ISOTROPIC) MAPPING_FixIsotropic( dc );
    DC_UpdateXforms( dc );
 done:
    release_dc_ptr( dc );
    return ret;
}


/***********************************************************************
 *           SetViewportOrgEx    (GDI32.@)
 */
BOOL WINAPI SetViewportOrgEx( HDC hdc, INT x, INT y, LPPOINT pt )
{
    INT ret = TRUE;
    DC * dc = get_dc_ptr( hdc );
    if (!dc) return FALSE;
    if (dc->funcs->pSetViewportOrg)
    {
        if((ret = dc->funcs->pSetViewportOrg( dc->physDev, x, y )) != TRUE)
	{
	    if(ret == GDI_NO_MORE_WORK)
	        ret = TRUE;
	    goto done;
	}
    }
    if (pt)
    {
        pt->x = dc->vportOrgX;
	pt->y = dc->vportOrgY;
    }
    dc->vportOrgX = x;
    dc->vportOrgY = y;
    DC_UpdateXforms( dc );

 done:
    release_dc_ptr( dc );
    return ret;
}


/***********************************************************************
 *           SetWindowExtEx    (GDI32.@)
 */
BOOL WINAPI SetWindowExtEx( HDC hdc, INT x, INT y, LPSIZE size )
{
    INT ret = TRUE;
    DC * dc = get_dc_ptr( hdc );
    if (!dc) return FALSE;
    if (dc->funcs->pSetWindowExt)
    {
        if((ret = dc->funcs->pSetWindowExt( dc->physDev, x, y )) != TRUE)
	{
	    if(ret == GDI_NO_MORE_WORK)
	        ret = TRUE;
	    goto done;
	}
    }
    if (size)
    {
	size->cx = dc->wndExtX;
	size->cy = dc->wndExtY;
    }
    if ((dc->MapMode != MM_ISOTROPIC) && (dc->MapMode != MM_ANISOTROPIC))
	goto done;
    if (!x || !y)
    {
        ret = FALSE;
        goto done;
    }
    dc->wndExtX = x;
    dc->wndExtY = y;
    /* The API docs say that you should call SetWindowExtEx before
       SetViewportExtEx. This advice does not imply that Windows
       doesn't ensure the isotropic mapping after SetWindowExtEx! */
    if (dc->MapMode == MM_ISOTROPIC) MAPPING_FixIsotropic( dc );
    DC_UpdateXforms( dc );
 done:
    release_dc_ptr( dc );
    return ret;
}


/***********************************************************************
 *           SetWindowOrgEx    (GDI32.@)
 */
BOOL WINAPI SetWindowOrgEx( HDC hdc, INT x, INT y, LPPOINT pt )
{
    INT ret = TRUE;
    DC * dc = get_dc_ptr( hdc );
    if (!dc) return FALSE;
    if (dc->funcs->pSetWindowOrg)
    {
        if((ret = dc->funcs->pSetWindowOrg( dc->physDev, x, y )) != TRUE)
	{
	    if(ret == GDI_NO_MORE_WORK)
	        ret = TRUE;
	    goto done;
	}
    }
    if (pt)
    {
        pt->x = dc->wndOrgX;
	pt->y = dc->wndOrgY;
    }
    dc->wndOrgX = x;
    dc->wndOrgY = y;
    DC_UpdateXforms( dc );
 done:
    release_dc_ptr( dc );
    return ret;
}


/***********************************************************************
 *           OffsetViewportOrgEx    (GDI32.@)
 */
BOOL WINAPI OffsetViewportOrgEx( HDC hdc, INT x, INT y, LPPOINT pt)
{
    INT ret = TRUE;
    DC * dc = get_dc_ptr( hdc );
    if (!dc) return FALSE;
    if (dc->funcs->pOffsetViewportOrg)
    {
        if((ret = dc->funcs->pOffsetViewportOrg( dc->physDev, x, y )) != TRUE)
	{
	    if(ret == GDI_NO_MORE_WORK)
	        ret = TRUE;
	    goto done;
	}
    }
    if (pt)
    {
        pt->x = dc->vportOrgX;
	pt->y = dc->vportOrgY;
    }
    dc->vportOrgX += x;
    dc->vportOrgY += y;
    DC_UpdateXforms( dc );
 done:
    release_dc_ptr( dc );
    return ret;
}


/***********************************************************************
 *           OffsetWindowOrgEx    (GDI32.@)
 */
BOOL WINAPI OffsetWindowOrgEx( HDC hdc, INT x, INT y, LPPOINT pt )
{
    INT ret = TRUE;
    DC * dc = get_dc_ptr( hdc );
    if (!dc) return FALSE;
    if (dc->funcs->pOffsetWindowOrg)
    {
        if((ret = dc->funcs->pOffsetWindowOrg( dc->physDev, x, y )) != TRUE)
	{
	    if(ret == GDI_NO_MORE_WORK)
	        ret = TRUE;
	    goto done;
	}
    }
    if (pt)
    {
        pt->x = dc->wndOrgX;
	pt->y = dc->wndOrgY;
    }
    dc->wndOrgX += x;
    dc->wndOrgY += y;
    DC_UpdateXforms( dc );
 done:
    release_dc_ptr( dc );
    return ret;
}


/***********************************************************************
 *           ScaleViewportExtEx    (GDI32.@)
 */
BOOL WINAPI ScaleViewportExtEx( HDC hdc, INT xNum, INT xDenom,
                                    INT yNum, INT yDenom, LPSIZE size )
{
    INT ret = TRUE;
    DC * dc = get_dc_ptr( hdc );
    if (!dc) return FALSE;
    if (dc->funcs->pScaleViewportExt)
    {
        if((ret = dc->funcs->pScaleViewportExt( dc->physDev, xNum, xDenom, yNum, yDenom )) != TRUE)
	{
	    if(ret == GDI_NO_MORE_WORK)
	        ret = TRUE;
	    goto done;
	}
    }
    if (size)
    {
	size->cx = dc->vportExtX;
	size->cy = dc->vportExtY;
    }
    if ((dc->MapMode != MM_ISOTROPIC) && (dc->MapMode != MM_ANISOTROPIC))
	goto done;
    if (!xNum || !xDenom || !xNum || !yDenom)
    {
        ret = FALSE;
        goto done;
    }
    dc->vportExtX = (dc->vportExtX * xNum) / xDenom;
    dc->vportExtY = (dc->vportExtY * yNum) / yDenom;
    if (dc->vportExtX == 0) dc->vportExtX = 1;
    if (dc->vportExtY == 0) dc->vportExtY = 1;
    if (dc->MapMode == MM_ISOTROPIC) MAPPING_FixIsotropic( dc );
    DC_UpdateXforms( dc );
 done:
    release_dc_ptr( dc );
    return ret;
}


/***********************************************************************
 *           ScaleWindowExtEx    (GDI32.@)
 */
BOOL WINAPI ScaleWindowExtEx( HDC hdc, INT xNum, INT xDenom,
                                  INT yNum, INT yDenom, LPSIZE size )
{
    INT ret = TRUE;
    DC * dc = get_dc_ptr( hdc );
    if (!dc) return FALSE;
    if (dc->funcs->pScaleWindowExt)
    {
        if((ret = dc->funcs->pScaleWindowExt( dc->physDev, xNum, xDenom, yNum, yDenom )) != TRUE)
	{
	    if(ret == GDI_NO_MORE_WORK)
	        ret = TRUE;
	    goto done;
	}
    }
    if (size)
    {
	size->cx = dc->wndExtX;
	size->cy = dc->wndExtY;
    }
    if ((dc->MapMode != MM_ISOTROPIC) && (dc->MapMode != MM_ANISOTROPIC))
	goto done;
    if (!xNum || !xDenom || !xNum || !yDenom)
    {
        ret = FALSE;
        goto done;
    }
    dc->wndExtX = (dc->wndExtX * xNum) / xDenom;
    dc->wndExtY = (dc->wndExtY * yNum) / yDenom;
    if (dc->wndExtX == 0) dc->wndExtX = 1;
    if (dc->wndExtY == 0) dc->wndExtY = 1;
    if (dc->MapMode == MM_ISOTROPIC) MAPPING_FixIsotropic( dc );
    DC_UpdateXforms( dc );
 done:
    release_dc_ptr( dc );
    return ret;
}