/*
 * (C) Copyright 1996-2016 ECMWF.
 * 
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
 * In applying this licence, ECMWF does not waive the privileges and immunities 
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

/*! \file BaseDriverImages.h
    \brief Implementation of methods to display images of driver base class.

    Magics Team - ECMWF 2005

    Started: March 2005

    Changes:

*/

#ifdef MAGICS_RASTER
#include <gd.h>
#endif

using namespace magics;


static inline int getRealLine(ifstream& I, char* cline)
{
	int k = 0;
	char ch;
	while ( I.get(ch) )
	{
		cline[k] = ch;
		if ( ch == '\n' || ch == '\0' || I.eof() )
			break;
		k++;
	}
	cline[k] = '\0';
	return k;
}

static inline int getGoodLine(ifstream& I, char* cline,const int& n)
{
	int k = 0;
	char ch;
	while(I.get(ch))
	{
		cline[k] = ch;
		if ( ch == '\n' || ch == '\0' || I.eof() || k == n ) break;
		k++;
	}
	cline[k] = '\0';
	return k;
}

static inline int check_ps(const string& file, int& boxx, int& boxy, int& orientation)
{
	ifstream I(file.c_str());
	if (!I) return 1;

	string::size_type x,y;
	char *cline;
	cline = new char[2048];
	int k = getGoodLine(I,cline,2048);
	string s(cline,k);

	// level 3 only "%!PS-Adobe-3.0";
	x = s.find("%!PS");
	if(x == string::npos) return 1;  //no PostScript

	// Looks like a PostScript file
	int r = 0;
	boxx = boxy = 0;
	orientation = 0; // Portrait
	int boundingbox_found = 1;
	int orientation_found = 1;
	MFloat xx = 0.0,yy = 0.0;

	while(!I.eof()) // look for  %%BoundingBox: %%Orientation:
	{
		k = getGoodLine(I, cline,2048);
		int i = ( k > 15 ) ? 15 : k;
		if ( i < 15 )  // dont bother
			continue;
		string f(cline,i);

		if ( boundingbox_found )
		{
			x = f.find("%%BoundingBox:");
			if ( x != string::npos  )
			{
				string str(cline,k);
				y = str.find("atend");
				if ( y != string::npos )
					continue;
				else
				{
					str.erase(0,x+15);
					istringstream ist(str.c_str());

					MFloat dum1, dum2;
					ist >>dum1 >> dum2 >> xx >> yy;
					xx = xx - dum1;
					yy = yy - dum2;
					boundingbox_found = 0;
				}
			}
		}
		if ( orientation_found )
		{
			x = f.find("%%Orientation:");
			if ( x != string::npos )
			{
				string str(cline,k);
				y = str.find("atend");
				if ( y != string::npos )
					continue;
				else
				{
					y = str.find("Landscape");
					orientation = ( y != string::npos  ) ? 1 : 0;
					orientation_found = 0;
				}
			}
		}
		if ( (orientation_found + boundingbox_found) == 0 ) break;
	}

	boxx = (int) xx;  boxy = (int) yy;
	if ( boxx == 0 || boxy == 0 ) // No boundingbox
	{
		boxx = 595;
		boxy = 841;
	}

	I.close();
	delete [] cline;
	return r;
}

static inline int check_ppmHeader(ifstream& I, int& col, int& row)
{
	int colours;
	char *cline = new char[2048];

	int k = getRealLine(I, cline);
	string s = cline;
	int r = 0;
	int x = s.find("P6");

	if(x<0) r = 1;// not PPM P6 format

	if(!r)
	{
		cline[0] = '#';
		while ( cline[0] == '#')  // skip comments
			k = getRealLine(I,cline);
		istringstream ist1(cline);
		ist1 >> col >> row;
		k = getRealLine(I,cline);
		istringstream ist2(cline);
		ist2 >> colours;

		if ( col == 0 || row == 0 || colours == 0 ) r =  1;
	}

	delete [] cline;
	return r;
}


