/*
  win3.c

  Paul Casteels
  09-May-1994
  casteels@uia.ac.be

  A driver for Windows 3.x

*/
#ifdef WIN3

#include <stdio.h>
#include <string.h>
#include "plplotP.h"
#include "drivers.h"
#include <windows.h>
//#define NDEBUG
#include <assert.h>
#include "plplot.rc"

#define TEXT_MODE 0
#define GRAPHICS_MODE 1


#define CLEAN 0
#define DIRTY 1

const float xRes = 8192,yRes = 8192;
const int niceCnt = 20;
const char szPlPlotClass[] = "PlplotClass";
const char szPlPlotWName[] = "PlPlot Window";
const char aboutText[] = "        Plplot    "PLPLOT_VERSION
								"\nComments/Questions to\n"
								"plplot-list@dino.ph.utexas.edu\n";

LRESULT CALLBACK _export PlPlotWndProc (HWND hwnd,UINT message,
  UINT wParam,LONG lParam);

typedef struct {
  HWND hwnd;
  HMENU hMenu;
  HPEN hPen;
  HDC hdc;
  float xScale,yScale;
  int nextPlot;	// set to 1 by Nextplot menu
  int nice;			// be nice for windows multitasking
  int rePaint;		// if the background is cleared we need a repaint
  int rePaintBsy;	// if we are repainting block the rest
						// plRemakePlot is not reentrant (in Windows)?
  int newCursor;
  float cursorX,cursorY;
} WinDev;

MSG msg;
long colors[16]={
  RGB(0,0,0),		// 0 = black
  RGB(0,0,255),	// 1 = blue
  RGB(0,255,0),	// 2 = green
  RGB(0,255,255),	// 3 = cyan
  RGB(255,0,0),	// 4 = red
  RGB(255,0,255),	// 5 = magenta
  RGB(255,255,0),	// 6 = yellow
  RGB(255,255,255),	// 7 = white
  RGB(0,0,0),
  RGB(0,0,0),
  RGB(0,0,0),
  RGB(0,0,0),
  RGB(0,0,0),
  RGB(0,0,0),
  RGB(0,0,0),
  RGB(0,0,0)};

// Transfer control to windows
void checkMessage() {
  if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
	 TranslateMessage(&msg);
	 DispatchMessage(&msg);
  }
}

// Simplest way to connect to a printer
HDC GetPrinterDC () {
  char szPrinter[80];
  char *szDevice,*szDriver,*szOutput;
  GetProfileString("windows","device",",,,",szPrinter,80);
  if (NULL != (szDevice = strtok(szPrinter,"," )) &&
		NULL != (szDriver = strtok(NULL,     ", ")) &&
		NULL != (szOutput = strtok(NULL,     ", ")))
	 return CreateDC(szDriver,szDevice,szOutput,NULL);
  return 0;
}

/*----------------------------------------------------------------------*\
* Initialize device.
\*----------------------------------------------------------------------*/

void
plD_init_win3(PLStream *pls)
{
	 HWND   hwndMain;
	 WNDCLASS wndclass;
	 HANDLE hInstance;

	 WinDev *dev;
	 int xPos,yPos;
	 int nWidth,nHeight;

	 pls->termin = 1;			// is an interactive terminal
	 pls->icol0 = 1;			// current color
	 pls->width = 1;			// current pen width
	 pls->bytecnt = 0;
	 pls->page = 0;
	 pls->plbuf_write = 1;	// buffer the output
	 pls->dev_flush = 1;		// flush as we like

/* Set up device parameters */
	 if (pls->dev != NULL)
		delete pls->dev;
	 pls->dev = new WinDev;
	 assert(pls->dev != NULL);
	 dev = (WinDev *) pls->dev;
	 dev->nextPlot = 0;
	 dev->nice = 0;
	 dev->hPen = CreatePen(PS_SOLID,0,colors[0]);

// Get/save this from plplot.ini ??
	 xPos = 100;
	 yPos = 100;
	 nWidth = 600;
	 nHeight = 400;

	 hwndMain = GetActiveWindow();
	 hInstance = GetWindowWord(hwndMain,GWW_HINSTANCE);
	 dev->hMenu = LoadMenu(hInstance,"COMMANDS");

	 wndclass.style = CS_HREDRAW | CS_VREDRAW;
	 wndclass.lpfnWndProc = ::PlPlotWndProc;
	 wndclass.cbClsExtra = 0;
	 wndclass.cbWndExtra = sizeof(pls);
	 wndclass.hInstance = hInstance;
	 wndclass.hIcon = LoadIcon(hInstance,"PLICON");
	 wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
	 wndclass.hbrBackground = GetStockObject(WHITE_BRUSH);
	 wndclass.lpszMenuName = NULL;
	 wndclass.lpszClassName = szPlPlotClass;
	 RegisterClass (&wndclass);

	 dev->hwnd = CreateWindow(szPlPlotClass,szPlPlotWName,
		WS_OVERLAPPEDWINDOW,
		xPos,yPos,nWidth,nHeight,
		NULL,dev->hMenu,
		hInstance,NULL);

	 SetWindowLong(dev->hwnd,0,(long)pls);

	 ShowWindow(dev->hwnd,SW_SHOW);

	 plP_setpxl(2.5,2.5);           /* Pixels/mm. */
	 plP_setphy(0,xRes,0,yRes);
}

