/******************************************************************************
*
*
* Notepad2
*
* Print.cpp
*   Scintilla Printing Functionality
*   Mostly taken from SciTE, (c) Neil Hodgson, http://www.scintilla.org
*
* See Readme.txt for more information about this source code.
* Please send me your comments to this work.
*
* Distributed under the terms of the GNU General Public License,
* see License.txt for details.
*
*                                              (c) Florian Balmer 1996-2004
*                                                       textview@bluewin.ch
*                                               http://www.flos-freeware.ch
*
*
******************************************************************************/
#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <string.h>
#include "platform.h"
#include "scintilla.h"
#include "scilexer.h"
extern "C" {
#include "dialogs.h"
#include "helpers.h"
}
#include "resource.h"


extern "C" HINSTANCE g_hInstance;


// Global settings...
extern "C" int iPrintHeader;
extern "C" int iPrintFooter;
extern "C" int iPrintZoom;
extern "C" RECT pagesetupMargin;


// Stored objects...
HGLOBAL hDevMode = NULL;
HGLOBAL hDevNames = NULL;


//=============================================================================
//
//  EditPrint() - Code from SciTE
//
extern "C" HWND hwndStatus;

void StatusUpdatePrintPage(int iPageNum)
{
  char tch[32];

  FormatString(tch,COUNTOF(tch),IDS_PRINTFILE,iPageNum);

  StatusSetText(hwndStatus,255,tch);
  StatusSetSimple(hwndStatus,TRUE);

  InvalidateRect(hwndStatus,NULL,TRUE);
  UpdateWindow(hwndStatus);
}


