/*
 * Enhanced metafile functions
 * Copyright 1998 Douglas Ridgway
 *           1999 Huw D M Davies 
 */ 

#include <string.h>
#include <assert.h>
#include "winbase.h"
#include "wingdi.h"
#include "wine/winestring.h"
#include "winerror.h"
#include "enhmetafile.h"
#include "debugtools.h"
#include "heap.h"

DEFAULT_DEBUG_CHANNEL(enhmetafile)

/****************************************************************************
 *          EMF_Create_HENHMETAFILE
 */
HENHMETAFILE EMF_Create_HENHMETAFILE(ENHMETAHEADER *emh, HFILE hFile, HANDLE
				     hMapping )
{
    HENHMETAFILE hmf = GDI_AllocObject( sizeof(ENHMETAFILEOBJ),
					ENHMETAFILE_MAGIC );
    ENHMETAFILEOBJ *metaObj = (ENHMETAFILEOBJ *)GDI_HEAP_LOCK( hmf );
    metaObj->emh = emh;
    metaObj->hFile = hFile;
    metaObj->hMapping = hMapping;
    GDI_HEAP_UNLOCK( hmf );
    return hmf;
}

/****************************************************************************
 *          EMF_Delete_HENHMETAFILE
 */
static BOOL EMF_Delete_HENHMETAFILE( HENHMETAFILE hmf )
{
    ENHMETAFILEOBJ *metaObj = (ENHMETAFILEOBJ *)GDI_GetObjPtr( hmf,
							   ENHMETAFILE_MAGIC );
    if(!metaObj) return FALSE;
    if(metaObj->hMapping) {
        UnmapViewOfFile( metaObj->emh );
	CloseHandle( metaObj->hMapping );
	CloseHandle( metaObj->hFile );
    } else
        HeapFree( SystemHeap, 0, metaObj->emh );
    return GDI_FreeObject( hmf );
}

/******************************************************************
 *         EMF_GetEnhMetaHeader
 *
 * Returns ptr to ENHMETAHEADER associated with HENHMETAFILE
 * Should be followed by call to EMF_ReleaseEnhMetaHeader
 */
static ENHMETAHEADER *EMF_GetEnhMetaHeader( HENHMETAFILE hmf )
{
    ENHMETAFILEOBJ *metaObj = (ENHMETAFILEOBJ *)GDI_GetObjPtr( hmf,
							   ENHMETAFILE_MAGIC );
    TRACE("hmf %04x -> enhmetaObj %p\n", hmf, metaObj);
    return metaObj ? metaObj->emh : NULL;
}

/******************************************************************
 *         EMF_ReleaseEnhMetaHeader
 *
 * Releases ENHMETAHEADER associated with HENHMETAFILE
 */
static BOOL EMF_ReleaseEnhMetaHeader( HENHMETAFILE hmf )
{
    return GDI_HEAP_UNLOCK( hmf );
}

/*****************************************************************************
 *         EMF_GetEnhMetaFile
 *
 */
static HENHMETAFILE EMF_GetEnhMetaFile( HFILE hFile )
{
    ENHMETAHEADER *emh;
    HANDLE hMapping;
    
    hMapping = CreateFileMappingA( hFile, NULL, PAGE_READONLY, 0, 0, NULL );
    emh = MapViewOfFile( hMapping, FILE_MAP_READ, 0, 0, 0 );

    if (emh->iType != EMR_HEADER || emh->dSignature != ENHMETA_SIGNATURE) {
        WARN("Invalid emf header type 0x%08lx sig 0x%08lx.\n",
	     emh->iType, emh->dSignature);
	UnmapViewOfFile( emh );
	CloseHandle( hMapping );
	return 0;
    }
    return EMF_Create_HENHMETAFILE( emh, hFile, hMapping );
}


/*****************************************************************************
 *          GetEnhMetaFileA (GDI32.174)
 *
 *
 */
