/*
Gwenview - A simple image viewer for TDE
Copyright 2000-2004 Aurélien Gâteau
 
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
*/
#include <iostream>

// TQt
#include <tqdir.h>
#include <tqfile.h>
#include <tqimage.h>
#include <tqstring.h>

// KDE
#include <tdeapplication.h>
#include <tdeaboutdata.h>
#include <tdecmdlineargs.h>
#include <kdebug.h>
#include <tdefilemetainfo.h>

// Local
#include "imageutils/imageutils.h"
#include "imageutils/jpegcontent.h"

using namespace std;

const char* ORIENT6_FILE="orient6.jpg";
const int ORIENT6_WIDTH=128;  // This size is the size *after* orientation
const int ORIENT6_HEIGHT=256; // has been applied
const char* CUT_FILE="cut.jpg";
const TQString ORIENT6_COMMENT="a comment";
const TQString ORIENT1_VFLIP_FILE="test_orient1_vflip.jpg";
const char* THUMBNAIL_FILE="test_thumbnail.jpg";
const char* TMP_FILE="tmp.jpg";


class TestEnvironment {
public:
	TestEnvironment() {
		bool result;
		TQFile in(ORIENT6_FILE);
		result=in.open(IO_ReadOnly);
		Q_ASSERT(result);
		
		TQFileInfo info(in);
		int size=info.size()/2;
		
		char* data=new char[size];
		int readSize=in.readBlock(data, size);
		Q_ASSERT(size==readSize);
		
		TQFile out(CUT_FILE);
		result=out.open(IO_WriteOnly);
		Q_ASSERT(result);
		
		int wroteSize=out.writeBlock(data, size);
		Q_ASSERT(size==wroteSize);
		delete []data;
	}

	~TestEnvironment() {
		TQDir::current().remove(CUT_FILE);
	}
};


typedef TQMap<TQString,TQString> MetaInfoMap;

MetaInfoMap getMetaInfo(const TQString& path) {
	KFileMetaInfo fmi(path);
	TQStringList list=fmi.supportedKeys();
	TQStringList::ConstIterator it=list.begin();
	MetaInfoMap map;
	
	for ( ; it!=list.end(); ++it) {
		KFileMetaInfoItem item=fmi.item(*it);
		map[*it]=item.string();
	}

	return map;
}

void compareMetaInfo(const TQString& path1, const TQString& path2, const TQStringList& ignoredKeys) {
	MetaInfoMap mim1=getMetaInfo(path1);
	MetaInfoMap mim2=getMetaInfo(path2);

	Q_ASSERT(mim1.keys()==mim2.keys());
	TQValueList<TQString> keys=mim1.keys();
	TQValueList<TQString>::ConstIterator it=keys.begin();
	for ( ; it!=keys.end(); ++it) {
		TQString key=*it;
		if (ignoredKeys.contains(key)) continue;

		if (mim1[key]!=mim2[key]) {
			kdError() << "Meta info differs. Key:" << key << ", V1:" << mim1[key] << ", V2:" << mim2[key] << endl;
		}
	}
}


void testResetOrientation() {
	ImageUtils::JPEGContent content;
	bool result;

	// Test resetOrientation without transform
	result=content.load(ORIENT6_FILE);
	Q_ASSERT(result);

	content.resetOrientation();

	result=content.save(TMP_FILE);
	Q_ASSERT(result);

	result=content.load(TMP_FILE);
	Q_ASSERT(result);
	Q_ASSERT(content.orientation() == ImageUtils::NORMAL);

	// Test resetOrientation with transform
	result=content.load(ORIENT6_FILE);
	Q_ASSERT(result);

	content.resetOrientation();
	content.transform(ImageUtils::ROT_90);

	result=content.save(TMP_FILE);
	Q_ASSERT(result);

	result=content.load(TMP_FILE);
	Q_ASSERT(result);
	Q_ASSERT(content.orientation() == ImageUtils::NORMAL);
}


/**
 * This function tests JPEGContent::transform() by applying a ROT_90
 * transformation, saving, reloading and applying a ROT_270 to undo the ROT_90.
 * Saving and reloading are necessary because lossless transformation only
 * happens in JPEGContent::save()
 */
void testTransform() {
	bool result;
	TQImage finalImage, expectedImage;

	ImageUtils::JPEGContent content;
	result = content.load(ORIENT6_FILE);
	Q_ASSERT(result);

	content.transform(ImageUtils::ROT_90);
	result = content.save(TMP_FILE);
	Q_ASSERT(result);

	result = content.load(TMP_FILE);
	Q_ASSERT(result);
	content.transform(ImageUtils::ROT_270);
	result = content.save(TMP_FILE);
	Q_ASSERT(result);

	result = finalImage.load(TMP_FILE);
	Q_ASSERT(result);

	result = expectedImage.load(ORIENT6_FILE);
	Q_ASSERT(result);

	Q_ASSERT(finalImage == expectedImage);
}


void testSetComment() {
	TQString comment = "test comment";
	ImageUtils::JPEGContent content;
	bool result;
	result = content.load(ORIENT6_FILE);
	Q_ASSERT(result);

	content.setComment(comment);
	Q_ASSERT(content.comment() == comment);
	result = content.save(TMP_FILE);
	Q_ASSERT(result);

	result = content.load(TMP_FILE);
	Q_ASSERT(result);
	Q_ASSERT(content.comment() == comment);
}


int main(int argc, char* argv[]) {
	TestEnvironment testEnv;
	bool result;
	TDEAboutData aboutData("testjpegcontent", "testjpegcontent", "0");
	TDECmdLineArgs::init( argc, argv, &aboutData );
	TDEApplication tdeapplication;

	// Reading info
	ImageUtils::JPEGContent content;
	result=content.load(ORIENT6_FILE);
	Q_ASSERT(result);
	Q_ASSERT(content.orientation() == 6);
	Q_ASSERT(content.comment() == ORIENT6_COMMENT);
	Q_ASSERT(content.size() == TQSize(ORIENT6_WIDTH, ORIENT6_HEIGHT));
	
	// thumbnail()
	TQImage thumbnail=content.thumbnail();
	result=thumbnail.save(THUMBNAIL_FILE, "JPEG");
	Q_ASSERT(result);

	testResetOrientation();
	testTransform();
	testSetComment();

    // Test that rotating a file a lot of times does not cause findJxform() to fail
    result = content.load(ORIENT6_FILE);
    Q_ASSERT(result);
    
    // 12*4 + 1 is the same as 1, since rotating four times brings you back
    for(int loop=0; loop< 12*4 + 1; ++loop) {
        content.transform(ImageUtils::ROT_90);
    }
    result = content.save(TMP_FILE);
    Q_ASSERT(result);

    result = content.load(TMP_FILE);
    Q_ASSERT(result);

    Q_ASSERT(content.size() == TQSize(ORIENT6_HEIGHT, ORIENT6_WIDTH));
    
	
	// Check the other meta info are still here
	TQStringList ignoredKeys;
	ignoredKeys << "Orientation" << "Comment";
	compareMetaInfo(ORIENT6_FILE, ORIENT1_VFLIP_FILE, ignoredKeys);

	// Test that loading and manipulating a truncated file does not crash
	result=content.load(CUT_FILE);
	Q_ASSERT(result);
	Q_ASSERT(content.orientation() == 6);
	Q_ASSERT(content.comment() == ORIENT6_COMMENT);
	content.transform(ImageUtils::VFLIP);
	kdWarning() << "# Next function should output errors about incomplete image" << endl;
	content.save(TMP_FILE);
	kdWarning() << "#" << endl;
}
