/* 
 * This file Copyright (C) 2010 Christopher Stamm
 *                              Fachhochschule Nordwestschweiz 
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
 * as published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include "pnm.h"
#include <iostream>
#include <cmath>	// evtl. #include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


#if defined(__linux__) || defined(__APPLE__)
	#define __POSIX__
#endif

#ifdef __POSIX__
	#include <libpgf/PGFimage.h>
	#include <unistd.h>	   // open, close
	#include <stdio.h>
	#include <errno.h>
	#include <string.h>
#else
	#include "PGFimage.h"
	#include "windows.h"
#endif

/////////////////////////////////////////////////////////////////////////////
// definitions
#ifdef __APPLE__
	#define __stat64 stat
	#define _stat64 stat
#elif defined __POSIX__
	#define __stat64 stat64
	#define _stat64 stat64
#endif

using namespace std;

/////////////////////////////////////////////////////////////////////////////
static bool Encoding(char *dest, int levels, int quality, bool roi, bool alpha) {
	ASSERT(dest);
	ASSERT(0 <= quality && quality <= MaxQuality);
	CPGFImage pgf;

#ifdef WIN32
	// no other choice to get a "HANDLE"
	HANDLE fd = CreateFile(dest, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
	if (fd == INVALID_HANDLE_VALUE) {
		cerr << "Error: Could not open destination file." << endl;
		return false;
	}
	
	// create stream object
	CPGFFileStream stream(fd);

#elif defined(__POSIX__)
	int fd = open(dest, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
	if (fd == -1) {
		cout << "Error: Could not open destination file." << endl;
		return false;
	}
	
	// create stream object
	CPGFFileStream stream(fd);		
#endif

	// create new PNMDoc
	CPNM pnm;

	// load image
	if (!pnm.ReadPNM(pgf, quality, levels, roi, alpha)) return false;

	try {
		// write image to pgf-file
		pgf.Write(&stream, levels);

	} catch(IOException& e) {
		int err = e.error;
		if (err >= AppError) err -= AppError;
		cerr << "Error: Writing PGF failed (" << err << ")!" << endl;

	#ifdef WIN32
		CloseHandle(fd);
	#elif defined(__POSIX__)
		close(fd);
	#endif 

		return false;
	}

#ifdef WIN32
	CloseHandle(fd);
#elif defined(__POSIX__)
	close(fd);
#endif 

	return true;
}

/////////////////////////////////////////////////////////////////////////////
static bool Decoding(char *source, bool roi, PGFRect& rect, bool ascii) {
	ASSERT(source);
	CPGFImage pgf;

#ifdef WIN32
	// no other choice to get a "HANDLE"
	HANDLE fd = CreateFile(source, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
	if (fd == INVALID_HANDLE_VALUE) {
		cerr << "Error: Could not open source file." << endl;
		return false;
	}

	// create stream object
	CPGFFileStream stream(fd);

#elif defined(__POSIX__)
	int fd = open(source, O_RDONLY);
	if (fd == -1){
		cout << "Error: Could not open source file." << endl;
		return false;
	}

	// create stream object
	CPGFFileStream stream(fd);
#endif

	try {
		// open pgf image
		pgf.Open(&stream);

		// read pgf image
	#ifdef __PGFROISUPPORT__
		if (pgf.ROIisSupported() && roi) {
			pgf.Read(rect);
		} else 
	#endif
		{
			pgf.Read();
		}

	} catch(IOException& e) {
		int err = e.error;
		if (err >= AppError) err -= AppError;
		cerr << "Error: Opening and reading PGF image failed (" << err << ")!" << endl;

	#ifdef WIN32
		CloseHandle(fd);
	#elif defined( __POSIX__)
		close(fd);
	#endif 

		return false;
	}

	// convert to PNM
	CPNM pnm;

	if (!pnm.WritePNM(pgf, roi, rect, !ascii)) {
		return false;
	}

#ifdef WIN32
	CloseHandle(fd);
#elif defined(__POSIX__)
	close(fd);
#endif

	return true;
}


/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) {
	int nRetCode = 0;

	// parse arguments
	int levels = 0, quality = 0;
	PGFRect rect;
	int arg = 1;			// argument 0 = pgf2pnm
	bool bROI = false;		// true -> ROI is used
	bool bASCII = false;	// ASCII mode 
	bool bEncode = false;	// PNM to PGF
	bool bAlpha = false;	// Alpha channel
	bool bWrongArgs = (argc < 2);
	char *filename;

	while (!bWrongArgs && arg < argc) {
		if (argv[arg][0] == '-') {
			// options
			switch(argv[arg][1]) {
			case 'e': 
				bEncode = true; 
				arg++; 
				break;
			case 'l': 
				arg++; 
				if (arg == argc) {
					bWrongArgs = true; 
				} else {
					levels = atoi(argv[arg]); arg++; 
					bWrongArgs = (levels < 1) || (levels > MaxLevel);
				}
				break;
			case 'q': 
				arg++; 
				if (arg == argc) {
					bWrongArgs = true; 
				} else {
					quality = atoi(argv[arg]); arg++;
					bWrongArgs = (quality < 0) || (quality > MaxQuality);
				}
				break;
			case 'r': 
				bROI = true;
				arg++;
				if (!bEncode) {
					if (arg == argc) bWrongArgs = true;
					if (arg + 4 > argc) {
						bWrongArgs = true;
					} else {
						rect = PGFRect(atoi(argv[arg]), atoi(argv[arg+1]), atoi(argv[arg+2]), atoi(argv[arg+3]));
						arg += 4;
					}
				}
				break;
			case 'a': 
				bASCII = true;
				arg++;
				break;
			case 't': 
				bAlpha = true;
				arg++;
				break;
			default: 
				arg++; 
				bWrongArgs = true; 
				break;
			}
		} else {
			filename = argv[arg];
			arg++;
		}
	}
	if (bWrongArgs) {
		cerr << "Usage: " << endl <<
			   "pgf2pnm [-r left top width height] [-a] source" << endl << 
				"               Converts a PGF image (source file) into a PNM image" << endl << 
				"               and sends its output to standard output." << endl << 
				"  Options:" << endl << 
				"  -r rect      Read a rectangular region of a PGF image supporting Region of" << endl << 
				"               interests (ROI). The rectangle is defined by its top-left" << endl <<
				"               corner and a positive width and height." << endl <<
				"  -a           Uses PNM ASCII mode instead of binary mode." << endl <<
				endl <<
			   "pgf2pnm -e [-l levels] [-q quality] [-r] [-t] destination" << endl << 
				"               Reads in a PNM image from standard input" << endl << 
				"               and creates a PGF image (destination file)." << endl << 
				"  Options:" << endl << 
				"  -l levels    Number of hierarchical levels [1.." << MaxLevel << "]. Default is 0." << endl <<
				"               0 means the number of levels are automatically set." << endl <<
				"  -q quality   Quality [0.." << MaxQuality << "]. Default is 0." << endl << 
				"               0 means perfect quality (lossless compression)," << endl << 
				"               " << MaxQuality << " means maximum compression." << endl << 
				"  -r           Region of interest (ROI) encoding scheme is used." << endl <<
				"               This encoding scheme has a slightly worse compression ratio." << endl <<
				"  -t           Supports RGBA file format: PPM image followed by PGM image." << endl <<
				endl;
		nRetCode = 2;
	} else {
		if (bEncode) {
			// read pnm and write pgf
			if (!Encoding(filename, levels, quality, bROI, bAlpha)) nRetCode = 3;
		} else {
			// read pgf and write pnm
			if (!Decoding(filename, bROI, rect, bASCII)) nRetCode = 4;
		}
	}
	return nRetCode;
}