/*!
  \brief Image render method for ALL drivers.

  This method should be used by all Magics++ drivers to render image objects.
*/
MAGICS_NO_EXPORT void BaseDriver::renderImage(const ImportObject& obj) const
{
	std::string f = obj.getFormat();
	GraphicsFormat format = PNG;
	if(magCompare(f,"ps")) format = PS;
	else if(magCompare(f,"eps")) format = EPS;
	else if(magCompare(f,"gif")) format = GIF;
	else if(magCompare(f,"jpeg") || magCompare(f,"jpg")) format = JPG;
	else if(magCompare(f,"png")) format = PNG;
	else if(magCompare(f,"svg")) format = SVG;

	MFloat width=0;
	MFloat height=0;

	if(obj.getWidth()==-1 && ( magCompare(f,"gif") || magCompare(f,"png") || magCompare(f,"jpeg")|| magCompare(f,"jpg") ) )
	{
#ifndef MAGICS_RASTER
		MagLog::error() << "BaseDriverImages: Dimension is -1 and default size can not be determined (GD library required)!" << endl;
		return;
#else
		FILE *in = fopen(obj.getPath().c_str(), "rb");
		gdImagePtr image = 0;
		if(magCompare(f,"png")) image = gdImageCreateFromPng(in);
		else if(magCompare(f,"jpeg") || magCompare(f,"jpg")) image = gdImageCreateFromJpeg(in);
		else if(magCompare(f,"gif"))
		{
#ifdef MAGICS_GIF
			image = gdImageCreateFromGif(in);
#else
			MagLog::error() << "GIF import is not supported in this version! You need a GIF enabled GD library." << endl;
			return;
#endif
		}
		fclose(in);
		width  = gdImageSX(image);
		height = gdImageSY(image);
		gdImageDestroy(image);
#endif
	}
	else
	{
		width  = obj.getWidth();
		height = obj.getHeight();
	}
	const MFloat ow = (obj.getWidth()<0)  ? convertCM(1./coordRatioX_) : width;
	const MFloat oh = (obj.getHeight()<0) ? convertCM(1./coordRatioY_) : height;

	const ImageProperties::OriginReference ori = obj.getOriginReference();
	if(ori == ImageProperties::centre)
		convertToPixmap(obj.getPath(),format, 300,
			projectX(obj.getOrigin().x()-(ow*.5)),
			projectY(obj.getOrigin().y()-(oh*.5)),
			projectX(obj.getOrigin().x()+(ow*.5)),
			projectY(obj.getOrigin().y()+(oh*.5)) );
	else
		convertToPixmap(obj.getPath(),format, 300,
			projectX(obj.getOrigin().x()),
			projectY(obj.getOrigin().y()),
			projectX(obj.getOrigin().x()+ow),
			projectY(obj.getOrigin().y()+oh) );
}//end BaseDriver::renderImage()


