/** \file
 * \brief Draw Functions for GDI
 *
 * See Copyright Notice in "iup.h"
 */

#include <windows.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <math.h>

#include "iup.h"

#include "iup_attrib.h"
#include "iup_class.h"
#include "iup_image.h"
#include "iup_drvdraw.h"
#include "iup_draw.h"

#include "iupwin_drv.h"
#include "iupwin_info.h"
#include "iupwin_draw.h"
#include "iupwin_str.h"

#include "backend-gdix.h"
#define iupColor2ARGB(_c) WD_ARGB(iupDrawAlpha(_c), iupDrawRed(_c), iupDrawGreen(_c), iupDrawBlue(_c))

/* From iupwin_draw_wdl.c */
int iupwinDrawInitWDL(void);
int iupwinDrawFinishWDL(void);
IdrawCanvas* iupdrvDrawCreateCanvasWDL(Ihandle* ih);
void iupdrvDrawKillCanvasWDL(IdrawCanvas* dc);
void iupdrvDrawFlushWDL(IdrawCanvas* dc);
void iupdrvDrawUpdateSizeWDL(IdrawCanvas* dc);
void iupdrvDrawGetSizeWDL(IdrawCanvas* dc, int *w, int *h);
void iupdrvDrawLineWDL(IdrawCanvas* dc, int x1, int y1, int x2, int y2, long color, int style, int line_width);
void iupdrvDrawRectangleWDL(IdrawCanvas* dc, int x1, int y1, int x2, int y2, long color, int style, int line_width);
void iupdrvDrawArcWDL(IdrawCanvas* dc, int x1, int y1, int x2, int y2, double a1, double a2, long color, int style, int line_width);
void iupdrvDrawEllipseWDL(IdrawCanvas* dc, int x1, int y1, int x2, int y2, long color, int style, int line_width);
void iupdrvDrawPolygonWDL(IdrawCanvas* dc, int* points, int count, long color, int style, int line_width);
void iupdrvDrawPixelWDL(IdrawCanvas* dc, int x, int y, long color);
void iupdrvDrawRoundedRectangleWDL(IdrawCanvas* dc, int x1, int y1, int x2, int y2, int corner_radius, long color, int style, int line_width);
void iupdrvDrawTextWDL(IdrawCanvas* dc, const char* text, int len, int x, int y, int w, int h, long color, const char* font, int flags, double text_orientation);
void iupdrvDrawImageWDL(IdrawCanvas* dc, const char* name, int make_inactive, const char* bgcolor, int x, int y, int w, int h);
void iupdrvDrawSetClipRectWDL(IdrawCanvas* dc, int x1, int y1, int x2, int y2);
void iupdrvDrawSetClipRoundedRectWDL(IdrawCanvas* dc, int x1, int y1, int x2, int y2, int corner_radius);
void iupdrvDrawResetClipWDL(IdrawCanvas* dc);
void iupdrvDrawGetClipRectWDL(IdrawCanvas* dc, int *x1, int *y1, int *x2, int *y2);
void iupdrvDrawSelectRectWDL(IdrawCanvas* dc, int x1, int y1, int x2, int y2);
void iupdrvDrawFocusRectWDL(IdrawCanvas* dc, int x1, int y1, int x2, int y2);
void iupdrvDrawBezierWDL(IdrawCanvas* dc, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, long color, int style, int line_width);
void iupdrvDrawQuadraticBezierWDL(IdrawCanvas* dc, int x1, int y1, int x2, int y2, int x3, int y3, long color, int style, int line_width);
void iupdrvDrawLinearGradientWDL(IdrawCanvas* dc, int x1, int y1, int x2, int y2, float angle, long color1, long color2);
void iupdrvDrawRadialGradientWDL(IdrawCanvas* dc, int cx, int cy, int radius, long colorCenter, long colorEdge);

struct _IdrawCanvas{
  Ihandle* ih;
  int w, h;

  HWND hWnd;
  int release_dc;
  HBITMAP hBitmap, hOldBitmap;
  HDC hBitmapDC, hDC;

  int clip_x1, clip_y1, clip_x2, clip_y2;

  int sim_aa;

  IdrawCanvas* wdl_gc;

  /* GDI+ antialiasing cache */
  dummy_GpGraphics* cached_graphics;
};

void iupwinDrawInit(void)
{
  iupwinDrawThemeInit();

  if (iupwinDrawInitWDL())  /* if WDL is using Direct2D must manually initialize GDI+ */
    gdix_init();
}

void iupwinDrawFinish(void)
{
  if (iupwinDrawFinishWDL())  /* if WDL is using Direct2D must manually terminates GDI+ */
    gdix_fini();
}

IUP_SDK_API IdrawCanvas* iupdrvDrawCreateCanvas(Ihandle* ih)
{
  IdrawCanvas* dc = calloc(1, sizeof(IdrawCanvas));
  RECT rect;

  dc->ih = ih;

  /* Try WDL first (Direct2D with automatic GDI+ fallback), if that fails use GDI */
  dc->wdl_gc = iupdrvDrawCreateCanvasWDL(ih);
  if (dc->wdl_gc)
    return dc;

  /* WDL not available, fallback to GDI */
  dc->hWnd = (HWND)IupGetAttribute(ih, "HWND");  /* Use the attribute, so it can work with FileDlg preview area */

  /* valid only inside the ACTION callback of an IupCanvas */
  dc->hDC = (HDC)IupGetAttribute(ih, "HDC_WMPAINT");
  if (!dc->hDC)
  {
    dc->hDC = GetDC(dc->hWnd);
    dc->release_dc = 1;
  }

  GetClientRect(dc->hWnd, &rect);
  dc->w = rect.right - rect.left;
  dc->h = rect.bottom - rect.top;

  dc->hBitmap = CreateCompatibleBitmap(dc->hDC, dc->w, dc->h);
  dc->hBitmapDC = CreateCompatibleDC(dc->hDC);
  dc->hOldBitmap = SelectObject(dc->hBitmapDC, dc->hBitmap);

  SetBkMode(dc->hBitmapDC, TRANSPARENT);
  SetTextAlign(dc->hBitmapDC, TA_TOP|TA_LEFT);

  iupAttribSet(ih, "DRAWDRIVER", "GDI");
  dc->sim_aa = iupAttribGetInt(ih, "DRAWANTIALIAS");

  return dc;
}

