// C++ code Copyright (C) David R. Evans G4AMJ/NQ0I

//  surface class

#include <surf_win.h>

#include <ctype.h>
#include <fstream.h>
#include <math.h>

#include <owl\gdiobjec.h>
#include <print.h>

// constructor
windows31::windows31(popup* p, boolean autoupdate) : canvas(*p),
						     _autoupdate(autoupdate)
{ *_print_device = 0;
  *_print_driver = 0;
  *_print_port = 0;
  cwp = p;
  heap_check(gfp = new grey_font_windows(0, 0));
  curx = 0;
  cury = 0;
  canvas.bitblt(0, 0, canvas.width(), canvas.height(), NULL, 0, 0, BLACKNESS);
  canvas.update();

// these aren't used; initialise to assist debugging
  x_ofs = 0;
  y_ofs = 0;
  use_left = 0;
  use_right = 0;
  use_top = 0;
  use_bottom = 0;
  _dotsize = 1;

// initialise the printer information (from Petzold)
  char pPrintInfo[80];

  GetProfileString("windows", "device", ",,,", pPrintInfo, 80);
  strcpy(_print_device, strtok(pPrintInfo, ","));
  strcpy(_print_driver, strtok(NULL, ", "));
  strcpy(_print_port, strtok(NULL, ", "));
}

// windows31 << LINE
surface& windows31::operator<<(LINE& rhs)
{ static TPen pen(RGB(255, 255, 255));

  canvas.set_pen(pen).moveto(rhs.x1(), rhs.y1()).lineto(rhs.x2(), rhs.y2());
  if (_autoupdate) then
    canvas.update();
  return *this;
}

// windows31 << dot
surface& windows31::operator<<(dot& rhs)
{ const int ds = ( (rhs.size() == -1) ? _dotsize : rhs.size());

  for (int n = 0; n < ds; n++)
    for (int m = 0; m < ds; m++)
		canvas.setpixel(rhs.x() - n, rhs.y() - m);

  if (_autoupdate) then
    canvas.update();
  return *this;
}

// windows31 << text
surface& windows31::operator<<(text& rhs)
{ if (!(rhs.length())) then
    return *this;

  canvas.fgc().bgc();

  const char* cp = (const char*)rhs.words();
  const int text_width = canvas.text_width(cp);

  int x = rhs.x(), y = rhs.y() - canvas.text_height(cp);

  if (x < 0) then
	 x = image_left() + (int)((abs(x) / 100.0) * image_width()) -
	  text_width / 2;

  canvas.write(cp, x, y);
  if (_autoupdate) then
	 canvas.update();

// temporarily force an update
//  canvas.update();

  return *this;
}

// move the current point
void windows31::moveto(const int x, const int y)
{ curx = x;
  cury = y;
}

void windows31::flush(void)
{ if (!(gfp->null())) then
  { const int orig_y = cury;
	 /* static */ TMemoryDC mem_dc;   // try static to see if faster

	 for (int n = 0; n < gfp->length(); n++)
	 { if (!(gfp->null(n))) then
		{ TBitmap& bm = *(TBitmap*)(gfp->memory_pattern(gfp->element(n)));
		  mem_dc.SelectObject(bm);
		  canvas.bitblt(curx, cury, gfp->width(), gfp->height(), mem_dc, 0, 0, SRCCOPY);
		}
		cury += gfp->height();
	 }
	 canvas.update(curx, orig_y, gfp->width(), gfp->height() * gfp->length());
  }
  gfp->length(0);
}

void windows31::clear(void)
{ gfp->length(0);
  canvas.clear().update(); 
}

// invert (on screen only)
void windows31::invert(void)
{ //TWindowDC(*cwp).BitBlt(0, 0, width(), height(), NULL, 0, 0, DSTINVERT);
  canvas.invert();
}

// I cannot get this to work in OWL
/* void windows31::print(const char*)
{ canvas.print();
} */


