/***************************************************************************
*   Copyright (C) 2003 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 "rezkonvimporter.h"

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

#include <tqfile.h>
#include <tqregexp.h>
#include <tqtextstream.h>

#include "datablocks/mixednumber.h"

RezkonvImporter::RezkonvImporter() : BaseImporter()
{}

RezkonvImporter::~RezkonvImporter()
{}

void RezkonvImporter::parseFile( const TQString &filename )
{
	TQFile input( filename );

	if ( input.open( IO_ReadOnly ) ) {
		TQTextStream stream( &input );
		stream.skipWhiteSpace();

		TQString line;

		while ( !stream.atEnd() ) {
			line = stream.readLine();

			if ( line.contains( TQRegExp( "^=====.*REZKONV.*" ) ) ) {
				TQStringList raw_recipe;
				while ( !( line = stream.readLine() ).contains( TQRegExp( "^=====\\s*$" ) ) && !stream.atEnd() )
					raw_recipe << line;

				readRecipe( raw_recipe );
			}
		}

		if ( fileRecipeCount() == 0 )
			setErrorMsg( i18n( "No recipes found in this file." ) );
	}
	else
		setErrorMsg( i18n( "Unable to open file." ) );
}

void RezkonvImporter::readRecipe( const TQStringList &raw_recipe )
{
	tdeApp->processEvents(); //don't want the user to think its frozen... especially for files with thousands of recipes

	Recipe recipe;

	TQStringList::const_iterator text_it = raw_recipe.begin();
	m_end_it = raw_recipe.end();

	//title (Titel)
	text_it++;
	recipe.title = ( *text_it ).mid( ( *text_it ).find( ":" ) + 1, ( *text_it ).length() ).stripWhiteSpace();
	kdDebug() << "Found title: " << recipe.title << endl;

	//categories (Kategorien):
	text_it++;
	TQStringList categories = TQStringList::split( ',', ( *text_it ).mid( ( *text_it ).find( ":" ) + 1, ( *text_it ).length() ) );
	for ( TQStringList::const_iterator it = categories.begin(); it != categories.end(); ++it ) {
		Element new_cat;
		new_cat.name = TQString( *it ).stripWhiteSpace();
		kdDebug() << "Found category: " << new_cat.name << endl;
		recipe.categoryList.append( new_cat );
	}

	//yield (Menge)
	text_it++;
	//get the number between the ":" and the next space after it
	TQString yield_str = ( *text_it ).stripWhiteSpace();
	yield_str.remove( TQRegExp( "^Menge:\\s*" ) );
	int sep_index = yield_str.find( ' ' );
	if ( sep_index != -1 )
		recipe.yield.type = yield_str.mid( sep_index+1 );
	readRange( yield_str.mid( 0, sep_index ), recipe.yield.amount, recipe.yield.amount_offset );
	kdDebug() << "Found yield: " << recipe.yield.amount << endl;

	bool is_sub = false;
	bool last_line_empty = false;
	text_it++;
	while ( text_it != raw_recipe.end() ) {
		if ( ( *text_it ).isEmpty() ) {
			last_line_empty = true;
			text_it++;
			continue;
		}

		if ( ( *text_it ).contains( TQRegExp( "^=====.*=$" ) ) )  //is a header
		{
			if ( ( *text_it ).contains( "quelle", false ) )
			{
				loadReferences( text_it, recipe );
				break; //reference lines are the last before the instructions
			}
			else
				loadIngredientHeader( *text_it, recipe );
		}

		//if it has no more than two spaces followed by a non-digit
		//then we'll assume it is a direction line
		else if ( last_line_empty && ( *text_it ).contains( TQRegExp( "^\\s{0,2}[^\\d\\s=]" ) ) )
			break;
		else
			loadIngredient( *text_it, recipe, is_sub );

		last_line_empty = false;
		text_it++;
	}

	loadInstructions( text_it, recipe );

	add
		( recipe );

	current_header = TQString::null;
}

void RezkonvImporter::loadIngredient( const TQString &string, Recipe &recipe, bool &is_sub )
{
	Ingredient new_ingredient;
	new_ingredient.amount = 0; //amount not required, so give default of 0

	TQRegExp cont_test( "^-{1,2}" );
	if ( string.stripWhiteSpace().contains( cont_test ) ) {
		TQString name = string.stripWhiteSpace();
		name.remove( cont_test );
		kdDebug() << "Appending to last ingredient: " << name << endl;
		if ( !recipe.ingList.isEmpty() )  //so it doesn't crash when the first ingredient appears to be a continuation of another
			( *recipe.ingList.at( recipe.ingList.count() - 1 ) ).name += " " + name;

		return ;
	}

	//amount
	if ( !string.mid( 0, 7 ).stripWhiteSpace().isEmpty() )
		readRange( string.mid( 0, 7 ), new_ingredient.amount, new_ingredient.amount_offset );

	//unit
	TQString unit_str = string.mid( 8, 9 ).stripWhiteSpace();
	new_ingredient.units = Unit( unit_str, new_ingredient.amount );

	//name and preparation method
	new_ingredient.name = string.mid( 18, string.length() - 18 ).stripWhiteSpace();

	//separate out the preparation method
	TQString name_and_prep = new_ingredient.name;
	int separator_index = name_and_prep.find( "," );
	if ( separator_index != -1 ) {
		new_ingredient.name = name_and_prep.mid( 0, separator_index ).stripWhiteSpace();
		new_ingredient.prepMethodList = ElementList::split(",",name_and_prep.mid( separator_index + 1, name_and_prep.length() ).stripWhiteSpace() );
	}

	//header (if present)
	new_ingredient.group = current_header;

	bool last_is_sub = is_sub;
	if ( new_ingredient.prepMethodList.last().name == "or" ) {
		new_ingredient.prepMethodList.pop_back();
		is_sub = true;
	}
	else
		is_sub = false;

	if ( last_is_sub )
		( *recipe.ingList.at( recipe.ingList.count() - 1 ) ).substitutes.append(new_ingredient);
	else
		recipe.ingList.append( new_ingredient );
}

void RezkonvImporter::loadIngredientHeader( const TQString &string, Recipe &/*recipe*/ )
{
	TQString header = string;
	header.remove( TQRegExp( "^=*" ) ).remove( TQRegExp( "=*$" ) );
	header = header.stripWhiteSpace();

	kdDebug() << "found ingredient header: " << header << endl;

	current_header = header;
}