IUP_SDK_API void iupdrvDrawKillCanvas(IdrawCanvas* dc)
{
  if (dc->wdl_gc)
  {
    iupdrvDrawKillCanvasWDL(dc->wdl_gc);

    memset(dc, 0, sizeof(IdrawCanvas));
    free(dc);
    return;
  }

  /* Cleanup GDI+ graphics cache */
  if (dc->cached_graphics)
    gdix_vtable->fn_DeleteGraphics(dc->cached_graphics);

  SelectObject(dc->hBitmapDC, dc->hOldBitmap);
  DeleteObject(dc->hBitmap);
  DeleteDC(dc->hBitmapDC);  /* to match CreateCompatibleDC */
  if (dc->release_dc)
    ReleaseDC(dc->hWnd, dc->hDC);  /* to match GetDC */

  free(dc);
}

IUP_SDK_API void iupdrvDrawUpdateSize(IdrawCanvas* dc)
{
  int w, h;
  RECT rect;

  if (dc->wdl_gc)
  {
    iupdrvDrawUpdateSizeWDL(dc->wdl_gc);
    return;
  }

  GetClientRect(dc->hWnd, &rect);
  w = rect.right - rect.left;
  h = rect.bottom - rect.top;

  if (w != dc->w || h != dc->h)
  {
    dc->w = w;
    dc->h = h;

    /* Invalidate GDI+ graphics cache (HDC changed) */
    if (dc->cached_graphics)
    {
      gdix_vtable->fn_DeleteGraphics(dc->cached_graphics);
      dc->cached_graphics = NULL;
    }

    SelectObject(dc->hBitmapDC, dc->hOldBitmap);
    DeleteObject(dc->hBitmap);
    DeleteDC(dc->hBitmapDC);  /* to match CreateCompatibleDC */

    dc->hBitmap = CreateCompatibleBitmap(dc->hDC, dc->w, dc->h);
    dc->hBitmapDC = CreateCompatibleDC(dc->hDC);
    dc->hOldBitmap = SelectObject(dc->hBitmapDC, dc->hBitmap);

    SetBkMode(dc->hBitmapDC, TRANSPARENT);
    SetTextAlign(dc->hBitmapDC, TA_TOP|TA_LEFT);
  }
}

IUP_SDK_API void iupdrvDrawFlush(IdrawCanvas* dc)
{
  if (dc->wdl_gc)
  {
    iupdrvDrawFlushWDL(dc->wdl_gc);
    return;
  }

  BitBlt(dc->hDC, 0, 0, dc->w, dc->h, dc->hBitmapDC, 0, 0, SRCCOPY);
}

IUP_SDK_API void iupdrvDrawGetSize(IdrawCanvas* dc, int *w, int *h)
{
  if (dc->wdl_gc)
  {
    iupdrvDrawGetSizeWDL(dc->wdl_gc, w, h);
    return;
  }

  if (w) *w = dc->w;
  if (h) *h = dc->h;
}

static HPEN iDrawCreatePen(long color, int style, int line_width)
{
  LOGBRUSH LogBrush;
  LogBrush.lbStyle = BS_SOLID;
  LogBrush.lbColor = RGB(iupDrawRed(color), iupDrawGreen(color), iupDrawBlue(color));
  LogBrush.lbHatch = 0;

  if (style == IUP_DRAW_STROKE || style == IUP_DRAW_FILL)
    return ExtCreatePen(PS_GEOMETRIC | PS_SOLID, line_width, &LogBrush, 0, NULL);
  else if (style == IUP_DRAW_STROKE_DASH)
  {
    /* for some reason, dashes and spaces in GDI must be changed -1 and +1 to be exactly what specified */
    DWORD dashes[2] = { 9-1, 3+1 };
    return ExtCreatePen(PS_GEOMETRIC | PS_USERSTYLE, line_width, &LogBrush, 2, dashes);
  }
  else if (style == IUP_DRAW_STROKE_DOT)
  {
    DWORD dashes[2] = { 1-1, 2+1 };
    return ExtCreatePen(PS_GEOMETRIC | PS_USERSTYLE, line_width, &LogBrush, 2, dashes);
  }
  else if (style == IUP_DRAW_STROKE_DASH_DOT)
  {
    DWORD dashes[4] = { 7-1, 3+1, 1-1, 3+1 };
    return ExtCreatePen(PS_GEOMETRIC | PS_USERSTYLE, line_width, &LogBrush, 4, dashes);
  }
  else if (style == IUP_DRAW_STROKE_DASH_DOT_DOT)
  {
    DWORD dashes[6] = { 7-1, 3+1, 1-1, 3+1, 1-1, 3+1 };
    return ExtCreatePen(PS_GEOMETRIC | PS_USERSTYLE, line_width, &LogBrush, 6, dashes);
  }
  return NULL;
}

static void iwinDrawSetLineStyleSimAA(int style, dummy_GpPen* pen)
{
  if (style != IUP_DRAW_STROKE)
  {
    float* dashes = NULL;
    int dashes_count = 0;

    if (style == IUP_DRAW_STROKE_DASH)
    {
      static float s_dashes[2] = { 9.0f, 3.0f };
      dashes = s_dashes;
      dashes_count = 2;
    }
    else if (style == IUP_DRAW_STROKE_DOT)
    {
      static float s_dashes[2] = { 1.0f, 2.0f };
      dashes = s_dashes;
      dashes_count = 2;
    }
    else if (style == IUP_DRAW_STROKE_DASH_DOT)
    {
      static float s_dashes[4] = { 7.0f, 3.0f, 1.0f, 3.0f };
      dashes = s_dashes;
      dashes_count = 4;
    }
    else if (style == IUP_DRAW_STROKE_DASH_DOT_DOT)
    {
      static float s_dashes[6] = { 7.0f, 3.0f, 1.0f, 3.0f, 1.0f, 3.0f };
      dashes = s_dashes;
      dashes_count = 6;
    }
    else
      return;

    gdix_vtable->fn_SetPenDashOffset(pen, 0.5f);
    gdix_vtable->fn_SetPenDashArray(pen, dashes, dashes_count);
    gdix_vtable->fn_SetPenDashStyle(pen, dummy_DashStyleCustom);
  }
}

