// Brian Maddox - USGS MCMC SES - bmaddox@usgs.gov
// USGSDOQPluginLayer.cpp: implementation of the USGSDOQPluginLayer class.

#include "USGSDOQPluginLayer.h"
#include "ImageLib/ImageCommon.h"
#include "ImageLib/RGBPixel.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

USGSDOQPluginLayer::USGSDOQPluginLayer(int layerID, const char* strFilename,
                                       HINSTANCE hInstance)
 : RasterPluginLayer(layerID, strFilename, hInstance),
   _projection(UNKNOWN_PROJSYS), _datum(UNKNOWN_DATUM), _unit(UNKNOWN_UNIT),
   _zone(0), _samplesperpixel(0)
{
//  _lastrow = new long int;
  _lowerbound = new long int;
  _upperbound = new long int;
}

USGSDOQPluginLayer::~USGSDOQPluginLayer()
{
  try
  {
    delete indoq;
//    delete _lastrow;
    delete _lowerbound;
    delete _upperbound;
//    delete [] array;
    for (int loopc = 0; loopc < 10; loopc++)
      delete [] _cache[loopc];
  }
  catch (...)
  {
  }
}

void USGSDOQPluginLayer::getProjection(PROJSYS& projSys, DATUM& datum,
                                       UNIT& unit, int& zone,
                                       double* pParams) const
{
  projSys = _projection;
  datum = _datum;
  unit = _unit;
  zone = _zone;
}


void USGSDOQPluginLayer::getBoundingRect(double& left, double& bottom,
                                         double& right, double& top) const
{
  left = ulx;
  right = ulx + (_width * _resolution);
  top = uly;
  bottom = uly - (_height * _resolution);
}

long USGSDOQPluginLayer::getImageHeight() const
{
  return _height;
}

long USGSDOQPluginLayer::getImageWidth() const
{
  return _width;
}

bool USGSDOQPluginLayer::load()
{
  std::string _coordsystem;
  std::string _tdatum;
  std::string _units;
  std::string filename = getFilename();
  std::string _tbandorg;
  std::string quadname, quadrant, quadstate;//strings for description
  try
  {
    if (!(indoq = new(std::nothrow)
          USGSImageLib::DOQImageIFile(filename)))
      throw std::bad_alloc();
    if (!indoq->good())
      throw USGSImageLib::ImageException(IMAGE_FILE_OPEN_ERR);

    // Now go ahead and parse some stuff from the header and store it
    indoq->getSamples(_width);
    indoq->getLines(_height);
    indoq->getXOrigin(ulx);
    indoq->getYOrigin(uly);
    indoq->getHorizontalResolution(_resolution);
    indoq->getHorizontalCoordinateSystem(_coordsystem);
    if (!USGSImageLib::cmp_nocase(_coordsystem, std::string("UTM")))
      _projection = UTM;
    else if (!USGSImageLib::cmp_nocase(_coordsystem, std::string("SPCS")))
      _projection = SPCS;
    indoq->getHorizontalDatum(_tdatum);
    if (!USGSImageLib::cmp_nocase(_tdatum, std::string("NAD83")))
      _datum = NAD83;
    else if (!USGSImageLib::cmp_nocase(_tdatum, std::string("NAD27")))
      _datum = NAD27;

    // Now determine the units of the DOQ
    indoq->getHorizontalUnits(_units);
    if (!USGSImageLib::cmp_nocase(_units, std::string("METERS")))
      _unit = METERS;
    else if (!USGSImageLib::cmp_nocase(_units, std::string("FEET")))
      _unit = US_FEET;
    else if (!USGSImageLib::cmp_nocase(_units, std::string("SURVEY FEET")))
      _unit = US_FEET;
    else if (!USGSImageLib::cmp_nocase(_units, std::string("RADIANS")))
      _unit = RADIANS;
    else if (!USGSImageLib::cmp_nocase(_units, std::string("ARC SECONDS")))
      _unit = ARC_SECONDS;
    else if (!USGSImageLib::cmp_nocase(_units, 
                                       std::string("INTERNATIONAL FEET")))
      _unit = INTERNATIONAL_FEET;

    indoq->getCoordinateZone(_zone);
    indoq->getSamplesPerPixel(_samplesperpixel);

    // Initialize the scanline cache
//    if (!(array = new(std::nothrow) unsigned char[_width * _samplesperpixel]))
//      throw std::bad_alloc();
    for (int loopc = 0; loopc < 10; loopc++)
      _cache[loopc] = new unsigned char[_width * _samplesperpixel];

    // perform the initial scanline caching
    cacheScanlines(0);

//    *_lastrow = 0;
//    indoq->getRawScanline(0, array);

	//get the description
	indoq->getQuadName(quadname);
    indoq->getQuadrant(quadrant);
    indoq->getState(quadstate, 1);
    description = quadname + std::string(" ") + quadrant + std::string(", ")
             + quadstate;
    return true;
  }
  catch (...)
  {
    return false;
  }
}

bool USGSDOQPluginLayer::getRowColColor(long row, long col, 
                                        unsigned char& r, unsigned char& g,
                                        unsigned char& b) const
{
  int cachepos; // which cacheposition we use

  if ( (row < 0) || (row >= _height) || (col < 0) || (col >= _width) )
    return false;

  try
  {
    // If the scanline is already cached, don't do anything
    if ( !( (row >= *_lowerbound) && (row <= *_upperbound) ) )
      cacheScanlines(row);

    cachepos = row - *_lowerbound;

    if (_samplesperpixel == 1) // handle greyscale case
    {
//      r = g = b = array[col];
      r = g = b = _cache[cachepos][col];
    }
    else
    {
//      r = array[col * _samplesperpixel];
//      g = array[col * _samplesperpixel + 1];
//      b = array[col * _samplesperpixel + 2];
      r = _cache[cachepos][col * _samplesperpixel];
      g = _cache[cachepos][col * _samplesperpixel + 1];
      b = _cache[cachepos][col * _samplesperpixel + 2];
    }

    return true;
  }
  catch (...)
  {
    return false;
  }
}

