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

#include <tdeapplication.h>
#include <tdeconfig.h>
#include <kdebug.h>
#include <tdefontdialog.h>
#include <kcolordialog.h>
#include <tdelocale.h>
#include <tdepopupmenu.h>
#include <kiconloader.h>
#include <tdestandarddirs.h>
#include <tdetempfile.h>

#include <tdehtmlview.h>
#include <dom/dom_doc.h>
#include <dom/css_rule.h>

#include <tqinputdialog.h>
#include <tqaction.h>
#include <tqlabel.h>
#include <tqfile.h>
#include <tqregexp.h>
#include <tqtextedit.h>
#include <tqtooltip.h>
#include <tqobjectlist.h>
#include <tqvaluelist.h>
#include <tqlayout.h>

#include "datablocks/mixednumber.h"
#include "dialogs/borderdialog.h"
#include "exporters/htmlexporter.h"

#include <cmath>

KreDisplayItem::KreDisplayItem( const TQString &n, const TQString &_name ) : nodeId(n), name(_name)
{
	clear();
}

void KreDisplayItem::clear()
{
	alignment = TQt::AlignHCenter;
	show = true;
	backgroundColor = TQColor(255,255,255);
	textColor = TQColor(0,0,0);
	columns = 1;
}

SetupDisplay::SetupDisplay( const Recipe &sample, TQWidget *parent ) : TDEHTMLPart(parent),
		box_properties( new PropertiesMap ),
		node_item_map( new TQMap<TQString, KreDisplayItem*> ),
		has_changes( false ),
		popup(0)
{
	connect( this, TQ_SIGNAL( popupMenu(const TQString &,const TQPoint &) ), TQ_SLOT( nodeClicked(const TQString &,const TQPoint &) ) );

	if ( sample.recipeID != -1 )
		m_sample = sample;
	else {
		m_sample.title = i18n("Recipe Title");
 		m_sample.yield.amount = 0;
		m_sample.categoryList.append( Element(i18n( "Category 1, Category 2, ..." ) ) );
		m_sample.instructions = i18n("Instructions");
		m_sample.prepTime = TQTime(0,0);
	
		m_sample.authorList.append( Element(i18n( "Author 1, Author 2, ..." )) );
	
		Ingredient ing;
		ing.name = i18n("Ingredient 1");
		m_sample.ingList.append( ing );
	
		ing.name = i18n("Ingredient 2");
		m_sample.ingList.append( ing );

		ing.name = "...";
		m_sample.ingList.append( ing );
	
		RatingCriteria rc;
		Rating rating1;
		rating1.rater = i18n("Rater");
		rating1.comment = i18n("Comment");
	
		rc.name = i18n("Criteria 1");
		rc.stars = 5.0;
		rating1.append(rc);
	
		rc.name = i18n("Criteria 2");
		rc.stars = 2.5;
		rating1.append(rc);

		IngredientProperty prop;
		prop.name = i18n("Property 1");
		m_sample.properties.append(prop);
		prop.name = i18n("Property 2");
		m_sample.properties.append(prop);
		prop.name = "...";
		m_sample.properties.append(prop);

		m_sample.ratingList.append(rating1);
	}

	kdDebug()<<"first load"<<endl;
	loadHTMLView();
	show();

	createItem( "background", i18n("Background"), BackgroundColor );
	createItem( "title", i18n("Title"), Font | BackgroundColor | TextColor | Visibility | Alignment | Border );
	createItem( "instructions", i18n("Instructions"), Font | BackgroundColor | TextColor | Visibility | Alignment | Border );
	createItem( "prep_time", i18n("Preparation Time"), Font | BackgroundColor | TextColor | Visibility | Alignment | Border );
	createItem( "photo", i18n("Photo"), Visibility | Border );
	createItem( "authors", i18n("Authors"), Font | BackgroundColor | TextColor | Visibility | Alignment | Border );
	createItem( "categories", i18n("Categories"), Font | BackgroundColor | TextColor | Visibility | Alignment | Border );
	createItem( "ingredients", i18n("Ingredients"), Font | BackgroundColor | TextColor | Visibility | Alignment | Border | Columns );
	createItem( "properties", i18n("Properties"), Font | BackgroundColor | TextColor | Visibility | Alignment | Border | Columns );
	createItem( "ratings", i18n("Ratings"), Font | BackgroundColor | TextColor | Visibility | Alignment | Border );
	createItem( "yield", i18n("Yield"), Font | BackgroundColor | TextColor | Visibility | Alignment | Border );
}