static int colorHasAlpha(long color)
{
  return iupDrawAlpha(color) != 255;
}

/* Cached GDI+ graphics getter - reuses graphics object for antialiasing */
static dummy_GpGraphics* iGetCachedGraphicsSimAA(IdrawCanvas* dc)
{
  if (!dc->cached_graphics)
  {
    gdix_vtable->fn_CreateFromHDC(dc->hBitmapDC, &dc->cached_graphics);
    gdix_vtable->fn_SetSmoothingMode(dc->cached_graphics, dummy_SmoothingModeAntiAlias);
  }

  return dc->cached_graphics;
}

static void iwinDrawRectangleSimAA(IdrawCanvas* dc, int x1, int y1, int x2, int y2, long color, int style, int line_width)
{
  dummy_GpGraphics* graphics = iGetCachedGraphicsSimAA(dc);

  iupDrawCheckSwapCoord(x1, x2);
  iupDrawCheckSwapCoord(y1, y2);

  if (style == IUP_DRAW_FILL)
  {
    dummy_GpBrush* brush;
    gdix_vtable->fn_CreateSolidFill(iupColor2ARGB(color), &brush);

    gdix_vtable->fn_FillRectangle(graphics, brush, (float)(x1 - 0.5f), (float)(y1 - 0.5f), (float)(x2 - x1 + 1), (float)(y2 - y1 + 1));   /* in this case Size = Max - Min  */

    gdix_vtable->fn_DeleteBrush(brush);
  }
  else
  {
    dummy_GpPen* pen;
    gdix_vtable->fn_CreatePen1(iupColor2ARGB(color), (float)line_width, dummy_UnitPixel, &pen);
    iwinDrawSetLineStyleSimAA(style, pen);

    gdix_vtable->fn_DrawRectangle(graphics, pen, (float)x1, (float)y1, (float)(x2 - x1), (float)(y2 - y1));   /* in this case Size = Max - Min */

    gdix_vtable->fn_DeletePen(pen);
  }
}

IUP_SDK_API void iupdrvDrawRectangle(IdrawCanvas* dc, int x1, int y1, int x2, int y2, long color, int style, int line_width)
{
  if (dc->wdl_gc)
  {
    iupdrvDrawRectangleWDL(dc->wdl_gc, x1, y1, x2, y2, color, style, line_width);
    return;
  }

  if (dc->sim_aa && colorHasAlpha(color))
    iwinDrawRectangleSimAA(dc, x1, y1, x2, y2, color, style, line_width);
  else
  {
    SetDCBrushColor(dc->hBitmapDC, RGB(iupDrawRed(color), iupDrawGreen(color), iupDrawBlue(color)));

    if (style == IUP_DRAW_FILL)
    {
      RECT rect;
      iupDrawCheckSwapCoord(x1, x2);
      iupDrawCheckSwapCoord(y1, y2);
      SetRect(&rect, x1, y1, x2 + 1, y2 + 1);
      FillRect(dc->hBitmapDC, &rect, (HBRUSH)GetStockObject(DC_BRUSH));
    }
    else if (style == IUP_DRAW_STROKE && line_width == 1)
    {
      RECT rect;
      iupDrawCheckSwapCoord(x1, x2);
      iupDrawCheckSwapCoord(y1, y2);
      SetRect(&rect, x1, y1, x2 + 1, y2 + 1);
      FrameRect(dc->hBitmapDC, &rect, (HBRUSH)GetStockObject(DC_BRUSH));
    }
    else
    {
      POINT line_poly[5];
      HPEN hPen = iDrawCreatePen(color, style, line_width);
      HPEN hPenOld = SelectObject(dc->hBitmapDC, hPen);
      line_poly[0].x = x1;
      line_poly[0].y = y1;
      line_poly[1].x = x1;
      line_poly[1].y = y2;
      line_poly[2].x = x2;
      line_poly[2].y = y2;
      line_poly[3].x = x2;
      line_poly[3].y = y1;
      line_poly[4].x = x1;
      line_poly[4].y = y1;
      Polyline(dc->hBitmapDC, line_poly, 5);
      SelectObject(dc->hBitmapDC, hPenOld);
      DeleteObject(hPen);
    }
  }
}

static void iwinDrawLineSimAA(IdrawCanvas* dc, int x1, int y1, int x2, int y2, long color, int style, int line_width)
{
  dummy_GpGraphics* graphics = iGetCachedGraphicsSimAA(dc);
  dummy_GpPen* pen;

  gdix_vtable->fn_CreatePen1(iupColor2ARGB(color), (float)line_width, dummy_UnitPixel, &pen);
  iwinDrawSetLineStyleSimAA(style, pen);

  gdix_vtable->fn_DrawLine(graphics, pen, (float)x1, (float)y1, (float)x2, (float)y2);

  gdix_vtable->fn_DeletePen(pen);
}

IUP_SDK_API void iupdrvDrawLine(IdrawCanvas* dc, int x1, int y1, int x2, int y2, long color, int style, int line_width)
{
  if (dc->wdl_gc)
  {
    iupdrvDrawLineWDL(dc->wdl_gc, x1, y1, x2, y2, color, style, line_width);
    return;
  }

  if (dc->sim_aa && ((x1 != x2 && y1 != y2) || colorHasAlpha(color)))
    iwinDrawLineSimAA(dc, x1, y1, x2, y2, color, style, line_width);
  else
  {
    POINT line_poly[2];
    HPEN hPen = iDrawCreatePen(color, style, line_width);
    HPEN hPenOld = SelectObject(dc->hBitmapDC, hPen);

    line_poly[0].x = x1;
    line_poly[0].y = y1;
    line_poly[1].x = x2;
    line_poly[1].y = y2;
    Polyline(dc->hBitmapDC, line_poly, 2);
    SetPixelV(dc->hBitmapDC, x2, y2, RGB(iupDrawRed(color), iupDrawGreen(color), iupDrawBlue(color)));

    SelectObject(dc->hBitmapDC, hPenOld);
    DeleteObject(hPen);
  }
}