// print to the current printer
void windows31::print(const char*)
{
// much of this routine needs to be rewritten and incorporated in a windows31_printer
  // object, so as to hide the details from higher level code

// get the correct DLL
  char driver_file [13];
  strcpy(driver_file, _print_driver);
  strcat(driver_file, ".DRV");
  HINSTANCE hDriver = LoadLibrary(driver_file);
  if ((UINT)hDriver < 22) then
    fatal_error("Unable to load printer driver");

// the type LPFNDEVMODE seems to be utterly undocumented. According to the
// examples in the SDK, a simple FARPROC should be sufficient. It is not.
  LPFNDEVMODE lpfnExtDeviceMode = (LPFNDEVMODE)GetProcAddress(hDriver, "ExtDeviceMode");
  if (!lpfnExtDeviceMode) then           // not windows 3.1 driver
    fatal_error("Device driver is not 3.1");    // take the easy way out

// how large is the devmode for this printer?
  const int struct_size = (*lpfnExtDeviceMode)(cwp->HWindow, hDriver,
                            NULL, _print_device, _print_port, NULL,
				 NULL, 0);

  byte* device_mode_b = new byte [struct_size];
  DEVMODE*& device_mode = (DEVMODE*)device_mode_b;

// get the capabilities
  (*lpfnExtDeviceMode)(cwp->HWindow, hDriver,
                            (DEVMODE*)device_mode_b, _print_device, _print_port, NULL,
			    NULL, DM_COPY);

// set it to landscape
  device_mode->dmOrientation = DMORIENT_LANDSCAPE;
  (*lpfnExtDeviceMode)(cwp->HWindow, hDriver,
			    (DEVMODE*)device_mode_b, _print_device, _print_port,
				 (DEVMODE*)device_mode_b,
			    NULL, DM_MODIFY | DM_COPY);

  HDC hdcprint = CreateDC(_print_driver, _print_device, _print_port, device_mode_b);

  DOCINFO DocInfo;
  DocInfo.lpszDocName = "WMIDAS 17.00";
  DocInfo.lpszOutput = NULL;
  StartDoc(hdcprint, &DocInfo);
  StartPage(hdcprint);
  
  int x_dpi = device_mode->dmPrintQuality,
      y_dpi = (device_mode->dmFields & DM_YRESOLUTION) ? device_mode->dmYResolution
                                                             : x_dpi;

// we have to make some guesses if the driver merely specifies a crude resolution
  switch (x_dpi)
  { case 0 : x_dpi = 300; break;                   // argh! The PS driver is messed up
	 case DMRES_HIGH : x_dpi = 200; break;
	 case DMRES_MEDIUM : x_dpi = 150; break;
	 case DMRES_LOW : x_dpi = 100; break;
	 case DMRES_DRAFT : x_dpi = 75; break;
	 default : break;
  }

  switch (y_dpi)
  { case 0 : y_dpi = 300; break;
	 case DMRES_HIGH : y_dpi = 200; break;
	 case DMRES_MEDIUM : y_dpi = 150; break;
	 case DMRES_LOW : y_dpi = 100; break;
	 case DMRES_DRAFT : y_dpi = 75; break;
	 default : break;
  }

// we also fudge things if we are using the PCL driver and it says 150dpi (which usually
// means a low-memory PCL printer)
  if (!strcmp(_print_driver, "HPPCL")) then
  { x_dpi = 300;
	 y_dpi = 300;
  }

  const float x_size = 8.5,
              y_size = 11;

// calculate the factor to scale the bitmap
  const int x_factor = (int)((x_dpi * x_size) / canvas.height()),
		      y_factor = (int)((y_dpi * y_size) / canvas.width()),
		      factor = MIN(x_factor, y_factor);

// calculate the offset to place it nicely on the page
  const int x_offset = ((int)(x_dpi * x_size) - canvas.height() * factor) / 2,
				y_offset = ((int)(y_dpi * y_size) - canvas.width() * factor) / 2;

// now copy the source bitmap into the destination hdc
  StretchBlt(hdcprint, x_offset, y_offset, canvas.width() * factor,
             canvas.height() * factor, canvas._memdc, 0, 0,
		       canvas.width(), canvas.height(), SRCINVERT);

  EndPage(hdcprint);
  EndDoc(hdcprint);

  destroy_array(device_mode_b);
  DeleteDC(hdcprint);
  FreeLibrary(hDriver);
}