SetupDisplay::~SetupDisplay()
{
	delete box_properties;
	delete node_item_map;
}

void SetupDisplay::loadHTMLView( const TQString &templateFile, const TQString &styleFile )
{
	kdDebug()<<"loading template: "<<templateFile<<" style: "<<styleFile<<endl;
	TQString tmp_filename = locateLocal( "tmp", "krecipes_recipe_view" );
	HTMLExporter exporter( tmp_filename + ".html", "html" );
	if ( templateFile != TQString::null )
		exporter.setTemplate( templateFile );
	if ( styleFile != TQString::null )
		exporter.setStyle( styleFile );

	RecipeList recipeList;
	recipeList.append(m_sample);

	TQFile file(tmp_filename + ".html");
	if ( file.open( IO_WriteOnly ) ) {
		TQTextStream stream(&file);
		exporter.writeStream(stream,recipeList);
	}
	else {
		kdDebug()<<"Unable to open file for writing"<<endl;
	}
	file.close();

	KURL url;
	url.setPath( tmp_filename + ".html" );
	openURL( url );
	kdDebug() << "Opening URL: " << url.htmlURL() << endl;
}

void SetupDisplay::reload()
{
	loadHTMLView( m_activeTemplate, m_activeStyle );
}

void SetupDisplay::loadTemplate( const TQString &filename )
{
	bool storeChangedState = has_changes;
	KTempFile tmpFile;
	saveLayout(tmpFile.name());
	has_changes = storeChangedState; //saveLayout() sets changes to false
	
	loadHTMLView( filename, tmpFile.name() );

	m_activeTemplate = filename;
}

void SetupDisplay::createItem( const TQString &node, const TQString &name, unsigned int properties )
{
	KreDisplayItem * item = new KreDisplayItem( node, name );
	box_properties->insert( item, properties );
	node_item_map->insert( node, item );
}

void SetupDisplay::loadLayout( const TQString &filename )
{
	TQFile input( filename );
	if ( input.open( IO_ReadOnly ) ) {
		TQDomDocument doc;
		TQString error;
		int line;
		int column;
		if ( !doc.setContent( &input, &error, &line, &column ) ) {
			kdDebug() << TQString( i18n( "\"%1\" at line %2, column %3.  This may not be a Krecipes layout file." ) ).arg( error ).arg( line ).arg( column ) << endl;
			return ;
		}

		m_styleSheet = DOM::CSSStyleSheet();

		TQMap<TQString,KreDisplayItem*>::iterator it;
		for ( it = node_item_map->begin(); it != node_item_map->end(); ++it ) {
			it.data()->clear();
		}
		processDocument( doc );

		loadHTMLView(m_activeTemplate, filename);
		m_activeStyle = filename;

		has_changes = false;
	}
	else
		kdDebug() << "Unable to open file: " << filename << endl;
}

void SetupDisplay::beginObject( const TQString &object )
{
	TQMap<TQString, KreDisplayItem*>::iterator map_it = node_item_map->find( object );
	if ( map_it != node_item_map->end() )
		m_currentItem = map_it.data();
	else
		m_currentItem = 0;
}

void SetupDisplay::endObject()
{
	m_currentItem = 0;
}

void SetupDisplay::loadBackgroundColor( const TQString &object, const TQColor &color )
{
	if ( m_currentItem ) {
		m_currentItem->backgroundColor = color;
		m_styleSheet.insertRule("."+object+" { "+bgColorAsCSS(color)+" }",m_styleSheet.cssRules().length());
	}
}

void SetupDisplay::loadFont( const TQString &object, const TQFont &font )
{
	if ( m_currentItem ) {
		m_currentItem->font = font;
		m_styleSheet.insertRule("."+object+" { "+fontAsCSS(font)+" }",m_styleSheet.cssRules().length());
	}
}

void SetupDisplay::loadTextColor( const TQString &object, const TQColor &color )
{
	if ( m_currentItem ) {
		m_currentItem->textColor = color;
		m_styleSheet.insertRule("."+object+" { "+textColorAsCSS(color)+" }",m_styleSheet.cssRules().length());
	}
}