static int winDrawCalcArc(int c1, int c2, double a, int horiz)
{
  double proj;
  int pos;
  if (horiz)
    proj = cos(IUP_DEG2RAD * a);
  else
    proj = - sin(IUP_DEG2RAD * a);
  pos = (c2 + c1) / 2 + iupROUND((c2 - c1 + 1) * proj / 2.0);
  return pos;
}

static void iwinDrawArcSimAA(IdrawCanvas* dc, int x1, int y1, int x2, int y2, double a1, double a2, long color, int style, int line_width)
{
  float xc, yc, rx, ry, dx, dy;
  float baseAngle, sweepAngle;
  dummy_GpGraphics* graphics = iGetCachedGraphicsSimAA(dc);

  iupDrawCheckSwapCoord(x1, x2);
  iupDrawCheckSwapCoord(y1, y2);

  dx = (float)(x2 - x1);
  dy = (float)(y2 - y1);

  rx = dx / 2.0f;
  ry = dy / 2.0f;

  xc = (float)x1 + rx;
  yc = (float)y1 + ry;

  baseAngle = (float)(360.0 - a2);
  sweepAngle = (float)(a2 - a1);

  if (style == IUP_DRAW_FILL)
  {
    dummy_GpBrush* brush;
    gdix_vtable->fn_CreateSolidFill(iupColor2ARGB(color), &brush);

    if (sweepAngle == 360.0f)
      gdix_vtable->fn_FillEllipse(graphics, brush, xc - rx, yc - ry, dx, dy);
    else
      gdix_vtable->fn_FillPie(graphics, brush, xc - rx, yc - ry, dx, dy, baseAngle, sweepAngle);

    gdix_vtable->fn_DeleteBrush(brush);
  }
  else
  {
    dummy_GpPen* pen;
    gdix_vtable->fn_CreatePen1(iupColor2ARGB(color), (float)line_width, dummy_UnitPixel, &pen);
    iwinDrawSetLineStyleSimAA(style, pen);

    if (sweepAngle == 360.0f)
      gdix_vtable->fn_DrawEllipse(graphics, pen, xc - rx, yc - ry, dx, dy);
    else
      gdix_vtable->fn_DrawArc(graphics, pen, xc - rx, yc - ry, dx, dy, baseAngle, sweepAngle);

    gdix_vtable->fn_DeletePen(pen);
  }
}

IUP_SDK_API void iupdrvDrawArc(IdrawCanvas* dc, int x1, int y1, int x2, int y2, double a1, double a2, long color, int style, int line_width)
{
  int XStartArc, XEndArc, YStartArc, YEndArc;

  if (dc->wdl_gc)
  {
    iupdrvDrawArcWDL(dc->wdl_gc, x1, y1, x2, y2, a1, a2, color, style, line_width);
    return;
  }

  if (dc->sim_aa)
  {
    iwinDrawArcSimAA(dc, x1, y1, x2, y2, a1, a2, color, style, line_width);
    return;
  }

  iupDrawCheckSwapCoord(x1, x2);
  iupDrawCheckSwapCoord(y1, y2);

  XStartArc = winDrawCalcArc(x1, x2, a1, 1);
  XEndArc = winDrawCalcArc(x1, x2, a2, 1);
  YStartArc = winDrawCalcArc(y1, y2, a1, 0);
  YEndArc = winDrawCalcArc(y1, y2, a2, 0);

  if (style==IUP_DRAW_FILL)
  {
    HBRUSH hBrush = CreateSolidBrush(RGB(iupDrawRed(color),iupDrawGreen(color),iupDrawBlue(color)));
    HBRUSH hBrushOld = SelectObject(dc->hBitmapDC, hBrush);
    BeginPath(dc->hBitmapDC); 
    Pie(dc->hBitmapDC, x1, y1, x2 + 1, y2 + 1, XStartArc, YStartArc, XEndArc, YEndArc);
    EndPath(dc->hBitmapDC);
    FillPath(dc->hBitmapDC);
    SelectObject(dc->hBitmapDC, hBrushOld);
    DeleteObject(hBrush);
  }
  else
  {
    HPEN hPen = iDrawCreatePen(color, style, line_width);
    HPEN hPenOld = SelectObject(dc->hBitmapDC, hPen);
    Arc(dc->hBitmapDC, x1, y1, x2 + 1, y2 + 1, XStartArc, YStartArc, XEndArc, YEndArc);
    SelectObject(dc->hBitmapDC, hPenOld);
    DeleteObject(hPen);
  }
}

IUP_SDK_API void iupdrvDrawEllipse(IdrawCanvas* dc, int x1, int y1, int x2, int y2, long color, int style, int line_width)
{
  if (dc->wdl_gc)
  {
    iupdrvDrawEllipseWDL(dc->wdl_gc, x1, y1, x2, y2, color, style, line_width);
    return;
  }

  iupDrawCheckSwapCoord(x1, x2);
  iupDrawCheckSwapCoord(y1, y2);

  if (style == IUP_DRAW_FILL)
  {
    HBRUSH hBrush = CreateSolidBrush(RGB(iupDrawRed(color), iupDrawGreen(color), iupDrawBlue(color)));
    HBRUSH hBrushOld = SelectObject(dc->hBitmapDC, hBrush);
    HPEN hPen = CreatePen(PS_NULL, 0, 0);
    HPEN hPenOld = SelectObject(dc->hBitmapDC, hPen);
    Ellipse(dc->hBitmapDC, x1, y1, x2 + 1, y2 + 1);
    SelectObject(dc->hBitmapDC, hPenOld);
    SelectObject(dc->hBitmapDC, hBrushOld);
    DeleteObject(hPen);
    DeleteObject(hBrush);
  }
  else
  {
    HPEN hPen = iDrawCreatePen(color, style, line_width);
    HPEN hPenOld = SelectObject(dc->hBitmapDC, hPen);
    HBRUSH hBrush = (HBRUSH)GetStockObject(NULL_BRUSH);
    HBRUSH hBrushOld = SelectObject(dc->hBitmapDC, hBrush);
    Ellipse(dc->hBitmapDC, x1, y1, x2 + 1, y2 + 1);
    SelectObject(dc->hBitmapDC, hBrushOld);
    SelectObject(dc->hBitmapDC, hPenOld);
    DeleteObject(hPen);
  }
}