extern "C" BOOL EditPrint(HWND hwnd,LPCSTR pszDocTitle,LPCSTR pszPageFormat)
{

  // Don't print empty documents
  if (SendMessage(hwnd,SCI_GETLENGTH,0,0) == 0) {
    MsgBox(MBINFO,IDS_PRINT_EMPTY);
    return TRUE; }

  int startPos;
  int endPos;

  HDC hdc;

  RECT rectMargins;
  RECT rectPhysMargins;
  RECT rectSetup;
  POINT ptPage;
  POINT ptDpi;

  //RECT rectSetup;

  TEXTMETRIC tm;

  int headerLineHeight;
  HFONT fontHeader;

  int footerLineHeight;
  HFONT fontFooter;

  char dateString[256];

  DOCINFO di = {sizeof(DOCINFO), 0, 0, 0, 0};

  LONG lengthDoc;
  LONG lengthDocMax;
  LONG lengthPrinted;

  struct RangeToFormat frPrint;

  int pageNum;
  BOOL printPage;

  char pageString[32];

  HPEN pen;
  HPEN penOld;

  PRINTDLG pdlg = {
                      sizeof(PRINTDLG), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
                  };
  pdlg.hwndOwner = GetParent(hwnd);
  pdlg.hInstance = g_hInstance;
  pdlg.Flags = PD_USEDEVMODECOPIES | PD_ALLPAGES | PD_RETURNDC;
  pdlg.nFromPage = 1;
  pdlg.nToPage = 1;
  pdlg.nMinPage = 1;
  pdlg.nMaxPage = 0xffffU;
  pdlg.nCopies = 1;
  pdlg.hDC = 0;
  pdlg.hDevMode = hDevMode;
  pdlg.hDevNames = hDevNames;

  startPos = SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0);;
  endPos = SendMessage(hwnd,SCI_GETSELECTIONEND,0,0);

  if (startPos == endPos) {
    pdlg.Flags |= PD_NOSELECTION;
  } else {
    pdlg.Flags |= PD_SELECTION;
  }
  if (0) {
    // Don't display dialog box, just use the default printer and options
    pdlg.Flags |= PD_RETURNDEFAULT;
  }
  if (!PrintDlg(&pdlg)) {
    return TRUE; // False means error...
  }

  hDevMode = pdlg.hDevMode;
  hDevNames = pdlg.hDevNames;

  hdc = pdlg.hDC;

  // Get printer resolution
  ptDpi.x = GetDeviceCaps(hdc, LOGPIXELSX);    // dpi in X direction
  ptDpi.y = GetDeviceCaps(hdc, LOGPIXELSY);    // dpi in Y direction

  // Start by getting the physical page size (in device units).
  ptPage.x = GetDeviceCaps(hdc, PHYSICALWIDTH);   // device units
  ptPage.y = GetDeviceCaps(hdc, PHYSICALHEIGHT);  // device units

  // Get the dimensions of the unprintable
  // part of the page (in device units).
  rectPhysMargins.left = GetDeviceCaps(hdc, PHYSICALOFFSETX);
  rectPhysMargins.top = GetDeviceCaps(hdc, PHYSICALOFFSETY);

  // To get the right and lower unprintable area,
  // we take the entire width and height of the paper and
  // subtract everything else.
  rectPhysMargins.right = ptPage.x            // total paper width
                          - GetDeviceCaps(hdc, HORZRES) // printable width
                          - rectPhysMargins.left;        // left unprintable margin

  rectPhysMargins.bottom = ptPage.y            // total paper height
                           - GetDeviceCaps(hdc, VERTRES)  // printable height
                           - rectPhysMargins.top;        // right unprintable margin

  // At this point, rectPhysMargins contains the widths of the
  // unprintable regions on all four sides of the page in device units.

  // Take in account the page setup given by the user (if one value is not null)
  if (pagesetupMargin.left != 0 || pagesetupMargin.right != 0 ||
          pagesetupMargin.top != 0 || pagesetupMargin.bottom != 0) {

    // Convert the hundredths of millimeters (HiMetric) or
    // thousandths of inches (HiEnglish) margin values
    // from the Page Setup dialog to device units.
    // (There are 2540 hundredths of a mm in an inch.)

    char localeInfo[3];
    GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, localeInfo, 3);

    if (localeInfo[0] == '0') {  // Metric system. '1' is US System
      rectSetup.left = MulDiv (pagesetupMargin.left, ptDpi.x, 2540);
      rectSetup.top = MulDiv (pagesetupMargin.top, ptDpi.y, 2540);
      rectSetup.right  = MulDiv(pagesetupMargin.right, ptDpi.x, 2540);
      rectSetup.bottom  = MulDiv(pagesetupMargin.bottom, ptDpi.y, 2540);
    } else {
      rectSetup.left  = MulDiv(pagesetupMargin.left, ptDpi.x, 1000);
      rectSetup.top  = MulDiv(pagesetupMargin.top, ptDpi.y, 1000);
      rectSetup.right  = MulDiv(pagesetupMargin.right, ptDpi.x, 1000);
      rectSetup.bottom  = MulDiv(pagesetupMargin.bottom, ptDpi.y, 1000);
    }

    // Dont reduce margins below the minimum printable area
    rectMargins.left  = max(rectPhysMargins.left, rectSetup.left);
    rectMargins.top  = max(rectPhysMargins.top, rectSetup.top);
    rectMargins.right  = max(rectPhysMargins.right, rectSetup.right);
    rectMargins.bottom  = max(rectPhysMargins.bottom, rectSetup.bottom);
  } else {
    rectMargins.left  = rectPhysMargins.left;
    rectMargins.top  = rectPhysMargins.top;
    rectMargins.right  = rectPhysMargins.right;
    rectMargins.bottom  = rectPhysMargins.bottom;
  }

  // rectMargins now contains the values used to shrink the printable
  // area of the page.

  // Convert device coordinates into logical coordinates
  DPtoLP(hdc, (LPPOINT)&rectMargins, 2);
  DPtoLP(hdc, (LPPOINT)&rectPhysMargins, 2);

  // Convert page size to logical units and we're done!
  DPtoLP(hdc, (LPPOINT) &ptPage, 1);

  headerLineHeight = MulDiv(10,ptDpi.y, 72);
  fontHeader = CreateFont(headerLineHeight,
                          0, 0, 0,
                          FW_BOLD,
                          0,
                          0,
                          0, 0, 0,
                          0, 0, 0,
                          "Arial");
  SelectObject(hdc, fontHeader);
  GetTextMetrics(hdc, &tm);
  headerLineHeight = tm.tmHeight + tm.tmExternalLeading;

  footerLineHeight = MulDiv(9,ptDpi.y, 72);
  fontFooter = CreateFont(footerLineHeight,
                          0, 0, 0,
                          FW_NORMAL,
                          0,
                          0,
                          0, 0, 0,
                          0, 0, 0,
                          "Arial");
  SelectObject(hdc, fontFooter);
  GetTextMetrics(hdc, &tm);
  footerLineHeight = tm.tmHeight + tm.tmExternalLeading;

  di.lpszDocName = pszDocTitle;
  di.lpszOutput = 0;
  di.lpszDatatype = 0;
  di.fwType = 0;
  if (StartDoc(hdc, &di) < 0) {
    return FALSE;
  }

  // Get current date...
  SYSTEMTIME st;
  GetLocalTime(&st);
  GetDateFormat(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&st,NULL,dateString,256);

  // Get current time...
  if (iPrintHeader == 0)
  {
    char timeString[128];
    GetTimeFormat(LOCALE_USER_DEFAULT,TIME_NOSECONDS,&st,NULL,timeString,128);
    lstrcat(dateString," ");
    lstrcat(dateString,timeString);
  }

  // Set print zoom...
  SendMessage(hwnd,SCI_SETPRINTMAGNIFICATION,(WPARAM)iPrintZoom,0);

  lengthDoc = SendMessage(hwnd,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;
  }

  // We must substract the physical margins from the printable area
  frPrint.hdc = hdc;
  frPrint.hdcTarget = hdc;
  frPrint.rc.left = rectMargins.left - rectPhysMargins.left;
  frPrint.rc.top = rectMargins.top - rectPhysMargins.top;
  frPrint.rc.right = ptPage.x - rectMargins.right - rectPhysMargins.left;
  frPrint.rc.bottom = ptPage.y - rectMargins.bottom - rectPhysMargins.top;
  frPrint.rcPage.left = 0;
  frPrint.rcPage.top = 0;
  frPrint.rcPage.right = ptPage.x - rectPhysMargins.left - rectPhysMargins.right - 1;
  frPrint.rcPage.bottom = ptPage.y - rectPhysMargins.top - rectPhysMargins.bottom - 1;
  frPrint.rc.top += headerLineHeight + headerLineHeight / 2;
  frPrint.rc.bottom -= footerLineHeight + footerLineHeight / 2;
  // Print each page
  pageNum = 1;

  //PropSet propsPrint;
  //propsPrint.superPS = &props;
  //SetFileProperties(propsPrint);

  while (lengthPrinted < lengthDoc) {
    printPage = (!(pdlg.Flags & PD_PAGENUMS) ||
                 (pageNum >= pdlg.nFromPage) && (pageNum <= pdlg.nToPage));

    wsprintf(pageString, pszPageFormat, pageNum);
    //propsPrint.Set("CurrentPage", pageString);

    if (printPage) {

      // Show wait cursor...
      SendMessage(hwnd,SCI_SETCURSOR,SC_CURSORWAIT,0);

      // Display current page number in Statusbar
      StatusUpdatePrintPage(pageNum);

      StartPage(hdc);

      SetTextColor(hdc, RGB(0,0,0));
      SetBkColor(hdc, RGB(255,255,255));
      SelectObject(hdc, fontHeader);
      UINT ta = SetTextAlign(hdc, TA_BOTTOM);
      RECT rcw = {frPrint.rc.left, frPrint.rc.top - headerLineHeight - headerLineHeight / 2,
                  frPrint.rc.right, frPrint.rc.top - headerLineHeight / 2};
      rcw.bottom = rcw.top + headerLineHeight;

      if (iPrintHeader < 3)
      {
        ExtTextOut(hdc, frPrint.rc.left + 5, frPrint.rc.top - headerLineHeight / 2,
                      /*ETO_OPAQUE*/0, &rcw, pszDocTitle,
                      lstrlen(pszDocTitle), NULL);
      }

      // Print date in header
      if (iPrintHeader == 0 || iPrintHeader == 1)
      {
        SIZE sizeInfo;
        SelectObject(hdc,fontFooter);
        GetTextExtentPoint32(hdc,dateString,lstrlen(dateString),&sizeInfo);
        ExtTextOut(hdc, frPrint.rc.right - 5 - sizeInfo.cx, frPrint.rc.top - headerLineHeight / 2,
                      /*ETO_OPAQUE*/0, &rcw, dateString,
                      lstrlen(dateString), NULL);
      }

      SetTextAlign(hdc, ta);
      pen = CreatePen(0, 1, RGB(0,0,0));
      penOld = (HPEN)SelectObject(hdc, pen);
      MoveToEx(hdc, frPrint.rc.left, frPrint.rc.top - headerLineHeight / 4, NULL);
      LineTo(hdc, frPrint.rc.right, frPrint.rc.top - headerLineHeight / 4);
      SelectObject(hdc, penOld);
      DeleteObject(pen);
    }

    frPrint.chrg.cpMin = lengthPrinted;
    frPrint.chrg.cpMax = lengthDoc;

    lengthPrinted = SendMessage(hwnd,SCI_FORMATRANGE,printPage,(LPARAM)&frPrint);

    if (printPage) {
      SetTextColor(hdc, RGB(0,0,0));
      SetBkColor(hdc, RGB(255,255,255));
      SelectObject(hdc, fontFooter);
      UINT ta = SetTextAlign(hdc, TA_TOP);
      RECT rcw = {frPrint.rc.left, frPrint.rc.bottom + footerLineHeight / 2,
                  frPrint.rc.right, frPrint.rc.bottom + footerLineHeight + footerLineHeight / 2};

      if (iPrintFooter == 0)
      {
        SIZE sizeFooter;
        GetTextExtentPoint32(hdc,pageString,lstrlen(pageString),&sizeFooter);
        ExtTextOut(hdc, frPrint.rc.right - 5 - sizeFooter.cx, frPrint.rc.bottom + footerLineHeight / 2,
                      /*ETO_OPAQUE*/0, &rcw, pageString,
                      lstrlen(pageString), NULL);
      }

      SetTextAlign(hdc, ta);
      pen = ::CreatePen(0, 1, RGB(0,0,0));
      penOld = (HPEN)SelectObject(hdc, pen);
      SetBkColor(hdc, RGB(0,0,0));
      MoveToEx(hdc, frPrint.rc.left, frPrint.rc.bottom + footerLineHeight / 4, NULL);
      LineTo(hdc, frPrint.rc.right, frPrint.rc.bottom + footerLineHeight / 4);
      SelectObject(hdc, penOld);
      DeleteObject(pen);

      EndPage(hdc);
    }
    pageNum++;

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

  SendMessage(hwnd,SCI_FORMATRANGE, FALSE, 0);

  EndDoc(hdc);
  DeleteDC(hdc);
  if (fontHeader) {
    DeleteObject(fontHeader);
  }
  if (fontFooter) {
    DeleteObject(fontFooter);
  }

  // Reset Statusbar to default mode
  StatusSetSimple(hwndStatus,FALSE);

  // Remove wait cursor...
  SendMessage(hwnd,SCI_SETCURSOR,SC_CURSORNORMAL,0);

  return TRUE;
}