HENHMETAFILE WINAPI GetEnhMetaFileA( 
	     LPCSTR lpszMetaFile  /* filename of enhanced metafile */
    )
{
    HENHMETAFILE hmf;
    HFILE hFile;

    hFile = CreateFileA(lpszMetaFile, GENERIC_READ, FILE_SHARE_READ, 0,
			OPEN_EXISTING, 0, 0);
    if (hFile == INVALID_HANDLE_VALUE) {
        WARN("could not open %s\n", lpszMetaFile);
	return 0;
    }
    hmf = EMF_GetEnhMetaFile( hFile );
    if(!hmf)
        CloseHandle( hFile );
    return hmf;
}

/*****************************************************************************
 *          GetEnhMetaFile32W  (GDI32.180)
 */
HENHMETAFILE WINAPI GetEnhMetaFileW(
             LPCWSTR lpszMetaFile)  /* filename of enhanced metafile */ 
{
    HENHMETAFILE hmf;
    HFILE hFile;

    hFile = CreateFileW(lpszMetaFile, GENERIC_READ, FILE_SHARE_READ, 0,
			OPEN_EXISTING, 0, 0);
    if (hFile == INVALID_HANDLE_VALUE) {
        WARN("could not open %s\n", debugstr_w(lpszMetaFile));
	return 0;
    }
    hmf = EMF_GetEnhMetaFile( hFile );
    if(!hmf)
        CloseHandle( hFile );
    return hmf;
}

/*****************************************************************************
 *        GetEnhMetaFileHeader  (GDI32.178)
 *
 *  If _buf_ is NULL, returns the size of buffer required.
 *  Otherwise, copy up to _bufsize_ bytes of enhanced metafile header into 
 *  _buf.
 */
UINT WINAPI GetEnhMetaFileHeader( 
       HENHMETAFILE hmf, /* enhanced metafile */
       UINT bufsize,     /* size of buffer */
       LPENHMETAHEADER buf /* buffer */ 
    )
{
    LPENHMETAHEADER emh;

    if (!buf) return sizeof(ENHMETAHEADER);
    emh = EMF_GetEnhMetaHeader(hmf);
    if(!emh) return FALSE;
    memmove(buf, emh, MIN(sizeof(ENHMETAHEADER), bufsize));
    EMF_ReleaseEnhMetaHeader(hmf);
    return MIN(sizeof(ENHMETAHEADER), bufsize);
}


/*****************************************************************************
 *          GetEnhMetaFileDescription32A  (GDI32.176)
 */
UINT WINAPI GetEnhMetaFileDescriptionA( 
       HENHMETAFILE hmf, /* enhanced metafile */
       UINT size, /* size of buf */ 
       LPSTR buf /* buffer to receive description */
    )
{
     LPENHMETAHEADER emh = EMF_GetEnhMetaHeader(hmf);
     INT first;
 
     if(!emh) return FALSE;
     if(emh->nDescription == 0 || emh->offDescription == 0) {
         EMF_ReleaseEnhMetaHeader(hmf);
 	return 0;
     }
     if (!buf || !size ) {
         EMF_ReleaseEnhMetaHeader(hmf);
 	return emh->nDescription;
     }
 
     first = lstrlenW( (WCHAR *) ((char *) emh + emh->offDescription));
 
     lstrcpynWtoA(buf, (WCHAR *) ((char *) emh + emh->offDescription), size);
     buf += first + 1;
     lstrcpynWtoA(buf, (WCHAR *) ((char *) emh + emh->offDescription+2*(first+1)),
 		 size - first - 1);
 
     EMF_ReleaseEnhMetaHeader(hmf);
     return MIN(size, emh->nDescription);
}

/*****************************************************************************
 *          GetEnhMetaFileDescription32W  (GDI32.177)
 *
 *  Copies the description string of an enhanced metafile into a buffer 
 *  _buf_.
 *
 *  If _buf_ is NULL, returns size of _buf_ required. Otherwise, returns
 *  number of characters copied.
 */