static void iwinDrawPolygonSimAA(IdrawCanvas* dc, int* points, int count, long color, int style, int line_width)
{
  int i;
  dummy_GpGraphics* graphics = iGetCachedGraphicsSimAA(dc);
  dummy_GpPath* path;

  gdix_vtable->fn_CreatePath(dummy_FillModeAlternate, &path);

  for (i = 0; i < count - 1; i++)
  {
    int j1 = 2 * i;
    int j2 = 2 * (i + 1);
    gdix_vtable->fn_AddPathLine(path, (float)points[j1], (float)points[j1 + 1], (float)points[j2], (float)points[j2 + 1]);
  }

  if (style == IUP_DRAW_FILL)
  {
    dummy_GpBrush* brush;
    gdix_vtable->fn_CreateSolidFill(iupColor2ARGB(color), &brush);

    gdix_vtable->fn_FillPath(graphics, brush, path);

    gdix_vtable->fn_DeleteBrush(brush);
  }
  else
  {
    dummy_GpPen* pen;
    gdix_vtable->fn_CreatePen1(iupColor2ARGB(color), (float)line_width, dummy_UnitPixel, &pen);
    iwinDrawSetLineStyleSimAA(style, pen);

    gdix_vtable->fn_DrawPath(graphics, pen, path);

    gdix_vtable->fn_DeletePen(pen);
  }

  gdix_vtable->fn_DeletePath(path);
}

IUP_SDK_API void iupdrvDrawPolygon(IdrawCanvas* dc, int* points, int count, long color, int style, int line_width)
{
  if (dc->wdl_gc)
  {
    iupdrvDrawPolygonWDL(dc->wdl_gc, points, count, color, style, line_width);
    return;
  }

  if (dc->sim_aa)
  {
    iwinDrawPolygonSimAA(dc, points, count, color, style, line_width);
    return;
  }

  if (style == IUP_DRAW_FILL)
  {
    HBRUSH hBrush = CreateSolidBrush(RGB(iupDrawRed(color),iupDrawGreen(color),iupDrawBlue(color)));
    HBRUSH hBrushOld = SelectObject(dc->hBitmapDC, hBrush);
    BeginPath(dc->hBitmapDC);
    Polygon(dc->hBitmapDC, (POINT*)points, count);
    EndPath(dc->hBitmapDC);
    FillPath(dc->hBitmapDC);
    SelectObject(dc->hBitmapDC, hBrushOld);
    DeleteObject(hBrush);
  }
  else
  {
    /* For stroked polygons, need to close the path by adding first point at the end */
    POINT* closed_points;
    int use_heap = 0;
    POINT stack_points[256];

    if (count + 1 <= 256)
      closed_points = stack_points;
    else
    {
      closed_points = (POINT*)malloc((count + 1) * sizeof(POINT));
      use_heap = 1;
    }

    /* Copy all points */
    memcpy(closed_points, points, count * sizeof(POINT));
    /* Add first point at the end to close the polygon */
    closed_points[count] = closed_points[0];

    HPEN hPen = iDrawCreatePen(color, style, line_width);
    HPEN hPenOld = SelectObject(dc->hBitmapDC, hPen);
    Polyline(dc->hBitmapDC, closed_points, count + 1);
    SelectObject(dc->hBitmapDC, hPenOld);
    DeleteObject(hPen);

    if (use_heap)
      free(closed_points);
  }
}

IUP_SDK_API void iupdrvDrawPixel(IdrawCanvas* dc, int x, int y, long color)
{
  if (dc->wdl_gc)
  {
    iupdrvDrawPixelWDL(dc->wdl_gc, x, y, color);
    return;
  }

  /* Basic GDI fallback - just set pixel */
  SetPixel(dc->hBitmapDC, x, y, RGB(iupDrawRed(color), iupDrawGreen(color), iupDrawBlue(color)));
}

IUP_SDK_API void iupdrvDrawRoundedRectangle(IdrawCanvas* dc, int x1, int y1, int x2, int y2, int corner_radius, long color, int style, int line_width)
{
  if (dc->wdl_gc)
  {
    iupdrvDrawRoundedRectangleWDL(dc->wdl_gc, x1, y1, x2, y2, corner_radius, color, style, line_width);
    return;
  }

  /* Basic GDI fallback - use RoundRect */
  iupDrawCheckSwapCoord(x1, x2);
  iupDrawCheckSwapCoord(y1, y2);

  if (style == IUP_DRAW_FILL)
  {
    HBRUSH hBrush = CreateSolidBrush(RGB(iupDrawRed(color), iupDrawGreen(color), iupDrawBlue(color)));
    HBRUSH hBrushOld = SelectObject(dc->hBitmapDC, hBrush);
    HPEN hPen = CreatePen(PS_NULL, 0, 0);
    HPEN hPenOld = SelectObject(dc->hBitmapDC, hPen);
    RoundRect(dc->hBitmapDC, x1, y1, x2 + 1, y2 + 1, corner_radius * 2, corner_radius * 2);
    SelectObject(dc->hBitmapDC, hPenOld);
    SelectObject(dc->hBitmapDC, hBrushOld);
    DeleteObject(hPen);
    DeleteObject(hBrush);
  }
  else
  {
    HPEN hPen = iDrawCreatePen(color, style, line_width);
    HPEN hPenOld = SelectObject(dc->hBitmapDC, hPen);
    HBRUSH hBrush = (HBRUSH)GetStockObject(NULL_BRUSH);
    HBRUSH hBrushOld = SelectObject(dc->hBitmapDC, hBrush);
    RoundRect(dc->hBitmapDC, x1, y1, x2 + 1, y2 + 1, corner_radius * 2, corner_radius * 2);
    SelectObject(dc->hBitmapDC, hBrushOld);
    SelectObject(dc->hBitmapDC, hPenOld);
    DeleteObject(hPen);
  }
}