/*!
  \brief converting object to pixmap

  This method should be used by all Magics++ drivers

*/
MAGICS_NO_EXPORT bool BaseDriver::convertToPixmap(const string &fname, const GraphicsFormat format, const int reso,
		     const MFloat wx0, const MFloat wy0,const MFloat wx1,const MFloat wy1) const
{
	debugOutput("Start Image conversion");

	int Landscape = 0;
	MFloat bx1=100.;
	MFloat by1=100.;
	unsigned char *image = 0;
	int col=0,row=0;
	int status = 0;
	string s2(" ");
	string pixmapFormat("rgb");

	if(format==PS || format==EPS) //File is PostScript
	{
//		FILE* fd3;
//		char buf[1024];
		string cmd;
		int x1 = 0;
		int y1 = 0;

		if(check_ps(fname,x1,y1,Landscape) )
		{
			MagLog::error() << "BaseDriverImages: Open of source PostScript file failed!" << endl;
			return false;
       		}

		s2 = getTmpName()+".ppm";
		if(s2==" ")
		{
			MagLog::error() << "BaseDriverImages: Open of temp file failed!" << endl;
			return false;
		}

	//	if(format==PS)  // does not work with EPS
		{
		  const MFloat Xres = MFloat(reso);
		  bx1 = MFloat(x1)/72.*Xres + 0.5;
		  x1  = (int) bx1;
		  by1 = MFloat(y1)/72.*Xres + 0.5;
		  y1  = (int) by1;

		  char boxx[5];
		  char boxy[5];
		  char boxz[5];
		  sprintf(boxx,"%d",x1);
		  sprintf(boxy,"%d",y1);
		  sprintf(boxz,"%d",reso);

		  cmd = "( gs -q -dNOPAUSE -dSAFER -sDEVICE=ppmraw -sOutputFile=" + s2 +
			" -dGraphicsAlphaBits=4 -dTextAlphaBits=4 -dCOLORSCREEN -dBATCH -g" +
			boxx + "x" + boxy + " -r" + boxz + " " + fname + " < /dev/null )";
		}

		status = system(cmd.c_str());

		if(status)
		{
			MagLog::error() << "BaseDriverImages: Command exit Not zero" << endl;
			return false;
		}
		ifstream I(s2.c_str());
		if(!I)
		{
			MagLog::error() << "BaseDriverImages: Incorrect PostScript Format!" << endl;
			return false;
		}
		if(check_ppmHeader(I,col,row) )
		{
			return 1;
		}

		image = new unsigned char [row*col*3];
		I.read((char*)image,row*col*3);
		I.close();
		remove(s2.c_str());
	}
#ifdef MAGICS_RASTER
	else if(format==GIF || format==PNG || format==JPG)
	{
		// convert png to ppm(raw) like image
		FILE* fd3 = fopen(fname.c_str(),"rb");
		if(!fd3)
		{
			MagLog::error() << "BaseDriverImages: Open failed for raster source > "<< fname << endl;
			return false;
		}
		gdImagePtr imp = 0;
		if(format==JPG)      imp = gdImageCreateFromJpeg(fd3);
		else if(format==PNG) imp = gdImageCreateFromPng(fd3);
		else if(format==GIF)
		{
#ifdef MAGICS_GIF
			imp = gdImageCreateFromGif(fd3);
#else
			MagLog::error() << "GIF pixmap import is not supported in this version! You need a GIF enabled GD library." << endl;
			return false;
#endif
		}
		fclose(fd3);
		if(!imp)
		{
			MagLog::error() << "BaseDriverImages: Incorrect raster file format (can not be handled by GD) !" << endl;
			return false;
		}

		col = gdImageSX(imp);
		row = gdImageSY(imp);
		bx1 = (MFloat) col;
		by1 = (MFloat) row;
		Landscape = 0;

		if(format == PNG && // imp->transparent == 1 &&
                   alphaEnabled_ == true)
		{
			pixmapFormat="rgba";

			image = new unsigned char [col*row*4];
			unsigned char *p = image;
			int i,j;
			for (i = 0; i<row; i++)
			for (j = 0; j<col; j++)
			{
				int c = gdImageGetPixel(imp,j,i);
				int r = gdImageRed(imp,c);
				int g = gdImageGreen(imp,c);
				int b = gdImageBlue(imp,c);
				int a = gdImageAlpha(imp,c);
				*(p++) = (unsigned char) r;
				*(p++) = (unsigned char) g;
				*(p++) = (unsigned char) b;
				*(p++) = (unsigned char) 255-2*a;
			}
		}
		else
		{
			image = new unsigned char [col*row*3];
			unsigned char *p = image;
			int i,j;
			for (i = 0; i<row; i++)
			for (j = 0; j<col; j++)
			{
				int c = gdImageGetPixel(imp,j,i);
				int r = gdImageRed(imp,c);
				int g = gdImageGreen(imp,c);
				int b = gdImageBlue(imp,c);
				*(p++) = (unsigned char) r;
				*(p++) = (unsigned char) g;
				*(p++) = (unsigned char) b;
			}
		}

		gdImageDestroy(imp);
	}
#endif
	else 
	{
		MagLog::warning() << "BaseDriverImages: graphics formats ("<<format<<") is NOT supported!" << endl;
		return 1;//No PPM
	}

	MFloat x0 = wx0; //Left
	MFloat x1 = wx1; //Right
	MFloat y0 = wy0;
	MFloat y1 = wy1;

	// const MFloat aspect = bx1/by1;
	//if ( Landscape == 1 )		y1 = y0 + abs(x1-x0)*aspect;
	//else if ( Landscape == 0 )	x1 = x0 + abs(y1-y0)*aspect;

	bool alpha = (pixmapFormat == "rgba");
	status = renderPixmap(x0,y0,x1,y1,col,row,image,Landscape,alpha);

	if(!status) MagLog::warning() <<"BaseDriver::convertToPixmap() -> no Pixmap could be drawn! Zero size of at least one dimension."<< std::endl;
	delete [] image;

	return status;
}

/*
bool BaseDriver::renderPixmap(MFloat ,MFloat,MFloat,MFloat ,int w,int h,unsigned char* IOUT,int, bool hasAlpha) const
{
#ifndef MAGICS_RASTER
	MagLog::warning() << "Image import is not implemented for the used driver!!!" << endl;
	return false;
#else
	gdImagePtr im = gdImageCreateTrueColor(w,h);
	unsigned char *p = IOUT;
	gdImageColorAllocateAlpha(im, 255, 255, 255, 127);
	int a = 0;

	for(int i=h-1;i>=0;i--)
	{
		for(int j=0;j<w; j++)
		{
			const int r = (int) *(p++);
			const int g = (int) *(p++);
			const int b = (int) *(p++);
			if(hasAlpha) a = (int) *(p++);
			const int col = gdImageColorResolveAlpha(im,r,g,b,a);
			gdImageSetPixel(im, w, h, col);
		}
	}
	gdImageDestroy(im);
	gdImageAlphaBlending(im, 1);
	gdImageSaveAlpha(im, 1); // save transparency

	stringstream out;
	out << output_resource_list_.size();
	string filename = "magics_resource_"+out.str()+".png";

	output_resource_list_.push_back(filename);


	FILE *outFile = fopen(filename.c_str(),"wb");
	gdImagePng(im,outFile);
	fclose(outFile);

	return true;
#endif
}
*/
