/***************************************************************************
*   Copyright (C) 2003 by                                                 *
*   Cyril Bosselut (bosselut@b1project.com)                               *
*                                                                         *
*   Copyright (C) 2003-2005 by                                            *
*   Jason Kivlighn (jkivlighn@gmail.com)                                  *
*                                                                         *
*   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.                                   *
***************************************************************************/

#include "kreimporter.h"

#include <tdelocale.h>
#include <kdebug.h>

#include <tqfile.h>
#include <tqstringlist.h>
#include <tdestandarddirs.h>

#include "datablocks/recipe.h"
#include "datablocks/categorytree.h"

KreImporter::KreImporter() : BaseImporter()
{}

void KreImporter::parseFile( const TQString &filename )
{
	TQFile * file = 0;
	bool unlink = false;
	kdDebug() << "loading file: %s" << filename << endl;

	if ( filename.right( 4 ) == ".kre" ) {
		file = new TQFile( filename );
		kdDebug() << "file is an archive" << endl;
		KTar* kre = new KTar( filename, "application/x-gzip" );
		kre->open( IO_ReadOnly );
		const KArchiveDirectory* dir = kre->directory();
		TQString name;
		TQStringList fileList = dir->entries();
		for ( TQStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it ) {
			if ( ( *it ).right( 6 ) == ".kreml" ) {
				name = *it;
			}
		}
		if ( name.isEmpty() ) {
			kdDebug() << "error: Archive doesn't contain a valid Krecipes file" << endl;
			setErrorMsg( i18n( "Archive does not contain a valid Krecipes file" ) );
			return ;
		}
		TQString tmp_dir = locateLocal( "tmp", "" );
		dir->copyTo( tmp_dir );
		file = new TQFile( tmp_dir + name );
		kre->close();
		unlink = true; //remove file after import
	}
	else {
		file = new TQFile( filename );
	}

	if ( file->open( IO_ReadOnly ) ) {
		kdDebug() << "file opened" << endl;
		TQDomDocument doc;
		TQString error;
		int line;
		int column;
		if ( !doc.setContent( file, &error, &line, &column ) ) {
			kdDebug() << TQString( "error: \"%1\" at line %2, column %3" ).arg( error ).arg( line ).arg( column ) << endl;
			setErrorMsg( TQString( i18n( "\"%1\" at line %2, column %3" ) ).arg( error ).arg( line ).arg( column ) );
			return ;
		}

		TQDomElement kreml = doc.documentElement();

		if ( kreml.tagName() != "krecipes" ) {
			setErrorMsg( i18n( "This file does not appear to be a *.kreml file" ) );
			return ;
		}

		// TODO Check if there are changes between versions
		TQString kreVersion = kreml.attribute( "version" );
		kdDebug() << TQString( i18n( "KreML version %1" ) ).arg( kreVersion ) << endl;

		TQDomNodeList r = kreml.childNodes();
		TQDomElement krecipe;

		for ( unsigned z = 0; z < r.count(); z++ ) {
			krecipe = r.item( z ).toElement();
			TQDomNodeList l = krecipe.childNodes();
			if ( krecipe.tagName() == "krecipes-recipe" ) {
				Recipe recipe;
				for ( unsigned i = 0; i < l.count(); i++ ) {
					TQDomElement el = l.item( i ).toElement();
					if ( el.tagName() == "krecipes-description" ) {
						readDescription( el.childNodes(), &recipe );
					}
					if ( el.tagName() == "krecipes-ingredients" ) {
						readIngredients( el.childNodes(), &recipe );
					}
					if ( el.tagName() == "krecipes-instructions" ) {
						recipe.instructions = el.text().stripWhiteSpace();
					}
					if ( el.tagName() == "krecipes-ratings" ) {
						readRatings( el.childNodes(), &recipe );
					}
				}
				add
					( recipe );
			}
			else if ( krecipe.tagName() == "krecipes-category-structure" ) {
				CategoryTree * tree = new CategoryTree;
				readCategoryStructure( l, tree );
				setCategoryStructure( tree );
			}
		}
	}
	if ( unlink ) {
		file->remove
		();
	}
}

KreImporter::~KreImporter()
{
}

void KreImporter::readCategoryStructure( const TQDomNodeList& l, CategoryTree *tree )
{
	for ( unsigned i = 0; i < l.count(); i++ ) {
		TQDomElement el = l.item( i ).toElement();

		TQString category = el.attribute( "name" );
		CategoryTree *child_node = tree->add
		                           ( Element( category ) );
		readCategoryStructure( el.childNodes(), child_node );
	}
}