UINT WINAPI GetEnhMetaFileDescriptionW( 
       HENHMETAFILE hmf, /* enhanced metafile */
       UINT size, /* size of buf */ 
       LPWSTR buf /* buffer to receive description */
    )
{
     LPENHMETAHEADER emh = EMF_GetEnhMetaHeader(hmf);

     if(!emh) return FALSE;
     if(emh->nDescription == 0 || emh->offDescription == 0) {
         EMF_ReleaseEnhMetaHeader(hmf);
 	return 0;
     }
     if (!buf || !size ) {
         EMF_ReleaseEnhMetaHeader(hmf);
 	return emh->nDescription;
     }
 
     memmove(buf, (char *) emh + emh->offDescription, 
 	    MIN(size,emh->nDescription));
     EMF_ReleaseEnhMetaHeader(hmf);
     return MIN(size, emh->nDescription);
}

/****************************************************************************
 *    SetEnhMetaFileBits (GDI32.315)
 *
 *  Creates an enhanced metafile by copying _bufsize_ bytes from _buf_.
 */
HENHMETAFILE WINAPI SetEnhMetaFileBits(UINT bufsize, const BYTE *buf)
{
    ENHMETAHEADER *emh = HeapAlloc( SystemHeap, 0, bufsize );
    memmove(emh, buf, bufsize);
    return EMF_Create_HENHMETAFILE( emh, 0, 0 );
}

/*****************************************************************************
 *  GetEnhMetaFileBits (GDI32.175)
 *
 */
UINT WINAPI GetEnhMetaFileBits(
    HENHMETAFILE hmf, 
    UINT bufsize, 
    LPBYTE buf  
) {
  return 0;
}

/*****************************************************************************
 *           PlayEnhMetaFileRecord  (GDI32.264)
 *
 *  Render a single enhanced metafile record in the device context hdc.
 *
 *  RETURNS
 *    TRUE on success, FALSE on error.
 *  BUGS
 *    Many unimplemented records.
 */