IUP_SDK_API void iupdrvDrawBezier(IdrawCanvas* dc, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, long color, int style, int line_width)
{
  if (dc->wdl_gc)
  {
    iupdrvDrawBezierWDL(dc->wdl_gc, x1, y1, x2, y2, x3, y3, x4, y4, color, style, line_width);
    return;
  }

  /* GDI fallback using PolyBezier */
  if (style == IUP_DRAW_FILL)
  {
    /* GDI PolyBezier doesn't support fill - use polygon approximation for filled Bezier */
    POINT points[21]; /* 20 segments should give smooth curve */
    int i, num_segments = 20;
    HBRUSH hBrush = CreateSolidBrush(RGB(iupDrawRed(color), iupDrawGreen(color), iupDrawBlue(color)));
    HBRUSH hBrushOld = SelectObject(dc->hBitmapDC, hBrush);
    HPEN hPen = CreatePen(PS_NULL, 0, 0);
    HPEN hPenOld = SelectObject(dc->hBitmapDC, hPen);

    /* Generate points along Bezier curve using parametric equation */
    for (i = 0; i <= num_segments; i++)
    {
      double t = (double)i / num_segments;
      double t1 = 1.0 - t;
      double t1_3 = t1 * t1 * t1;
      double t1_2_t = 3.0 * t1 * t1 * t;
      double t1_t_2 = 3.0 * t1 * t * t;
      double t_3 = t * t * t;

      points[i].x = (LONG)(t1_3 * x1 + t1_2_t * x2 + t1_t_2 * x3 + t_3 * x4);
      points[i].y = (LONG)(t1_3 * y1 + t1_2_t * y2 + t1_t_2 * y3 + t_3 * y4);
    }

    Polygon(dc->hBitmapDC, points, num_segments + 1);
    SelectObject(dc->hBitmapDC, hPenOld);
    SelectObject(dc->hBitmapDC, hBrushOld);
    DeleteObject(hPen);
    DeleteObject(hBrush);
  }
  else
  {
    /* Use native GDI PolyBezier for stroke */
    POINT points[4];
    HPEN hPen = iDrawCreatePen(color, style, line_width);
    HPEN hPenOld = SelectObject(dc->hBitmapDC, hPen);

    points[0].x = x1;
    points[0].y = y1;
    points[1].x = x2;
    points[1].y = y2;
    points[2].x = x3;
    points[2].y = y3;
    points[3].x = x4;
    points[3].y = y4;

    PolyBezier(dc->hBitmapDC, points, 4);

    SelectObject(dc->hBitmapDC, hPenOld);
    DeleteObject(hPen);
  }
}

IUP_SDK_API void iupdrvDrawQuadraticBezier(IdrawCanvas* dc, int x1, int y1, int x2, int y2, int x3, int y3, long color, int style, int line_width)
{
  /* Convert quadratic Bezier to cubic Bezier using the 2/3 formula:
   * Given quadratic: Q(t) with control points q0, q1, q2
   * Convert to cubic: C(t) with control points c0, c1, c2, c3
   *
   * c0 = q0                        (start point)
   * c1 = q0 + (2/3) * (q1 - q0)   (first control point)
   * c2 = q2 + (2/3) * (q1 - q2)   (second control point)
   * c3 = q2                        (end point)
   */
  int cx1, cy1, cx2, cy2;

  /* Calculate cubic control points from quadratic */
  cx1 = x1 + ((2 * (x2 - x1)) / 3);
  cy1 = y1 + ((2 * (y2 - y1)) / 3);
  cx2 = x3 + ((2 * (x2 - x3)) / 3);
  cy2 = y3 + ((2 * (y2 - y3)) / 3);

  /* Draw as cubic Bezier */
  iupdrvDrawBezier(dc, x1, y1, cx1, cy1, cx2, cy2, x3, y3, color, style, line_width);
}

IUP_SDK_API void iupdrvDrawGetClipRect(IdrawCanvas* dc, int *x1, int *y1, int *x2, int *y2)
{
  if (dc->wdl_gc)
  {
    iupdrvDrawGetClipRectWDL(dc->wdl_gc, x1, y1, x2, y2);
    return;
  }

  if (x1) *x1 = dc->clip_x1;
  if (y1) *y1 = dc->clip_y1;
  if (x2) *x2 = dc->clip_x2;
  if (y2) *y2 = dc->clip_y2;
}

IUP_SDK_API void iupdrvDrawResetClip(IdrawCanvas* dc)
{
  if (dc->wdl_gc)
  {
    iupdrvDrawResetClipWDL(dc->wdl_gc);
    return;
  }

  SelectClipRgn(dc->hBitmapDC, NULL);

  dc->clip_x1 = 0;
  dc->clip_y1 = 0;
  dc->clip_x2 = 0;
  dc->clip_y2 = 0;
}

IUP_SDK_API void iupdrvDrawSetClipRect(IdrawCanvas* dc, int x1, int y1, int x2, int y2)
{
  HRGN clip_hrgn;

  if (dc->wdl_gc)
  {
    iupdrvDrawSetClipRectWDL(dc->wdl_gc, x1, y1, x2, y2);
    return;
  }

  if (x1 == 0 && y1 == 0 && x2 == 0 && y2 == 0)
  {
    iupdrvDrawResetClip(dc);
    return;
  }

  /* make it an empty region */
  if (x1 >= x2) x1 = x2;
  if (y1 >= y2) y1 = y2;

  clip_hrgn = CreateRectRgn(x1, y1, x2 + 1, y2 + 1);
  SelectClipRgn(dc->hBitmapDC, clip_hrgn);
  DeleteObject(clip_hrgn);

  dc->clip_x1 = x1;
  dc->clip_y1 = y1;
  dc->clip_x2 = x2;
  dc->clip_y2 = y2;
}