void SetupDisplay::loadVisibility( const TQString &object, bool visible )
{
	if ( m_currentItem ) {
		m_currentItem->show = visible;
		emit itemVisibilityChanged( m_currentItem, visible );

		m_styleSheet.insertRule("."+object+" { "+visibilityAsCSS(visible)+" }",m_styleSheet.cssRules().length());
	}
}

void SetupDisplay::loadAlignment( const TQString &object, int alignment )
{
	if ( m_currentItem ) {
		m_currentItem->alignment = alignment;
		m_styleSheet.insertRule("."+object+" { "+alignmentAsCSS(alignment)+" }",m_styleSheet.cssRules().length());
	}
}

void SetupDisplay::loadBorder( const TQString &object, const KreBorder& border )
{
	if ( m_currentItem ) {
		m_currentItem->border = border;
		m_styleSheet.insertRule("."+object+" { "+borderAsCSS(border)+" }",m_styleSheet.cssRules().length());
	}
}

void SetupDisplay::loadColumns( const TQString &/*object*/, int cols )
{
	if ( m_currentItem ) {
		m_currentItem->columns = cols;
	}
}

void SetupDisplay::saveLayout( const TQString &filename )
{
	TQDomImplementation dom_imp;
	TQDomDocument doc = dom_imp.createDocument( TQString::null, "krecipes-layout", dom_imp.createDocumentType( "krecipes-layout", TQString::null, TQString::null ) );

	TQDomElement layout_tag = doc.documentElement();
	layout_tag.setAttribute( "version", 0.4 );
	//layout_tag.setAttribute( "generator", TQString("Krecipes v%1").arg(krecipes_version()) );
	doc.appendChild( layout_tag );

	for ( TQMap<TQString, KreDisplayItem*>::const_iterator it = node_item_map->begin(); it != node_item_map->end(); ++it ) {
		TQDomElement base_tag = doc.createElement( it.key() );
		layout_tag.appendChild( base_tag );

		int properties = (*box_properties)[it.data()];
		if ( properties & BackgroundColor ) {
			TQDomElement backgroundcolor_tag = doc.createElement( "background-color" );
			backgroundcolor_tag.appendChild( doc.createTextNode( it.data()->backgroundColor.name() ) );
			base_tag.appendChild( backgroundcolor_tag );
		}

		if ( properties & TextColor ) {
			TQDomElement textcolor_tag = doc.createElement( "text-color" );
			textcolor_tag.appendChild( doc.createTextNode( it.data()->textColor.name() ) );
			base_tag.appendChild( textcolor_tag );
		}

		if ( properties & Font ) {
			TQDomElement font_tag = doc.createElement( "font" );
			font_tag.appendChild( doc.createTextNode( it.data()->font.toString() ) );
			base_tag.appendChild( font_tag );
		}

		if ( properties & Visibility ) {
			TQDomElement visibility_tag = doc.createElement( "visible" );
			visibility_tag.appendChild( doc.createTextNode( (it.data()->show) ? "true" : "false" ) );
			base_tag.appendChild( visibility_tag );
		}

		if ( properties & Alignment ) {
			TQDomElement alignment_tag = doc.createElement( "alignment" );
			alignment_tag.appendChild( doc.createTextNode( TQString::number( it.data()->alignment ) ) );
			base_tag.appendChild( alignment_tag );
		}

		if ( properties & Border ) {
			TQDomElement border_tag = doc.createElement( "border" );
			border_tag.setAttribute( "width", it.data()->border.width );
			border_tag.setAttribute( "style", it.data()->border.style );
			border_tag.setAttribute( "color", it.data()->border.color.name() );
			base_tag.appendChild( border_tag );
		}

		if ( properties & Columns ) {
			TQDomElement columns_tag = doc.createElement( "columns" );
			columns_tag.appendChild( doc.createTextNode( TQString::number( it.data()->columns ) ) );
			base_tag.appendChild( columns_tag );
		}
	}

	TQFile out_file( filename );
	if ( out_file.open( IO_WriteOnly ) ) {
		has_changes = false;

		TQTextStream stream( &out_file );
		stream << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" << doc.toString();
	}
	else
		kdDebug() << "Error: Unable to write to file " << filename << endl;
}

