/*
* IupScintilla component
*
* Description : A source code editing component, 
* derived from Scintilla (http://www.scintilla.org/)
*/

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

#include "Scintilla.h"
#include "SciLexer.h"

#include <windows.h>

#include "iup.h"
#include "iup_scintilla.h"
#include "iupcbs.h"
#include "iup_key.h"

#include "iup_class.h"
#include "iup_object.h"
#include "iup_attrib.h"
#include "iup_str.h"
#include "iup_drv.h"
#include "iup_drvfont.h"
#include "iup_register.h"
#include "iup_layout.h"
#include "iup_assert.h"

#include "iupwin_drv.h"
#include "iupwin_str.h"

#include "iupsci.h"


#define WM_IUPCARET WM_APP+1   /* Custom IUP message */

void iupdrvScintillaRefreshCaret(Ihandle* ih)
{
  PostMessage(ih->handle, WM_IUPCARET, 0, 0L);
}

int iupdrvScintillaGetBorder(void)
{
  int border_size = 2 * 3;
  return border_size;
}

void iupdrvScintillaOpen(void)
{
  Scintilla_RegisterClasses(IupGetGlobal("HINSTANCE"));
}

sptr_t IupScintillaSendMessage(Ihandle* ih, unsigned int iMessage, uptr_t wParam, sptr_t lParam)
{
  return SendMessage(ih->handle, iMessage, wParam, lParam);
}

static int winScintillaWmNotify(Ihandle* ih, NMHDR* msg_info, int *result)
{
  SCNotification *pMsg = (SCNotification*)msg_info;

  iupScintillaNotify(ih, pMsg);

  (void)result;
  return 0; /* result not used */
}

static int winScintillaMsgProc(Ihandle* ih, UINT msg, WPARAM wp, LPARAM lp, LRESULT *result)
{
  if (msg==WM_KEYDOWN) /* process K_ANY before text callbacks */
  {
    int ret = iupwinBaseMsgProc(ih, msg, wp, lp, result);
    if (ret) 
    {
      if (iupObjectCheck(ih))
        iupAttribSet(ih, "_IUPWIN_IGNORE_CHAR", "1");
      *result = 0;
      return 1;
    }
    else
    {
      if (iupObjectCheck(ih))
        iupAttribSet(ih, "_IUPWIN_IGNORE_CHAR", NULL);
    }
  }

  switch (msg)
  {
  case WM_CHAR:
    {
      /* even aborting WM_KEYDOWN, a WM_CHAR will be sent, so ignore it also */
      /* if a dialog was shown, the loop will be processed, so ignore out of focus WM_CHAR messages */
      if (GetFocus() != ih->handle || iupAttribGet(ih, "_IUPWIN_IGNORE_CHAR"))
      {
        iupAttribSet(ih, "_IUPWIN_IGNORE_CHAR", NULL);
        *result = 0;
        return 1;
      }

      break;
    }
  case WM_KEYDOWN:
    {
      PostMessage(ih->handle, WM_IUPCARET, 0, 0L);
      return 0;  /* already processed at the beginning of this function */
    }
  case WM_KEYUP:
    {
      PostMessage(ih->handle, WM_IUPCARET, 0, 0L);
      break;
    }
  case WM_LBUTTONDBLCLK:
  case WM_MBUTTONDBLCLK:
  case WM_RBUTTONDBLCLK:
  case WM_LBUTTONDOWN:
  case WM_MBUTTONDOWN:
  case WM_RBUTTONDOWN:
    {
      iupwinFlagButtonDown(ih, msg);

      if (iupwinButtonDown(ih, msg, wp, lp)==-1)
      {
        *result = 0;
        return 1;
      }
      PostMessage(ih->handle, WM_IUPCARET, 0, 0L);
      break;
    }
  case WM_MBUTTONUP:
  case WM_RBUTTONUP:
  case WM_LBUTTONUP:
    {
      if (!iupwinFlagButtonUp(ih, msg))
      {
        *result = 0;
        return 1;
      }

      if (iupwinButtonUp(ih, msg, wp, lp)==-1)
      {
        *result = 0;
        return 1;
      }
      PostMessage(ih->handle, WM_IUPCARET, 0, 0L);
      break;
    }
  case WM_IUPCARET:
    {
      iupScintillaCallCaretCb(ih);
      break;
    }
  case WM_MOUSEMOVE:
    {
      iupwinMouseMove(ih, msg, wp, lp);
      break;
    }
  }

  return iupwinBaseMsgProc(ih, msg, wp, lp, result);
}