//=============================================================================
//
//  EditPrintSetup() - Code from SciTE
//
//  Custom controls: 30 Zoom
//                   31 Spin
//                   32 Header
//                   33 Footer
//
extern "C" UINT_PTR CALLBACK PageSetupHook(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
  switch (uiMsg)
  {
    case WM_INITDIALOG:
      {
        char tch[512];
        char *p1,*p2;

        SendDlgItemMessage(hwnd,30,EM_LIMITTEXT,32,0);

        SendDlgItemMessage(hwnd,31,UDM_SETRANGE,0,MAKELONG((short)20,(short)-10));
        SendDlgItemMessage(hwnd,31,UDM_SETPOS,0,MAKELONG((short)iPrintZoom,0));

        // Set header options
        GetString(IDS_PRINT_HEADER,tch,COUNTOF(tch));
        lstrcat(tch,"|");
        p1 = tch;
        while (p2 = strchr(p1,'|')) {
          *p2++ = '\0';
          if (*p1)
            SendDlgItemMessage(hwnd,32,CB_ADDSTRING,0,(LPARAM)p1);
          p1 = p2; }
        SendDlgItemMessage(hwnd,32,CB_SETCURSEL,(WPARAM)iPrintHeader,0);

        // Set footer options
        GetString(IDS_PRINT_FOOTER,tch,COUNTOF(tch));
        lstrcat(tch,"|");
        p1 = tch;
        while (p2 = strchr(p1,'|')) {
          *p2++ = '\0';
          if (*p1)
            SendDlgItemMessage(hwnd,33,CB_ADDSTRING,0,(LPARAM)p1);
          p1 = p2; }
        SendDlgItemMessage(hwnd,33,CB_SETCURSEL,(WPARAM)iPrintFooter,0);

        // Make combos handier
        SendDlgItemMessage(hwnd,32,CB_SETEXTENDEDUI,TRUE,0);
        SendDlgItemMessage(hwnd,33,CB_SETEXTENDEDUI,TRUE,0);
        SendDlgItemMessage(hwnd,1137,CB_SETEXTENDEDUI,TRUE,0);
        SendDlgItemMessage(hwnd,1138,CB_SETEXTENDEDUI,TRUE,0);
      }
      break;

    case WM_COMMAND:
      if (LOWORD(wParam) == IDOK)
      {
        LONG lPos = SendDlgItemMessage(hwnd,31,UDM_GETPOS,0,0);
        if (HIWORD(lPos) == 0)
          iPrintZoom = (int)(short)LOWORD(lPos);
        else
          iPrintZoom = 0;

        iPrintHeader = SendDlgItemMessage(hwnd,32,CB_GETCURSEL,0,0);
        iPrintFooter = SendDlgItemMessage(hwnd,33,CB_GETCURSEL,0,0);
      }
      break;

    default:
      break;
  }
  return(0);
}