void SetupDisplay::begin(const KURL &url, int xOffset, int yOffset)
{
	kdDebug()<<"begin"<<endl;
	TDEHTMLPart::begin(url,xOffset,yOffset);
	kdDebug()<<"end"<<endl;

	DOM::Document doc = document();
	DOM::DOMImplementation impl = doc.implementation();
	kdDebug() << "(1) document: " << document().handle() << endl;
	#if 0
	if ( !impl.isNull() ) {
		//m_styleSheet = impl.createCSSStyleSheet("-krecipes","screen");
		//m_styleSheet = DOM::CSSStyleSheet();
		//doc.addStyleSheet(m_styleSheet);
		//applyStylesheet();
	}
	#endif
}

void SetupDisplay::nodeClicked(const TQString &/*url*/,const TQPoint &point)
{
	DOM::Node node = nodeUnderMouse();
	DOM::Element element;
	if ( node.nodeType() != DOM::Node::ELEMENT_NODE ) {
		kdDebug()<<"not an element"<<endl;
		element = (DOM::Element)node.parentNode();
	}
	else
		element = (DOM::Element)node;

	while ( !element.parentNode().isNull() ) {
		if ( element.hasAttribute("class") ) {
			TQString id = element.getAttribute("class").string();
			if ( node_item_map->keys().contains(id) )
				break;
		}

		element = (DOM::Element)element.parentNode();
	}

	m_currNodeId = element.getAttribute("class").string();
	if ( m_currNodeId.isEmpty() ) {
		kdDebug()<<"Code error: unable to determine class of selected element"<<endl;
		return;
	}

	KreDisplayItem *item = *node_item_map->find( m_currNodeId );
	
	delete popup;
	popup = new TDEPopupMenu( view() );
	popup->insertTitle( item->name );

	unsigned int properties = 0;
	for ( PropertiesMap::const_iterator it = box_properties->begin(); it != box_properties->end(); ++it ) {
		if ( it.key()->nodeId == m_currNodeId ) {
			properties = it.data();
			break;
		}
	}

	TDEIconLoader il;

	if ( properties & BackgroundColor )
		popup->insertItem( i18n( "Background Color..." ), this, TQ_SLOT( setBackgroundColor() ) );

	if ( properties & TextColor )
		popup->insertItem( i18n( "Text Color..." ), this, TQ_SLOT( setTextColor() ) );

	if ( properties & Font )
		popup->insertItem( il.loadIconSet( "text", TDEIcon::Small, 16 ), i18n( "Font..." ), this, TQ_SLOT( setFont() ) );

	if ( properties & Visibility ) {
		int id = popup->insertItem( i18n( "Show" ), this, TQ_SLOT( setShown( int ) ) );
		popup->setItemChecked( id, item->show );
	}

	if ( properties & Alignment ) {
		TQPopupMenu * sub_popup = new TQPopupMenu( popup );

		TQActionGroup *alignment_actions = new TQActionGroup( this );
		alignment_actions->setExclusive( true );

		TQAction *c_action = new TQAction( i18n( "Center" ), i18n( "Center" ), 0, alignment_actions, 0, true );
		TQAction *l_action = new TQAction( i18n( "Left" ), i18n( "Left" ), 0, alignment_actions, 0, true );
		TQAction *r_action = new TQAction( i18n( "Right" ), i18n( "Right" ), 0, alignment_actions, 0, true );

		int align = item->alignment;
		if ( align & TQt::AlignHCenter )
			c_action->setOn(true);
		if ( align & TQt::AlignLeft )
			l_action->setOn(true);
		if ( align & TQt::AlignRight )
			r_action->setOn(true);

		connect( alignment_actions, TQ_SIGNAL( selected( TQAction* ) ), TQ_SLOT( setAlignment( TQAction* ) ) );

		popup->insertItem( i18n( "Alignment" ), sub_popup );

		alignment_actions->addTo( sub_popup );
	}

	if ( properties & Border )
		popup->insertItem( i18n( "Border..." ), this, TQ_SLOT( setBorder() ) );

	if ( properties & Columns )
		popup->insertItem( i18n( "Columns..." ), this, TQ_SLOT( setColumns() ) );

	popup->popup( point );
}