IUP_SDK_API void iupdrvDrawSetClipRoundedRect(IdrawCanvas* dc, int x1, int y1, int x2, int y2, int corner_radius)
{
  HRGN clip_hrgn;

  if (dc->wdl_gc)
  {
    iupdrvDrawSetClipRoundedRectWDL(dc->wdl_gc, x1, y1, x2, y2, corner_radius);
    return;
  }

  if (x1 == 0 && y1 == 0 && x2 == 0 && y2 == 0)
  {
    iupdrvDrawResetClip(dc);
    return;
  }

  /* make it an empty region */
  iupDrawCheckSwapCoord(x1, x2);
  iupDrawCheckSwapCoord(y1, y2);

  /* Clamp radius to prevent oversized corners */
  int max_radius = ((x2 - x1) < (y2 - y1)) ? (x2 - x1) / 2 : (y2 - y1) / 2;
  if (corner_radius > max_radius)
    corner_radius = max_radius;

  /* Create rounded rectangle region for clipping */
  clip_hrgn = CreateRoundRectRgn(x1, y1, x2 + 1, y2 + 1, corner_radius * 2, corner_radius * 2);
  SelectClipRgn(dc->hBitmapDC, clip_hrgn);
  DeleteObject(clip_hrgn);

  dc->clip_x1 = x1;
  dc->clip_y1 = y1;
  dc->clip_x2 = x2;
  dc->clip_y2 = y2;
}

static void gdiRotateWorld(HDC hDC, int xc, int yc, double angle)
{
  XFORM xForm;

  SetGraphicsMode(hDC, GM_ADVANCED);
  ModifyWorldTransform(hDC, NULL, MWT_IDENTITY);

  xForm.eM11 = (FLOAT)cos(angle);
  xForm.eM12 = (FLOAT)sin(angle);
  xForm.eM21 = (FLOAT)-xForm.eM12;
  xForm.eM22 = (FLOAT)xForm.eM11;
  xForm.eDx = (FLOAT)xc;
  xForm.eDy = (FLOAT)yc;
  ModifyWorldTransform(hDC, &xForm, MWT_LEFTMULTIPLY);

  xForm.eM11 = (FLOAT)1;
  xForm.eM12 = (FLOAT)0;
  xForm.eM21 = (FLOAT)0;
  xForm.eM22 = (FLOAT)1;
  xForm.eDx = (FLOAT)-xc;
  xForm.eDy = (FLOAT)-yc;
  ModifyWorldTransform(hDC, &xForm, MWT_LEFTMULTIPLY);
}

static void gdiTranslateWorld(HDC hDC, int x, int y)
{
  XFORM xForm;
  xForm.eM11 = (FLOAT)1;
  xForm.eM12 = (FLOAT)0;
  xForm.eM21 = (FLOAT)0;
  xForm.eM22 = (FLOAT)1;
  xForm.eDx = (FLOAT)x;
  xForm.eDy = (FLOAT)y;
  ModifyWorldTransform(hDC, &xForm, MWT_RIGHTMULTIPLY);
}

static void gdiResetWorld(HDC hDC)
{
  ModifyWorldTransform(hDC, NULL, MWT_IDENTITY);
  SetGraphicsMode(hDC, GM_COMPATIBLE);
}

IUP_SDK_API void iupdrvDrawText(IdrawCanvas* dc, const char* text, int len, int x, int y, int w, int h, long color, const char* font, int flags, double text_orientation)
{
  if (dc->wdl_gc)
    iupdrvDrawTextWDL(dc->wdl_gc, text, len, x, y, w, h, color, font, flags, text_orientation);
  else
  {
    RECT rect;
    TCHAR* wtext;
    UINT uFormat = 0;
    int layout_w = w, layout_h = h;
    int layout_center = flags & IUP_DRAW_LAYOUTCENTER;

    HFONT hOldFont, hFont = (HFONT)iupwinGetHFont(font);
    SetTextColor(dc->hBitmapDC, RGB(iupDrawRed(color), iupDrawGreen(color), iupDrawBlue(color)));
    hOldFont = SelectObject(dc->hBitmapDC, hFont);

    if (text_orientation && layout_center)
      iupDrawGetTextSize(dc->ih, text, len, &layout_w, &layout_h, 0);

    rect.left = x;
    rect.right = x + layout_w;
    rect.top = y;
    rect.bottom = y + layout_h;

    wtext = iupwinStrToSystemLen(text, &len);

    uFormat |= DT_LEFT;
    if (flags & IUP_DRAW_RIGHT)
      uFormat = DT_RIGHT;
    else if (flags & IUP_DRAW_CENTER)
      uFormat = DT_CENTER;

    if (flags & IUP_DRAW_WRAP)
      uFormat |= DT_WORDBREAK;
    else if (flags & IUP_DRAW_ELLIPSIS)
      uFormat |= DT_END_ELLIPSIS;

    if (!(flags & IUP_DRAW_CLIP))
      uFormat |= DT_NOCLIP;

    if (text_orientation)
    {
      if (layout_center)
      {
        gdiRotateWorld(dc->hBitmapDC, x + layout_w / 2, y + layout_h / 2, -IUP_DEG2RAD*text_orientation);  /* counterclockwise */
        gdiTranslateWorld(dc->hBitmapDC, (w - layout_w) / 2, (h - layout_h) / 2);  /* append the transform */
      }
      else
        gdiRotateWorld(dc->hBitmapDC, x, y, -IUP_DEG2RAD*text_orientation);  /* counterclockwise */
    }

    DrawText(dc->hBitmapDC, wtext, len, &rect, uFormat);

    /* restore settings */
    SelectObject(dc->hBitmapDC, hOldFont);
    if (text_orientation)
      gdiResetWorld(dc->hBitmapDC);
  }
}

IUP_SDK_API void iupdrvDrawImage(IdrawCanvas* dc, const char* name, int make_inactive, const char* bgcolor, int x, int y, int w, int h)
{
  int bpp, img_w, img_h;
  HBITMAP hBitmap;

  if (dc->wdl_gc)
  {
    iupdrvDrawImageWDL(dc->wdl_gc, name, make_inactive, bgcolor, x, y, w, h);
    return;
  }

  hBitmap = (HBITMAP)iupImageGetImage(name, dc->ih, make_inactive, bgcolor);
  if (!hBitmap)
    return;

  /* must use this info, since image can be a driver image loaded from resources */
  iupdrvImageGetInfo(hBitmap, &img_w, &img_h, &bpp);

  if (w == -1 || w == 0) w = img_w;
  if (h == -1 || h == 0) h = img_h;

  iupwinDrawBitmap(dc->hBitmapDC, hBitmap, x, y, w, h, img_w, img_h, bpp);
}

