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

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

#include "qwt3d_surfaceplot.h"
#include "qwt3d_io_reader.h"

using namespace std;
using namespace Qwt3D;

const char* NativeReader::magicstring = "jk:11051895-17021986";

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);
  }


  //! 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)
	{
    char* p;
    if ((p = read_field (fp,false)) == 0)
        return false;
  
    if (strcmp (p, val ) != 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;
	
	/* allocate some space for the mesh */
 	double** data = allocateData(xmesh, ymesh);

	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_;
    }
  }

  /* close the file */
  fclose(file);

	((SurfacePlot*)plot)->loadFromData(data, xmesh, ymesh, minx, maxx, miny, maxy);
	deleteData(data,xmesh);

	return true;
}