/*----------------------------------------------------------------------*\
* line()
*
* Draw a line in the current color from (x1,y1) to (x2,y2).
\*----------------------------------------------------------------------*/

void
plD_line_win3(PLStream *pls, short x1a, short y1a, short x2a, short y2a)
{
  WinDev *dev = (WinDev *)pls->dev;
  SelectObject(dev->hdc,dev->hPen);
  MoveTo(dev->hdc,x1a * dev->xScale,(yRes - y1a) * dev->yScale);
  LineTo(dev->hdc,x2a * dev->xScale,(yRes - y2a) * dev->yScale);
  if (!dev->rePaintBsy)
	 if (dev->nice++ > niceCnt) {
		dev->nice = 0;
		checkMessage();
	 }
}

/*----------------------------------------------------------------------*\
* bgi_polyline()
*
* Draw a polyline in the current color.
\*----------------------------------------------------------------------*/

void
plD_polyline_win3(PLStream *pls, short *xa, short *ya, PLINT npts)
{
  WinDev *dev = (WinDev *)pls->dev;
  SelectObject(dev->hdc,dev->hPen);
  MoveTo(dev->hdc,xa[0] * dev->xScale,(yRes - ya[0]) * dev->yScale);
  for (int i=1;i<npts;i++) {
	 LineTo(dev->hdc,xa[i] * dev->xScale,(yRes - ya[i]) * dev->yScale);
  }
  if (!dev->rePaintBsy) {
	 dev->nice = 0;
	 checkMessage();
  }
}

/*----------------------------------------------------------------------*\
* bgi_eop()
*
* End of page.
\*----------------------------------------------------------------------*/

void
plD_eop_win3(PLStream *pls)
{
  WinDev *dev = (WinDev *)pls->dev;

  ReleaseDC(dev->hwnd,dev->hdc);
  EnableMenuItem(dev->hMenu,CM_PRINTPLOT,MF_ENABLED);
  EnableMenuItem(dev->hMenu,CM_NEXTPLOT,MF_ENABLED);
  while (!dev->nextPlot) {
	 GetMessage(&msg,NULL,0,0);
	 TranslateMessage(&msg);
	 DispatchMessage(&msg);
  }
  InvalidateRect(dev->hwnd,NULL,TRUE);
  UpdateWindow(dev->hwnd);
  dev->nextPlot = 0;
}

/*----------------------------------------------------------------------*\
* bop()
*
* Set up for the next page.
* Advance to next family file if necessary (file output).
\*----------------------------------------------------------------------*/

void
plD_bop_win3(PLStream *pls)
{
  WinDev *dev = (WinDev *)pls->dev;
  RECT rect;

  EnableMenuItem(dev->hMenu,CM_PRINTPLOT,MF_GRAYED);
  EnableMenuItem(dev->hMenu,CM_NEXTPLOT,MF_GRAYED);

  dev->hdc = GetDC(dev->hwnd);
  GetClientRect(dev->hwnd,&rect);
  dev->xScale = rect.right / (xRes + 1);
  dev->yScale = rect.bottom / (yRes + 1);

  dev->rePaint = 0;
  dev->rePaintBsy = 0;
  dev->nice = 0;
  pls->page++;
}

/*----------------------------------------------------------------------*\
* bgi_tidy()
*
* Close graphics file or otherwise clean up.
\*----------------------------------------------------------------------*/

void
plD_tidy_win3(PLStream *pls)
{
  WinDev *dev = (WinDev *)pls->dev;

  pls->page = 0;
  pls->OutFile = NULL;
  DestroyWindow(dev->hwnd);
}