IUP_SDK_API void iupdrvDrawSelectRect(IdrawCanvas* dc, int x1, int y1, int x2, int y2)
{
  if (dc->wdl_gc)
  {
    iupdrvDrawSelectRectWDL(dc->wdl_gc, x1, y1, x2, y2);
    return;
  }

  iupDrawCheckSwapCoord(x1, x2);
  iupDrawCheckSwapCoord(y1, y2);

  BitBlt(dc->hBitmapDC, x1, y1, x2 - x1 + 1, y2 - y1 + 1, dc->hBitmapDC, x1, y1, DSTINVERT);
}

IUP_SDK_API void iupdrvDrawFocusRect(IdrawCanvas* dc, int x1, int y1, int x2, int y2)
{
  RECT rect;

  if (dc->wdl_gc)
  {
    iupdrvDrawFocusRectWDL(dc->wdl_gc, x1, y1, x2, y2);
    return;
  }

  iupDrawCheckSwapCoord(x1, x2);
  iupDrawCheckSwapCoord(y1, y2);

  rect.left = x1;  
  rect.right = x2 + 1;
  rect.top = y1;
  rect.bottom = y2 + 1;

  /* for some reason text color affects focus drawing */
  SetTextColor(dc->hBitmapDC, RGB(0, 0, 0));

  DrawFocusRect(dc->hBitmapDC, &rect);
}

static long gdiInterpolateColor(long color1, long color2, float t)
{
  unsigned char r1 = iupDrawRed(color1), g1 = iupDrawGreen(color1), b1 = iupDrawBlue(color1), a1 = iupDrawAlpha(color1);
  unsigned char r2 = iupDrawRed(color2), g2 = iupDrawGreen(color2), b2 = iupDrawBlue(color2), a2 = iupDrawAlpha(color2);
  unsigned char r = (unsigned char)(r1 + t * (r2 - r1));
  unsigned char g = (unsigned char)(g1 + t * (g2 - g1));
  unsigned char b = (unsigned char)(b1 + t * (b2 - b1));
  unsigned char a = (unsigned char)(a1 + t * (a2 - a1));
  return iupDrawColor(r, g, b, a);
}

IUP_SDK_API void iupdrvDrawLinearGradient(IdrawCanvas* dc, int x1, int y1, int x2, int y2, float angle, long color1, long color2)
{
  if (dc->wdl_gc)
  {
    iupdrvDrawLinearGradientWDL(dc->wdl_gc, x1, y1, x2, y2, angle, color1, color2);
    return;
  }

  /* GDI fallback - draw as series of filled rectangles */
  {
    int i, steps;
    float t, dx, dy, length;
    int px1, py1, px2, py2;
    HBRUSH hBrush;
    RECT rect;

    iupDrawCheckSwapCoord(x1, x2);
    iupDrawCheckSwapCoord(y1, y2);

    /* Calculate gradient direction */
    float rad = angle * 3.14159265359f / 180.0f;
    dx = (float)cos(rad);
    dy = (float)sin(rad);

    /* Number of steps for smooth gradient */
    length = (float)sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
    steps = (int)length;
    if (steps < 2) steps = 2;
    if (steps > 256) steps = 256;

    /* Draw gradient strips */
    for (i = 0; i < steps; i++)
    {
      t = (float)i / (float)(steps - 1);
      long color = gdiInterpolateColor(color1, color2, t);
      hBrush = CreateSolidBrush(RGB(iupDrawRed(color), iupDrawGreen(color), iupDrawBlue(color)));

      /* Calculate strip position */
      if (fabs(dx) > fabs(dy))  /* More horizontal */
      {
        px1 = x1 + (int)(t * (x2 - x1));
        px2 = x1 + (int)((t + 1.0f / steps) * (x2 - x1));
        py1 = y1;
        py2 = y2;
      }
      else  /* More vertical */
      {
        px1 = x1;
        px2 = x2;
        py1 = y1 + (int)(t * (y2 - y1));
        py2 = y1 + (int)((t + 1.0f / steps) * (y2 - y1));
      }

      rect.left = px1;
      rect.top = py1;
      rect.right = px2 + 1;
      rect.bottom = py2 + 1;
      FillRect(dc->hBitmapDC, &rect, hBrush);
      DeleteObject(hBrush);
    }
  }
}

IUP_SDK_API void iupdrvDrawRadialGradient(IdrawCanvas* dc, int cx, int cy, int radius, long colorCenter, long colorEdge)
{
  if (dc->wdl_gc)
  {
    iupdrvDrawRadialGradientWDL(dc->wdl_gc, cx, cy, radius, colorCenter, colorEdge);
    return;
  }

  /* GDI fallback - draw as series of concentric circles */
  {
    int i, steps;
    float t, r;
    HBRUSH hBrush;
    HPEN hPen;

    /* Number of steps for smooth gradient */
    steps = radius;
    if (steps < 2) steps = 2;
    if (steps > 256) steps = 256;

    /* Draw from outside to inside */
    for (i = steps - 1; i >= 0; i--)
    {
      t = (float)i / (float)(steps - 1);
      long color = gdiInterpolateColor(colorCenter, colorEdge, t);
      r = (float)radius * t;

      hBrush = CreateSolidBrush(RGB(iupDrawRed(color), iupDrawGreen(color), iupDrawBlue(color)));
      hPen = CreatePen(PS_SOLID, 1, RGB(iupDrawRed(color), iupDrawGreen(color), iupDrawBlue(color)));
      SelectObject(dc->hBitmapDC, hBrush);
      SelectObject(dc->hBitmapDC, hPen);

      Ellipse(dc->hBitmapDC, (int)(cx - r), (int)(cy - r), (int)(cx + r + 1), (int)(cy + r + 1));

      DeleteObject(hBrush);
      DeleteObject(hPen);
    }
  }
}