BOOL WINAPI PlayEnhMetaFileRecord( 
     HDC hdc, /* device context in which to render EMF record */
     LPHANDLETABLE handletable, /* array of handles to be used in rendering record */
     const ENHMETARECORD *mr, /* EMF record to render */
     UINT handles  /* size of handle array */
     ) 
{
  int type;
  TRACE(
	"hdc = %08x, handletable = %p, record = %p, numHandles = %d\n", 
	  hdc, handletable, mr, handles);
  if (!mr) return FALSE;

  type = mr->iType;

  TRACE(" type=%d\n", type);
  switch(type) 
    {
    case EMR_HEADER:
      {
	/* ENHMETAHEADER *h = (LPENHMETAHEADER) mr; */
	break;
      }
    case EMR_EOF:
      break;
    case EMR_GDICOMMENT:
      /* application defined and processed */
      break;
    case EMR_SETMAPMODE:
      {
	DWORD mode = mr->dParm[0];
	SetMapMode(hdc, mode);
	break;
      }
    case EMR_SETBKMODE:
      {
	DWORD mode = mr->dParm[0];
	SetBkMode(hdc, mode);
	break;
      }
    case EMR_SETBKCOLOR:
      {
	DWORD mode = mr->dParm[0];
	SetBkColor(hdc, mode);
	break;
      }
    case EMR_SETPOLYFILLMODE:
      {
	DWORD mode = mr->dParm[0];
	SetPolyFillMode(hdc, mode);
	break;
      }
    case EMR_SETROP2:
      {
	DWORD mode = mr->dParm[0];
	SetROP2(hdc, mode);
	break;
      }
    case EMR_SETSTRETCHBLTMODE:
      {
	DWORD mode = mr->dParm[0];
	SetStretchBltMode(hdc, mode);
	break;
      }
    case EMR_SETTEXTALIGN:
      {
	DWORD align = mr->dParm[0];
	SetTextAlign(hdc, align);
	break;
      }
    case EMR_SETTEXTCOLOR:
      {
	DWORD color = mr->dParm[0];
	SetTextColor(hdc, color);
	break;
      }
    case EMR_SAVEDC:
      {
	SaveDC(hdc);
	break;
      }
    case EMR_RESTOREDC:
      {
	RestoreDC(hdc, mr->dParm[0]);
	break;
      }
    case EMR_INTERSECTCLIPRECT:
      {
	INT left = mr->dParm[0], top = mr->dParm[1], right = mr->dParm[2],
	      bottom = mr->dParm[3];
	IntersectClipRect(hdc, left, top, right, bottom);
	break;
      }
    case EMR_SELECTOBJECT:
      {
	DWORD obj = mr->dParm[0];
	SelectObject(hdc, (handletable->objectHandle)[obj]);
	break;
      }
    case EMR_DELETEOBJECT:
      {
	DWORD obj = mr->dParm[0];
	DeleteObject( (handletable->objectHandle)[obj]);
	(handletable->objectHandle)[obj] = 0;
	break;
      }
    case EMR_SETWINDOWORGEX:
      {
	DWORD x = mr->dParm[0], y = mr->dParm[1];
	SetWindowOrgEx(hdc, x, y, NULL);
	break;
      }
    case EMR_SETWINDOWEXTEX:
      {
	DWORD x = mr->dParm[0], y = mr->dParm[1];
	SetWindowExtEx(hdc, x, y, NULL);
	break;
      }
    case EMR_SETVIEWPORTORGEX:
      {
	DWORD x = mr->dParm[0], y = mr->dParm[1];
	SetViewportOrgEx(hdc, x, y, NULL);
	break;
      }
    case EMR_SETVIEWPORTEXTEX:
      {
	DWORD x = mr->dParm[0], y = mr->dParm[1];
	SetViewportExtEx(hdc, x, y, NULL);
	break;
      }
    case EMR_CREATEPEN:
      {
	DWORD obj = mr->dParm[0];
	(handletable->objectHandle)[obj] = 
	  CreatePenIndirect((LOGPEN *) &(mr->dParm[1]));
	break;
      }
    case EMR_EXTCREATEPEN:
      {
	DWORD obj = mr->dParm[0];
	DWORD style = mr->dParm[1], brush = mr->dParm[2];
	LOGBRUSH *b = (LOGBRUSH *) &mr->dParm[3];
	FIXME("Some ExtCreatePen args not handled\n");
	(handletable->objectHandle)[obj] = 
	  ExtCreatePen(style, brush, b, 0, NULL);
	break;
      }
    case EMR_CREATEBRUSHINDIRECT:
      {
	DWORD obj = mr->dParm[0];
	(handletable->objectHandle)[obj] = 
	  CreateBrushIndirect((LOGBRUSH *) &(mr->dParm[1]));
	break;
      }
    case EMR_EXTCREATEFONTINDIRECTW:
	{
	DWORD obj = mr->dParm[0];
	(handletable->objectHandle)[obj] = 
	  CreateFontIndirectW((LOGFONTW *) &(mr->dParm[1]));
	break;
	}
    case EMR_MOVETOEX:
      {
	DWORD x = mr->dParm[0], y = mr->dParm[1];
	MoveToEx(hdc, x, y, NULL);
	break;
      }
    case EMR_LINETO:
      {
	DWORD x = mr->dParm[0], y = mr->dParm[1];
        LineTo(hdc, x, y);
	break;
      }
    case EMR_RECTANGLE:
      {
	INT left = mr->dParm[0], top = mr->dParm[1], right = mr->dParm[2],
	      bottom = mr->dParm[3];
	Rectangle(hdc, left, top, right, bottom);
	break;
      }
    case EMR_ELLIPSE:
      {
	INT left = mr->dParm[0], top = mr->dParm[1], right = mr->dParm[2],
	      bottom = mr->dParm[3];
	Ellipse(hdc, left, top, right, bottom);
	break;
      }
    case EMR_POLYGON16:
      {
	/* 0-3 : a bounding rectangle? */
	INT count = mr->dParm[4];
	FIXME("Some Polygon16 args not handled\n");
	Polygon16(hdc, (POINT16 *)&mr->dParm[5], count);
	break;
      }
    case EMR_POLYLINE16:
      {
	/* 0-3 : a bounding rectangle? */
	INT count = mr->dParm[4];
	FIXME("Some Polyline16 args not handled\n");
	Polyline16(hdc, (POINT16 *)&mr->dParm[5], count);
	break;
      }

#if 0
    case EMR_POLYPOLYGON16:
      {
	INT polygons = mr->dParm[z];
	LPPOINT16 pts = (LPPOINT16) &mr->dParm[x];
	LPINT16 counts = (LPINT16) &mr->dParm[y];
	PolyPolygon16(hdc, pts, counts, polygons);
	break;
      }
#endif
    case EMR_STRETCHDIBITS:
      {
	LONG xDest = mr->dParm[4];
	LONG yDest = mr->dParm[5];
	LONG xSrc = mr->dParm[6];
	LONG ySrc = mr->dParm[7];
	LONG cxSrc = mr->dParm[8];
	LONG cySrc = mr->dParm[9];
	DWORD offBmiSrc = mr->dParm[10];
	DWORD offBitsSrc = mr->dParm[12];
	DWORD iUsageSrc = mr->dParm[14];
	DWORD dwRop = mr->dParm[15];
	LONG cxDest = mr->dParm[16];
	LONG cyDest = mr->dParm[17];

	StretchDIBits(hdc,xDest,yDest,cxDest,cyDest,
			    xSrc,ySrc,cxSrc,cySrc,
			    ((char *)mr)+offBitsSrc,
			    (const BITMAPINFO *)(((char *)mr)+offBmiSrc),
			    iUsageSrc,dwRop);
	break;
    }
    case EMR_EXTTEXTOUTW:
      {
	/* 0-3: ??? */
	DWORD flags = mr->dParm[4];
	/* 5, 6: ??? */
	DWORD x = mr->dParm[7], y = mr->dParm[8];
	DWORD count = mr->dParm[9];
	/* 10-16: ??? */
	LPWSTR str = (LPWSTR)& mr->dParm[17];
	/* trailing info: dx array? */
	FIXME("Many ExtTextOut args not handled\n");
	ExtTextOutW(hdc, x, y, flags, /* lpRect */ NULL, 
		      str, count, /* lpDx */ NULL); 
	break;
      }

    default:
      FIXME("type %d is unimplemented\n", type);
      /*  SetLastError(E_NOTIMPL); */
      break;
    }
  return TRUE;
}