const char* USGSDOQPluginLayer::getDescription() const
{

  try
  {
    return description.c_str();
  }
  catch (...)
  {
    return "";
  }
}

// This function starts at rowstart and caches the next 10 scanlines.  Return
// false instead of throwing an exception.
bool USGSDOQPluginLayer::cacheScanlines(long rowstart) const
{
  int rowcount;
  long _currrow;

  // This is to make sure we don't try to blow past the image bounds.
  if (rowstart <= 9) // just cache from 0 to 9
    rowstart = 0;
  else if (rowstart >= _height - 10)
    rowstart = _height - 10;
  // Here, the scanlines run 0 to height - 1, subtracting 9 since we also 
  // cache the first scanline gives us height - 10

  try
  {
    _currrow = rowstart;

    for (rowcount = 0; rowcount < 10; rowcount++)
    {
      indoq->getRawScanline(_currrow++, _cache[rowcount]);
    }

    // if we got here, modify the lower and upperbounds accordingly
    *_lowerbound = rowstart;
    *_upperbound = _currrow - 1;

    return true;
  }
  catch(...)
  {
    return false;
  }
}

void USGSDOQPluginLayer::showPropertiesDialog() const
{
	//Added for Doq Meta Data dialog - CBB
	//Initialize common controls, create dialog and pass it the filename as a LPARAM
	INITCOMMONCONTROLSEX ccom; //common control initializer
	
	ccom.dwSize = sizeof (LPINITCOMMONCONTROLSEX);
	ccom.dwSize = ICC_WIN95_CLASSES;
	
	::InitCommonControlsEx(&ccom);
	::CreateDialogParam( getInstance(), MAKEINTRESOURCE( IDD_PROPERTIES_DIALOG  ),
                        ::GetActiveWindow(), (DLGPROC)PropertiesDialogProc, 
						(LPARAM)(getFilename().c_str()));
}

BOOL CALLBACK PropertiesDialogProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    //Handles meta data dialog messages - CBB
	HWND hwnd2;            //handle to the edit control
	std::ifstream * file;    //file stream for reading in the header
	std::string *doqfile;   //file name
	char * data;           //buffer for text
	USGSImageLib::DOQImageIFile* indoq;   //temp doq for header size
	long bytecount;		   //size of header
	int counter;           //counter for loops
	static bool firstpaint; //just overload edit selection once
	RECT clientrect;   //the width and the height for resizing controls
	switch (uMsg)
	{
	case WM_INITDIALOG:
		//get the handle of the edit control
		hwnd2 = ::GetDlgItem(hwnd, IDC_EDIT1);
		//get the filename
		doqfile = new std::string((char *) lParam);
		//get the header size (not the best way, but easy)
		indoq = new USGSImageLib::DOQImageIFile ((*doqfile));
		indoq->getByteCount(bytecount);
		delete indoq;
		//create and init the buffer
		data = new char [bytecount +1];
		for (counter = 0; counter < bytecount+1; counter++)
			data[counter] = '\0';
		//open the doq and read in the header
		file = new ifstream(doqfile->c_str(), std::ios::in | std::ios::binary);
		file->read(data, bytecount);
		file->close();
		delete file;
		delete doqfile;
		//find and replace *\r with \r\n to put in end of lines
		for (counter = 0; counter < bytecount+1; counter++)
		{
			if (data[counter] == '*')
			{
				data[counter] = '\r';
				data[counter+1] = '\n';
			}
		}
		
		//send the buffer to the edit box
		::SendMessage( hwnd2, WM_SETTEXT, (WPARAM)0, (LPARAM)data);
		firstpaint = true; //set first paint
		delete [] data; //clean data
		return false;  //process default initdialog
	case WM_SIZE:
		clientrect.right = LOWORD(lParam);
		clientrect.bottom = HIWORD(lParam) - 30;
		clientrect.top = 0;
		clientrect.left = 0;
		//resize the text box
		hwnd2 = ::GetDlgItem(hwnd, IDC_EDIT1);
		//::SendMessage(hwnd2, WM_SIZE, (WPARAM)0, (LPARAM)&temp );
		::SetWindowPos(hwnd2, hwnd, 0, 0, clientrect.right, clientrect.bottom, SWP_NOZORDER);
		//resize the button
		hwnd2 = ::GetDlgItem(hwnd, IDC_OK);
		::SetWindowPos(hwnd2, hwnd, clientrect.right/2 - 30, clientrect.bottom, 60, 30, SWP_NOZORDER);

		return false;

	case WM_PAINT:
		if (firstpaint)
		{
			//get the hwnd of the edit box
			hwnd2 = ::GetDlgItem(hwnd, IDC_EDIT1);
			//tell the edit to stop selecting what it is selecting
			::SendMessage( hwnd2, EM_SETSEL, (WPARAM)-1, (LPARAM)-1 );
			firstpaint = false; //make sure that we don't do this again
		}
		return false; //process default paint after overload
	case WM_COMMAND://proccess ok button
		if (LOWORD(wParam) == IDC_OK)
			::PostMessage(hwnd, WM_CLOSE, 0, 0);
		return false;
	case WM_CLOSE:
	case WM_DESTROY:
		::DestroyWindow(hwnd); //destory dialog
	}
	return false; //process default messages
}
 