void SetupDisplay::applyStylesheet()
{
	loadTemplate( m_activeTemplate );
	if ( !document().isNull() && !m_styleSheet.isNull() ) {
		//document().removeStyleSheet(m_styleSheet);
		//document().addStyleSheet(m_styleSheet);
	}
}

void SetupDisplay::setBackgroundColor()
{
	KreDisplayItem *item = *node_item_map->find( m_currNodeId );
	if ( KColorDialog::getColor( item->backgroundColor, view() ) == TQDialog::Accepted ) {
		m_currentItem = item;
		loadBackgroundColor(m_currNodeId,item->backgroundColor);
		m_currentItem = 0;

		applyStylesheet();
		has_changes = true;
	}
}

void SetupDisplay::setBorder()
{
	KreDisplayItem *item = *node_item_map->find( m_currNodeId );
	BorderDialog borderDialog( item->border, view() );
	if ( borderDialog.exec() == TQDialog::Accepted ) {
		m_currentItem = item;
		loadBorder( m_currNodeId, borderDialog.border() );
		m_currentItem = 0;

		applyStylesheet();
		has_changes = true;
	}
}

void SetupDisplay::setColumns()
{
	KreDisplayItem *item = *node_item_map->find( m_currNodeId );
	int cols = TQInputDialog::getInteger( TQString::null, i18n("Select the number of columns to use:"), item->columns, 1, 100, 1, 0, view() );
	if ( cols > 0 ) {
		m_currentItem = item;
		loadColumns( m_currNodeId, cols );
		m_currentItem = 0;

		loadTemplate( m_activeTemplate );
		has_changes = true;
	}
}

void SetupDisplay::setTextColor()
{
	KreDisplayItem *item = *node_item_map->find( m_currNodeId );
	if ( KColorDialog::getColor( item->textColor, view() ) == TQDialog::Accepted ) {
		m_currentItem = item;
		loadTextColor(m_currNodeId,item->textColor);
		m_currentItem = 0;

		applyStylesheet();
		has_changes = true;
	}
}

void SetupDisplay::setShown( int id )
{
	KreDisplayItem *item = *node_item_map->find( m_currNodeId );
	emit itemVisibilityChanged( item, !popup->isItemChecked( id ) );

	m_currentItem = item;
	loadVisibility(m_currNodeId,!popup->isItemChecked( id ));
	m_currentItem = 0;

	applyStylesheet();
	has_changes = true;
}

void SetupDisplay::setFont()
{
	KreDisplayItem *item = *node_item_map->find( m_currNodeId );
	if ( TDEFontDialog::getFont( item->font, false, view() ) == TQDialog::Accepted ) {
		m_currentItem = item;
		loadFont(m_currNodeId,item->font);
		m_currentItem = 0;

		applyStylesheet();
		has_changes = true;
	}
}

void SetupDisplay::setAlignment( TQAction *action )
{
	KreDisplayItem *item = *node_item_map->find( m_currNodeId );

	//TODO: isn't there a simpler way to do this...
	//preserve non-horizontal alignment flags
	if ( item->alignment & TQt::AlignRight )
		item->alignment ^= TQt::AlignRight;
	if ( item->alignment & TQt::AlignHCenter )
		item->alignment ^= TQt::AlignHCenter;
	if ( item->alignment & TQt::AlignLeft )
		item->alignment ^= TQt::AlignLeft;

	if ( action->text() == i18n( "Center" ) )
		item->alignment |= TQt::AlignHCenter;
	else if ( action->text() == i18n( "Left" ) )
		item->alignment |= TQt::AlignLeft;
	else if ( action->text() == i18n( "Right" ) )
		item->alignment |= TQt::AlignRight;

	m_currentItem = item;
	loadAlignment(m_currNodeId,item->alignment);
	m_currentItem = 0;

	applyStylesheet();
	has_changes = true;
}

void SetupDisplay::setItemShown( KreDisplayItem *item, bool visible )
{
	item->show = visible;

	m_styleSheet.insertRule("."+item->nodeId+" { visibility:"+(item->show?"visible":"hidden")+" }",m_styleSheet.cssRules().length());
	applyStylesheet();

	has_changes = true;
}

void SetupDisplay::changeMade( void )
{
	has_changes = true;
}

#include "setupdisplay.moc"