void RezkonvImporter::loadInstructions( TQStringList::const_iterator &text_it, Recipe &recipe )
{
	TQString instr;
	TQRegExp rx_title( "^:{0,1}\\s*O-Titel\\s*:" );
	TQString line;
	text_it++;
	while ( text_it != m_end_it ) {
		line = *text_it;

		//titles longer than the line width are rewritten here
		if ( line.contains( rx_title ) ) {
			line.remove( rx_title );
			recipe.title = line.stripWhiteSpace();

			TQRegExp rx_line_cont( ":\\s*>{0,1}\\s*:" );
			while ( ( line = *text_it ).contains( rx_line_cont ) ) {
				line.remove( rx_line_cont );
				recipe.title += line;

				text_it++;
			}
			kdDebug() << "Found long title: " << recipe.title << endl;
		}
		else {
			instr += line.stripWhiteSpace();
		}
		text_it++;
	}

	recipe.instructions = instr;
}

void RezkonvImporter::loadReferences( TQStringList::const_iterator &text_it, Recipe &recipe )
{
	kdDebug() << "Found source header" << endl;

	while ( text_it != m_end_it ) {
		text_it++;
		TQRegExp rx_line_begin( "^\\s*-{0,2}\\s*" );

		TQRegExp rx_creation_date = TQRegExp( "^\\s*-{0,2}\\s*Erfasst \\*RK\\*", false );
		if ( ( *text_it ).contains( rx_creation_date ) )  // date followed by typist
		{
			TQString date = *text_it;
			date.remove( rx_creation_date ).remove( TQRegExp( " von\\s*$" ) );

			// Date is given as DD.MM.YY
			TQString s = date.section( ".", 0, 0 );
			int day = s.toInt();

			s = date.section( ".", 1, 1 );
			int month = s.toInt();

			s = date.section( ".", 2, 2 );
			int year = s.toInt();
			year += 1900;
			if ( year < 1970 )
				year += 100; //we'll assume nothing has been created before 1970 (y2k issues :p)

			recipe.ctime = TQDate(year,month,day);

			#if 0
			//typist
			text_it++;
			TQString typist = = *text_it;
			typist.remove( rx_line_begin );
			#endif

		}
		else //everything else is an author
		{
			if ( ( *text_it ).contains( rx_line_begin ) ) {
				TQString author = *text_it;
				author.remove( rx_line_begin );

				recipe.authorList.append( Element( author ) );
			}
			else
				break;
		}
	}
}

void RezkonvImporter::readRange( const TQString &range_str, double &amount, double &amount_offset )
{
	TQString from = range_str.section( '-', 0, 0 );
	TQString to   = range_str.section( '-', 1, 1 );

	amount = MixedNumber::fromString( from, nullptr, /* locale_aware = */ false ).toDouble();

	if ( !to.stripWhiteSpace().isEmpty() )
		amount_offset = MixedNumber::fromString( to, nullptr, /* locale_aware = */ false ).toDouble() - amount;
}
