#if defined(_MSC_VER) /* MSVC Compiler */
#pragma warning ( disable : 4786 )
#endif

#include <float.h>
#include <stdio.h>
#include <qtextstream.h>
#include <QtDebug>

#include "qwt3d_curve.h"
#include "qwt3d_io_reader.h"

using namespace std;
using namespace Qwt3D;

const char* NativeReader::magicstring = "jk:11051895-17021986";
QString IO::title = QString();

namespace
{
	FILE* open(QString fname) {
		FILE* file = fopen(QWT3DLOCAL8BIT(fname), "r");
		if (!file) {
			fprintf(stderr, "NativeReader::read: cannot open data file \"%s\"\n", QWT3DLOCAL8BIT(fname));
		}
		return file;
	}

	int read_char (FILE * fp, bool skipcomments = true) {
		int c;
  
		if ((c = fgetc (fp)) == EOF)				return (c);

		if (skipcomments) {
			if (c == '#') {
				do {
					if ((c = fgetc (fp)) == EOF)	return (c);
				} while (c != '\n' && c != '\r');
			}
		}
		return (c);
	}

	char* read_field (FILE * fp, bool skipcomments = true) {
		static char buf[71];
		int c, i;
  
		do {
			if ((c = read_char (fp,skipcomments)) == EOF)	return (NULL);
		} while (isspace (c));

		for (i = 0; i < 70 && !isspace (c); ++i) {
			buf[i] = c;
			if ((c = read_char (fp,skipcomments)) == EOF)	break;
		}
		buf[i] = '\0';
		return (buf);
	}

	QString read_line (FILE * fp, bool skipcomments = true) {
		int c;
		QString line;
  
		do {
			if ((c = read_char (fp,skipcomments)) == EOF)	return (NULL);
			if (c != '\n' && c != '\r')						line.append(c);
		} while (c != '\n' && c != '\r');
    
		return line;
	}

	//! set to data begin
	bool extract_info(FILE* fp, unsigned int& xmesh, unsigned int& ymesh, double& xmin, double& xmax, double& ymin, double& ymax) {
		char* p;
  
		// find out the size
		if ((p = read_field (fp)) == 0)		return false;
		xmesh = (unsigned int)atoi(p);

		if ((p = read_field (fp)) == 0)		return false;
		ymesh = (unsigned int)atoi (p);

		if (xmesh < 1 || ymesh < 1)			return false;
    
		// ... and the limits
		if ((p = read_field (fp)) == 0)		return false;
		xmin = atof (p);

		if ((p = read_field (fp)) == 0)		return false;
		xmax = atof (p);
    
		if ((p = read_field (fp)) == 0)		return false;
		ymin = atof (p);

		if ((p = read_field (fp)) == 0)		return false;
		ymax = atof (p);

		if (xmin > xmax || ymin > ymax)		return false;

		return true;
	}

	//! find out what the magic string is and compare
	bool check_magic(FILE* fp, const char* val)
	{
		Q_UNUSED(val);
		
		if ((IO::title = read_line (fp,false)) == 0)	return false;

		return true;
	}

	//! find out what the type is
	bool check_type(FILE* fp, const char* val)
	{
		char* p;
		if ((p = read_field (fp)) == 0)		return false;

		if (strcmp (p, val ) != 0)			return false;

		return true;
	}

	double** allocateData(int columns, int rows)
	{
 		double** data	= new double* [columns] ;
 
		for ( int i = 0; i < columns; ++i) 
		{
			data[i]		= new double [rows];
		}
		return data;
	}

	void deleteData(double**data, int columns)
	{
		for ( int i = 0; i < columns; i++) 
		{
			delete [] data[i];
		}
		delete [] data;
	}
}

NativeReader::NativeReader()
: minz_(-DBL_MAX), maxz_(DBL_MAX) 
{
}

bool NativeReader::collectInfo(FILE*& file, QString const& fname, unsigned& xmesh, unsigned& ymesh, 
															 double& minx, double& maxx, double& miny, double& maxy)
{
	if (fname.isEmpty())
		return false;
	
	file = open(fname);
	
	if (!file)
		return false;
	
	
	if (
				(!check_magic(file, magicstring))
			||(!check_type(file, "MESH"))
			||(!extract_info(file, xmesh, ymesh, minx, maxx, miny, maxy))
		 )
	{
		fclose(file);
		return false;
	}
 
	return true;
}


bool NativeReader::operator()(Plot3D* plot, QString const& fname)
{
	FILE* file;
	unsigned int xmesh, ymesh;
	double minx, maxx, miny, maxy;
	
	if ( !collectInfo(file, fname, xmesh, ymesh, minx, maxx, miny, maxy) )
		return false;
	
 	double** data = allocateData(xmesh, ymesh);		// allocate some space for the mesh

	for (unsigned int j = 0; j < ymesh; j++) {
		for (unsigned int i = 0; i < xmesh; i++) {
			if (fscanf(file, "%lf", &data[i][j]) != 1) {
				fprintf(stderr, "NativeReader::read: error in data file \"%s\"\n", QWT3DLOCAL8BIT(fname));
				return false;
			}

			if (data[i][j] > maxz_)			data[i][j] = maxz_;
			else if (data[i][j] < minz_)	data[i][j] = minz_;
		}
	}

	fclose(file);	// close the file

	Curve* curve = plot->curve();
	curve->loadFromData(data, xmesh, ymesh, minx, maxx, miny, maxy);
	qDebug() << "NativeReader: loadFromData - data =" << data << "[ xmesh =" << xmesh << "ymesh =" << ymesh << "maxx =" << maxx << "maxy =" << maxy << "]";

	plot->addCurve(curve);
	plot->addTitle(curve->title());
	deleteData(data,xmesh);

	return true;
}