/*****************************************************************************
 *
 *        EnumEnhMetaFile32  (GDI32.79)
 *
 *  Walk an enhanced metafile, calling a user-specified function _EnhMetaFunc_
 *  for each
 *  record. Returns when either every record has been used or 
 *  when _EnhMetaFunc_ returns FALSE.
 *
 *
 * RETURNS
 *  TRUE if every record is used, FALSE if any invocation of _EnhMetaFunc_
 *  returns FALSE.
 *
 * BUGS
 *   Ignores rect.
 */
BOOL WINAPI EnumEnhMetaFile( 
     HDC hdc, /* device context to pass to _EnhMetaFunc_ */
     HENHMETAFILE hmf, /* EMF to walk */
     ENHMFENUMPROC callback, /* callback function */ 
     LPVOID data, /* optional data for callback function */
     const RECT *rect  /* bounding rectangle for rendered metafile */
    )
{
    BOOL ret = TRUE;
    LPENHMETARECORD p = (LPENHMETARECORD) EMF_GetEnhMetaHeader(hmf);
    INT count;
    HANDLETABLE *ht;

    if(!p) return FALSE;
    count = ((LPENHMETAHEADER) p)->nHandles;
    ht = HeapAlloc( GetProcessHeap(), 0, sizeof(HANDLETABLE)*count);
    ht->objectHandle[0] = hmf;
    while (ret) {
        ret = (*callback)(hdc, ht, p, count, data); 
	if (p->iType == EMR_EOF) break;
	p = (LPENHMETARECORD) ((char *) p + p->nSize);
    }
    HeapFree( GetProcessHeap(), 0, ht);
    EMF_ReleaseEnhMetaHeader(hmf);
    return ret;
}


/**************************************************************************
 *    PlayEnhMetaFile  (GDI32.263)
 *
 *    Renders an enhanced metafile into a specified rectangle *lpRect
 *    in device context hdc.
 *
 * BUGS
 *    Almost entirely unimplemented
 *
 */