extern "C" void EditPrintSetup(HWND hwnd)
{
  PAGESETUPDLG pdlg = {
                          sizeof(PAGESETUPDLG), 0, 0, 0, 0, {0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0
                      };

  pdlg.Flags = PSD_ENABLEPAGESETUPHOOK | PSD_ENABLEPAGESETUPTEMPLATE;
  pdlg.lpfnPageSetupHook = PageSetupHook;
  pdlg.lpPageSetupTemplateName = MAKEINTRESOURCE(IDD_PAGESETUP);

  pdlg.hwndOwner = GetParent(hwnd);
  pdlg.hInstance = g_hInstance;

  if (pagesetupMargin.left != 0 || pagesetupMargin.right != 0 ||
          pagesetupMargin.top != 0 || pagesetupMargin.bottom != 0) {
    pdlg.Flags |= PSD_MARGINS;

    pdlg.rtMargin.left = pagesetupMargin.left;
    pdlg.rtMargin.top = pagesetupMargin.top;
    pdlg.rtMargin.right = pagesetupMargin.right;
    pdlg.rtMargin.bottom = pagesetupMargin.bottom;
  }

  pdlg.hDevMode = hDevMode;
  pdlg.hDevNames = hDevNames;

  if (!PageSetupDlg(&pdlg))
    return;

  pagesetupMargin.left = pdlg.rtMargin.left;
  pagesetupMargin.top = pdlg.rtMargin.top;
  pagesetupMargin.right = pdlg.rtMargin.right;
  pagesetupMargin.bottom = pdlg.rtMargin.bottom;

  hDevMode = pdlg.hDevMode;
  hDevNames = pdlg.hDevNames;
}


//=============================================================================
//
//  EditPrintInit() - Setup default page margin if no values from registry
//
extern "C" void EditPrintInit()
{
  if (pagesetupMargin.left == 0 || pagesetupMargin.top == 0 ||
      pagesetupMargin.right == 0 || pagesetupMargin.bottom == 0)
  {
    char localeInfo[3];
    GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, localeInfo, 3);

    if (localeInfo[0] == '0') {  // Metric system. '1' is US System
      pagesetupMargin.left = 2000;
      pagesetupMargin.top = 2000;
      pagesetupMargin.right = 2000;
      pagesetupMargin.bottom = 2000; }

    else {
      pagesetupMargin.left = 1000;
      pagesetupMargin.top = 1000;
      pagesetupMargin.right = 1000;
      pagesetupMargin.bottom = 1000; }
  }
}


// End of Print.cpp