int iupdrvScintillaPrintAttrib(Ihandle* ih, const char* value)
{
  PRINTDLG pdlg;
  DOCINFO di;
  int startPos = 0, endPos = 0;
  HDC hdc;
  struct Sci_RangeToFormat frPrint;
  int margin_left, margin_top, margin_right, margin_bottom, margin_units,
      page_width, page_height;
  int p, printPage;
  double dpi;
  LONG lengthDoc, lengthDocMax, lengthPrinted;

  ZeroMemory(&pdlg, sizeof(PRINTDLG));
  pdlg.lStructSize = sizeof(PRINTDLG);
  pdlg.hwndOwner = IupGetDialog(ih)->handle;
  pdlg.hInstance = (HINSTANCE)IupGetGlobal("HINSTANCE");
  pdlg.Flags = PD_USEDEVMODECOPIES | PD_ALLPAGES | PD_RETURNDC;
  pdlg.nCopies = 1;
  pdlg.nFromPage = 1;
  pdlg.nToPage = 1;
  pdlg.nMinPage = 1;
  pdlg.nMaxPage = 0xffffU; /* We do not know how many pages in the */
                           /* document until the printer is selected and the paper size is known. */

  /* See if a range has been selected */
  IupGetIntInt(ih, "SELECTIONPOS", &startPos, &endPos);

  if (startPos == endPos)
    pdlg.Flags |= PD_NOSELECTION;
  else
    pdlg.Flags |= PD_SELECTION;

  if (!iupAttribGetBoolean(ih, "PRINTDIALOG")) 
  {
    /* Don't display dialog box, just use the default printer and options */
    pdlg.Flags |= PD_RETURNDEFAULT;
  }

  if (!PrintDlg(&pdlg)) 
  {
    if (pdlg.hDevMode)
      GlobalFree(pdlg.hDevMode);
    if (pdlg.hDevNames)
      GlobalFree(pdlg.hDevNames);
    return 0;
  }

  hdc = pdlg.hDC;

  page_width = GetDeviceCaps(hdc, HORZRES);    /* physically printable pixels */
  page_height = GetDeviceCaps(hdc, VERTRES);
  dpi = (double)GetDeviceCaps(hdc, LOGPIXELSY);

  di.cbSize = sizeof(DOCINFO);
  di.lpszDocName = iupwinStrToSystem(value);
  di.lpszOutput = 0;
  di.lpszDatatype = 0;
  di.fwType = 0;

  if (StartDoc(hdc, &di) < 0) 
  {
    DeleteDC(hdc);  /* Use DeleteDC for printer */
    if (pdlg.hDevMode)
      GlobalFree(pdlg.hDevMode);
    if (pdlg.hDevNames)
      GlobalFree(pdlg.hDevNames);
    return 0;
  }

  lengthDoc = (LONG)IupScintillaSendMessage(ih, SCI_GETLENGTH, 0, 0);
  lengthDocMax = lengthDoc;
  lengthPrinted = 0;

  /* Requested to print selection */
  if (pdlg.Flags & PD_SELECTION) 
  {
    if (startPos > endPos) 
    {
      lengthPrinted = endPos;
      lengthDoc = startPos;
    }
    else 
    {
      lengthPrinted = startPos;
      lengthDoc = endPos;
    }

    if (lengthPrinted < 0)
      lengthPrinted = 0;
    if (lengthDoc > lengthDocMax)
      lengthDoc = lengthDocMax;
  }

  margin_units = iupSciGetPrintMarginUnits(ih);
  margin_left = iupSciGetPrintMargin(ih, "PRINTMARGINLEFT", margin_units, dpi);
  margin_top = iupSciGetPrintMargin(ih, "PRINTMARGINTOP", margin_units, dpi);
  margin_right = iupSciGetPrintMargin(ih, "PRINTMARGINRIGHT", margin_units, dpi);
  margin_bottom = iupSciGetPrintMargin(ih, "PRINTMARGINBOTTOM", margin_units, dpi);

  frPrint.hdc = hdc;
  frPrint.hdcTarget = hdc;
  frPrint.rc.left = margin_left;
  frPrint.rc.top = margin_top;
  frPrint.rc.right = page_width - margin_right;
  frPrint.rc.bottom = page_height - margin_bottom;
  frPrint.rcPage.left = 0;
  frPrint.rcPage.top = 0;
  frPrint.rcPage.right = page_width;
  frPrint.rcPage.bottom = page_height;

  /* Print each page */
  p = 1;  /* starts at 1 to match page selection */
  while (lengthPrinted < lengthDoc) 
  {
    printPage = (!(pdlg.Flags & PD_PAGENUMS) ||
                      ((p >= pdlg.nFromPage) && (p <= pdlg.nToPage)));

    if (printPage)
      StartPage(hdc);

    frPrint.chrg.cpMin = lengthPrinted;
    frPrint.chrg.cpMax = lengthDoc;
    lengthPrinted = (LONG)IupScintillaSendMessage(ih, SCI_FORMATRANGE, printPage, (sptr_t)&frPrint);
    p++;

    if (printPage)
      EndPage(hdc);

    if ((pdlg.Flags & PD_PAGENUMS) && (p > pdlg.nToPage))
      break;
  }

  IupScintillaSendMessage(ih, SCI_FORMATRANGE, FALSE, 0);

  EndDoc(hdc);
  DeleteDC(hdc);  /* Use DeleteDC for printer */

  if (pdlg.hDevMode)
    GlobalFree(pdlg.hDevMode);
  if (pdlg.hDevNames)
    GlobalFree(pdlg.hDevNames);

  return 0;
}


/*****************************************************************************/

int idrvScintillaMap(Ihandle* ih)
{
  DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
  DWORD dwExStyle = 0;

  if (!ih->parent)
    return IUP_ERROR;

  if (iupAttribGetBoolean(ih, "CANFOCUS"))
    dwStyle |= WS_TABSTOP;

  if (iupAttribGetBoolean(ih, "BORDER"))
    dwExStyle |= WS_EX_CLIENTEDGE;
  
  if (!iupwinCreateWindow(ih, TEXT("Scintilla"), dwExStyle, dwStyle, NULL))
    return IUP_ERROR;

  /* Process Scintilla Notifications */
  IupSetCallback(ih, "_IUPWIN_NOTIFY_CB", (Icallback)winScintillaWmNotify);

  /* Process BUTTON_CB, MOTION_CB and CARET_CB */
  IupSetCallback(ih, "_IUPWIN_CTRLMSGPROC_CB", (Icallback)winScintillaMsgProc);

  return IUP_NOERROR;
}

void iupdrvScintillaReleaseMethod(Iclass* ic)
{
  (void)ic;
  Scintilla_ReleaseResources();
}