BOOL WINAPI PlayEnhMetaFile( 
       HDC hdc, /* DC to render into */
       HENHMETAFILE hmf, /* metafile to render */
       const RECT *lpRect  /* rectangle to place metafile inside */
      )
{
    LPENHMETARECORD p = (LPENHMETARECORD) EMF_GetEnhMetaHeader(hmf);
    INT count;
    HANDLETABLE *ht;
    BOOL ret = FALSE;
    INT savedMode = 0;

    if(!p) return FALSE;
    count = ((LPENHMETAHEADER) p)->nHandles;
    ht = HeapAlloc( GetProcessHeap(), 0, sizeof(HANDLETABLE) * count);
    if (lpRect) {
        LPENHMETAHEADER h = (LPENHMETAHEADER) p;
	FLOAT xscale = (h->rclBounds.right - h->rclBounds.left) /
	  (lpRect->right - lpRect->left);
	FLOAT yscale = (h->rclBounds.bottom - h->rclBounds.top) /
	  (lpRect->bottom - lpRect->top);
	XFORM xform;
	xform.eM11 = xscale;
	xform.eM12 = 0;
	xform.eM21 = 0;
	xform.eM22 = yscale;
        xform.eDx = lpRect->left;
	xform.eDy = lpRect->top; 
	FIXME("play into rect doesn't work\n");
	savedMode = SetGraphicsMode(hdc, GM_ADVANCED);
	if (!SetWorldTransform(hdc, &xform)) {
	    WARN("World transform failed!\n");
	}
    }

    ht->objectHandle[0] = hmf;
    while (1) {
        PlayEnhMetaFileRecord(hdc, ht, p, count);
	if (p->iType == EMR_EOF) break;
	p = (LPENHMETARECORD) ((char *) p + p->nSize); /* casted so that arithmetic is in bytes */
    }
    HeapFree( GetProcessHeap(), 0, ht );
    EMF_ReleaseEnhMetaHeader(hmf);
    if (savedMode) SetGraphicsMode(hdc, savedMode);
    ret = TRUE; /* FIXME: calculate a more accurate return value */
    return ret;
}

/*****************************************************************************
 *  DeleteEnhMetaFile (GDI32.68)
 *
 *  Deletes an enhanced metafile and frees the associated storage.
 */
BOOL WINAPI DeleteEnhMetaFile(HENHMETAFILE hmf)
{
    return EMF_Delete_HENHMETAFILE( hmf );
}

/*****************************************************************************
 *  CopyEnhMetaFileA (GDI32.21)  Duplicate an enhanced metafile
 *
 *   
 */
HENHMETAFILE WINAPI CopyEnhMetaFileA(
    HENHMETAFILE hmfSrc, 
    LPCSTR file)
{
    ENHMETAHEADER *emrSrc = EMF_GetEnhMetaHeader( hmfSrc ), *emrDst;
    HENHMETAFILE hmfDst;

    if(!emrSrc) return FALSE;
    if (!file) {
        emrDst = HeapAlloc( SystemHeap, 0, emrSrc->nBytes );
	memcpy( emrDst, emrSrc, emrSrc->nBytes );
	hmfDst = EMF_Create_HENHMETAFILE( emrDst, 0, 0 );
    } else {
        HFILE hFile;
        hFile = CreateFileA( file, GENERIC_WRITE | GENERIC_READ, 0, NULL,
			     CREATE_ALWAYS, 0, -1);
	WriteFile( hFile, emrSrc, emrSrc->nBytes, 0, 0);
	hmfDst = EMF_GetEnhMetaFile( hFile );
    }
    EMF_ReleaseEnhMetaHeader( hmfSrc );
    return hmfDst;
}


/*****************************************************************************
 *  GetEnhMetaFilePaletteEntries (GDI32.179)  
 * 
 *  Copy the palette and report size  
 */

UINT WINAPI GetEnhMetaFilePaletteEntries(HENHMETAFILE hemf,
					     UINT cEntries,
					     LPPALETTEENTRY lppe)
{
    return 0;
}


/******************************************************************
 *         SetWinMetaFileBits   (GDI32.343)
 *      
 *         Translate from old style to new style.
 */

HENHMETAFILE WINAPI SetWinMetaFileBits(UINT cbBuffer,
					   CONST BYTE *lpbBuffer,
					   HDC hdcRef,
					   CONST METAFILEPICT *lpmfp
					   ) 
{
     FIXME("Stub\n");
     return 0;
}