void plD_state_win3(PLStream *pls,PLINT op) {
  WinDev *dev = (WinDev *)pls->dev;

  switch(op) {
  case PLSTATE_WIDTH:
	 break;
  case PLSTATE_COLOR0:
	 DeleteObject(dev->hPen);
	 dev->hPen = CreatePen(PS_SOLID,0,colors[(int)pls->icol0]);
	 break;
  }
}


/*----------------------------------------------------------------------*\
* bgi_esc()
*
* Escape function.
\*----------------------------------------------------------------------*/

void
plD_esc_win3(PLStream *pls, PLINT op , void *ptr)
{
  WinDev *dev = (WinDev *)pls->dev;
  HCURSOR holdcursor,hnewcursor;

  if (op == PLESC_GETC) {
	 hnewcursor = LoadCursor(NULL,IDC_CROSS);
	 holdcursor = GetClassWord(GetActiveWindow(),GCW_HCURSOR);
	 SetCursor(hnewcursor);
	 SetClassWord(GetActiveWindow(),
					  GCW_HCURSOR,
					  hnewcursor);
	 dev->newCursor = 0;
	 while (!dev->newCursor)
		checkMessage();
	 ((PLCursor *)ptr)->vpX = dev->cursorX;
	 ((PLCursor *)ptr)->vpY = dev->cursorY;
	 SetClassWord(GetActiveWindow(),
					  GCW_HCURSOR,
					  holdcursor);
  }
}

LRESULT CALLBACK _export PlPlotWndProc (HWND hwnd,UINT message,
  UINT wParam,LONG lParam)
{
  RECT rect;
  PAINTSTRUCT ps;
  PLStream *pls = (PLStream *)GetWindowLong(hwnd,0);
  WinDev *dev = NULL;
  HCURSOR hcurSave;

  if (pls)
	 dev = (WinDev *)pls->dev;

  switch (message) {
	 case WM_RBUTTONDOWN :
		dev->newCursor = 1;
		dev->cursorX = LOWORD(lParam) / dev->xScale;
		dev->cursorY = yRes - HIWORD(lParam) / dev->yScale;
		return 0;
	 case WM_ERASEBKGND :
		if (!dev->rePaintBsy)
		  dev->rePaint = 1;
		break;
	 case WM_PAINT :
		if (dev) {
		  if (dev->rePaint) {
			 dev->rePaint = 0;
			 dev->rePaintBsy = 1;
			 hcurSave = SetCursor(LoadCursor(NULL,IDC_WAIT));
			 dev->hdc = GetDC(dev->hwnd);
			 GetClientRect(dev->hwnd,&rect);
			 dev->xScale = rect.right / (xRes + 1);
			 dev->yScale = rect.bottom / (yRes + 1);
			 plRemakePlot(pls);
			 dev->rePaintBsy = 0;
			 SetCursor(hcurSave);
			 ReleaseDC(dev->hwnd,dev->hdc);
		  }
		  BeginPaint(hwnd,&ps);
		  EndPaint(hwnd,&ps);
		  return 0;
		}
		break;
	 case WM_DESTROY :
//		PostQuitMessage(0);
		return 0;
	 case WM_COMMAND :
		switch (wParam) {
		  case  CM_NEXTPLOT :
			 if (dev)
				dev->nextPlot = 1;
			 return 0;
		  case CM_PRINTPLOT :
			 dev->rePaintBsy = 1;
			 dev->hdc = GetPrinterDC();
			 dev->xScale = GetDeviceCaps(dev->hdc,HORZRES) / (xRes + 1);
			 dev->yScale = GetDeviceCaps(dev->hdc,VERTRES) / (yRes + 1);
			 Escape(dev->hdc,STARTDOC,0,NULL,NULL);
			 hcurSave = SetCursor(LoadCursor(NULL,IDC_WAIT));
			 plRemakePlot(pls);
			 Escape(dev->hdc,NEWFRAME,0,NULL,NULL);
			 Escape(dev->hdc,ENDDOC,0,NULL,NULL);
			 SetCursor(hcurSave);
			 dev->rePaintBsy = 0;
			 DeleteDC(dev->hdc);
			 return 0;
		  case CM_ABOUT :
			 MessageBox(hwnd,aboutText,"About",MB_OK);
			 return 0;
		}
  }
  return DefWindowProc(hwnd,message,wParam,lParam);
}
#else
pldummy_win3() {
  return 0;
}
#endif //WIN3