void KreImporter::readDescription( const TQDomNodeList& l, Recipe *recipe )
{
	for ( unsigned i = 0; i < l.count(); i++ ) {
		TQDomElement el = l.item( i ).toElement();
		if ( el.tagName() == "title" ) {
			recipe->title = el.text();
			kdDebug() << "Found title: " << recipe->title << endl;
		}
		else if ( el.tagName() == "author" ) {
			kdDebug() << "Found author: " << el.text() << endl;
			recipe->authorList.append( Element( el.text() ) );
		}
		else if ( el.tagName() == "serving" ) { //### Keep for < 0.9 compatibility
			recipe->yield.amount = el.text().toInt();
		}
		else if ( el.tagName() == "yield" ) {
			TQDomNodeList yield_children = el.childNodes();
			for ( unsigned j = 0; j < yield_children.count(); j++ ) {
				TQDomElement y = yield_children.item( j ).toElement();
				if ( y.tagName() == "amount" )
					readAmount(y,recipe->yield.amount,recipe->yield.amount_offset);
				else if ( y.tagName() == "type" )
					recipe->yield.type = y.text();
			}
		}
		else if ( el.tagName() == "preparation-time" ) {
			recipe->prepTime = TQTime::fromString( el.text() );
		}
		else if ( el.tagName() == "category" ) {
			TQDomNodeList categories = el.childNodes();
			for ( unsigned j = 0; j < categories.count(); j++ ) {
				TQDomElement c = categories.item( j ).toElement();
				if ( c.tagName() == "cat" ) {
					kdDebug() << "Found category: " << TQString( c.text() ).stripWhiteSpace() << endl;
					recipe->categoryList.append( Element( TQString( c.text() ).stripWhiteSpace() ) );
				}
			}
		}
		else if ( el.tagName() == "pictures" ) {
			if ( el.hasChildNodes() ) {
				TQDomNodeList pictures = el.childNodes();
				for ( unsigned j = 0; j < pictures.count(); j++ ) {
					TQDomElement pic = pictures.item( j ).toElement();
					TQCString decodedPic;
					if ( pic.tagName() == "pic" )
						kdDebug() << "Found photo" << endl;
					TQPixmap pix;
					KCodecs::base64Decode( TQCString( pic.text().latin1() ), decodedPic );
					int len = decodedPic.size();
					TQByteArray picData( len );
					memcpy( picData.data(), decodedPic.data(), len );
					bool ok = pix.loadFromData( picData, "JPEG" );
					if ( ok ) {
						recipe->photo = pix;
					}
				}
			}
		}
	}
}

void KreImporter::readIngredients( const TQDomNodeList& l, Recipe *recipe, const TQString &header, Ingredient *ing )
{
	for ( unsigned i = 0; i < l.count(); i++ ) {
		TQDomElement el = l.item( i ).toElement();
		if ( el.tagName() == "ingredient" ) {
			TQDomNodeList ingredient = el.childNodes();
			Ingredient new_ing;
			for ( unsigned j = 0; j < ingredient.count(); j++ ) {
				TQDomElement ing = ingredient.item( j ).toElement();
				if ( ing.tagName() == "name" ) {
					new_ing.name = TQString( ing.text() ).stripWhiteSpace();
					kdDebug() << "Found ingredient: " << new_ing.name << endl;
				}
				else if ( ing.tagName() == "amount" ) {
					readAmount(ing,new_ing.amount,new_ing.amount_offset);
				}
				else if ( ing.tagName() == "unit" ) {
					new_ing.units = Unit( ing.text().stripWhiteSpace(), new_ing.amount+new_ing.amount_offset );
				}
				else if ( ing.tagName() == "prep" ) {
					new_ing.prepMethodList = ElementList::split(",",TQString( ing.text() ).stripWhiteSpace());
				}
				else if ( ing.tagName() == "substitutes" ) {
					readIngredients(ing.childNodes(), recipe, header, &new_ing);
				}
			}
			new_ing.group = header;

			if ( !ing )
				recipe->ingList.append( new_ing );
			else
				ing->substitutes.append( new_ing );
		}
		else if ( el.tagName() == "ingredient-group" ) {
			readIngredients( el.childNodes(), recipe, el.attribute( "name" ) );
		}
	}
}

void KreImporter::readAmount( const TQDomElement& amountEl, double &amount, double &amount_offset )
{
	TQDomNodeList children = amountEl.childNodes();

	double min = 0,max = 0;
	for ( unsigned i = 0; i < children.count(); i++ ) {
		TQDomElement child = children.item( i ).toElement();
		if ( child.tagName() == "min" ) {
			min = ( TQString( child.text() ).stripWhiteSpace() ).toDouble();
		}
		else if ( child.tagName() == "max" )
			max = ( TQString( child.text() ).stripWhiteSpace() ).toDouble();
		else if ( child.tagName().isEmpty() )
			min = ( TQString( amountEl.text() ).stripWhiteSpace() ).toDouble();
	}

	amount = min;
	if ( max > 0 )
		amount_offset = max-min;
}

void KreImporter::readRatings( const TQDomNodeList& l, Recipe *recipe )
{
	for ( unsigned i = 0; i < l.count(); i++ ) {
		TQDomElement child = l.item( i ).toElement();
		if ( child.tagName() == "rating" ) {
			Rating r;

			TQDomNodeList ratingChildren = child.childNodes();
			for ( unsigned j = 0; j < ratingChildren.count(); j++ ) {
				TQDomElement ratingChild = ratingChildren.item( j ).toElement();
				if ( ratingChild.tagName() == "comment" ) {
					r.comment = ratingChild.text();
				}
				else if ( ratingChild.tagName() == "rater" ) {
					r.rater = ratingChild.text();
				}
				else if ( ratingChild.tagName() == "criterion" ) {
					readCriterion(ratingChild.childNodes(),r.ratingCriteriaList);
				}
			}
			recipe->ratingList.append(r);
		}
	}
}

void KreImporter::readCriterion( const TQDomNodeList& l, RatingCriteriaList &rc_list )
{
	for ( unsigned i = 0; i < l.count(); i++ ) {
		TQDomElement child = l.item( i ).toElement();

		if ( child.tagName() == "criteria" ) {
			RatingCriteria rc;

			TQDomNodeList criteriaChildren = child.childNodes();
			for ( unsigned j = 0; j < criteriaChildren.count(); j++ ) {
				TQDomElement criteriaChild = criteriaChildren.item( j ).toElement();
		
				if ( criteriaChild.tagName() == "name" ) {
					rc.name = criteriaChild.text();
				}
				else if ( criteriaChild.tagName() == "stars" ) {
					rc.stars = criteriaChild.text().toDouble();
				}
			}
			rc_list.append(rc);
		}
	}
}