/***************************************************************************
                          kbibdoc.cpp  -  description
                             -------------------
    begin                : Sat May 24 16:37:30 EST 2003
    copyright            : (C) 2003 by Thach Nguyen
    email                : thach.nguyen@rmit.edu.au
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 <algorithm>
#include <fstream>
using namespace::std;

// include files for Qt
#include <qdir.h>
#include <qwidget.h>
#include <qstring.h>

// include files for KDE
#include <klocale.h>
#include <kmessagebox.h>
#include <kio/job.h>
#include <kio/netaccess.h>
#include <ktempfile.h>
// application specific includes
#include "kbibdoc.h"
#include "kbib.h"

#include "kbibeditentrydlg.h"

//extern vector<string> entrydefTab;

KBibDoc::KBibDoc ( QWidget *parent, const char *name ) : QObject ( parent, name )
{
	p_view = 0;
	entry_list = new BibentryTable();
	modified = false;
}

KBibDoc::~KBibDoc()
{
	delete entry_list;
	delete  p_view;
}


bool KBibDoc::saveModified()
{
	bool completed=true;

	if ( modified )
	{
		KBibApp *win= ( KBibApp * ) parent();
		int want_save = KMessageBox::warningYesNoCancel ( win,
		                i18n ( "The current file has been modified.\n"
		                       "Do you want to save it?" ),
		                i18n ( "Warning" ) );
		switch ( want_save )
		{
			case KMessageBox::Yes:
				if ( m_url.fileName() == i18n ( "Untitled" ) )
				{
					win->slotFileSaveAs();
				}
				else
				{
					saveDocument ( m_url );
				};

				deleteContents();
				completed=true;
				break;

			case KMessageBox::No:
				setModified ( false );
				deleteContents();
				completed=true;
				break;

			case KMessageBox::Cancel:
				completed=false;
				break;

			default:
				completed=false;
				break;
		}
	}

	return completed;
}

void KBibDoc::closeDocument()
{
	deleteContents();
}

bool KBibDoc::newDocument()
{
	modified=false;
	m_url.setFileName ( i18n ( "Untitled" ) );
	p_view->clearListView();
	group_view->clearGroupView();
	//entry_list->removeEntryTab();
	//entry_list->removeStringMacroTab();
	entry_list->clear();

	
	return true;
}


void KBibDoc::deleteContents()
{
	/////////////////////////////////////////////////
	// TODO: Add implementation to delete the document contents
	/////////////////////////////////////////////////


}


void KBibDoc::addListView ( KBibListView *view )
{
	p_view = view;

	//connect(p_view,SIGNAL(signalListChange()), this, SLOT(slotEntryListChange()));
}


void KBibDoc::addGroupView ( KBibGroupView *view )
{
	group_view = view;

}



bool KBibDoc::openDocument ( const KURL url, bool import )
{
	KTempFile tmpfile;
	tmpfile.setAutoDelete ( TRUE );
	QString fn = tmpfile.name();
	//	cerr << "File name " << (url.url()).ascii();
	KBibApp* parent = ( KBibApp* ) this->parent();
	if ( !KIO::NetAccess::download ( url, fn, parent ) )
	{
		return 0;

	}


	char * st =  const_cast<char*> ( fn.ascii() );

	ifstream tmp_file ( st );
	if ( !tmp_file )
	{
		return false;
	}

	tmp_file.close();
	entry_list->clear();

	if ( entry_list->readBibfile ( st, import ) ==0 )
	{
		p_view -> updateListView ( entry_list );
		group_view->addBibEntryTable ( entry_list );

		//    fileName = filename;
		m_url = url;
		modified=false;
		return true;
	}
	else
	{
		return false;
	}
}





bool KBibDoc::saveDocument ( const KURL& url )
{
	KBibApp* parent = ( KBibApp* ) this->parent();
	if ( url.isLocalFile() )
	{
		if ( entry_list->writeBibfile ( ( url.path() ).ascii(), ( parent->kbibConfigState ).fieldDelimiter ) ==0 )
		{
			m_url = url;
			modified=false;
			return true;
		}
		else
		{
			QString msg=QString ( i18n ( "Error in saving file!" ) );
			KMessageBox::error ( parent, msg );
			return false;
		}
	}
	else
	{
		KTempFile tmpfile;
		tmpfile.setAutoDelete ( TRUE );
		QString fn = tmpfile.name();
		if ( entry_list->writeBibfile ( fn.ascii(), ( parent->kbibConfigState ).fieldDelimiter ) ==0 )
		{
			bool uploaded = KIO::NetAccess::upload ( fn, url, parent );
			if ( !uploaded )
			{
				QString msg ( i18n ( "Kbib is unable to upload the file - %1." ).arg ( url.url() ) );
				KMessageBox::error ( parent, msg );
				return false;
			}
			else
			{
				m_url = url;
				modified=false;
				return true;
			}
		}
		return false;
	}

}



int KBibDoc::size ( void )
{
	return entry_list->size();
}


void KBibDoc::createKeys ( QString connectingString )
{
	entry_list->createKeys ( connectingString );
	p_view->updateCurrentListView();
	modified = true;
	KBibApp *win= ( KBibApp * ) parent();

	win->slotDocChanged();
}

int KBibDoc::searchKey ( const QString st, int start )
{
	return entry_list->searchKey ( st, start );
}

BibEntry * KBibDoc::getEntry ( int i )
{
	return entry_list->get_entry ( i );
}

BibentryTable * KBibDoc::getEntryList ( void )
{
	return entry_list;
}



int KBibDoc::search_string ( char * st, int first, int att, int opt )
{
	int i = entry_list->search_string ( st, first, att, opt );
	p_view->highlightItem ( i );
	return i;
}

void KBibDoc::sort ( int ascending, QString st )
{
	entry_list->sort ( ascending, st );
	KBibApp *win= ( KBibApp * ) parent();
	p_view->updateCurrentListView();
	modified = true;
	win->slotDocChanged();
}


int KBibDoc::removeDuplicated()
{
	KBibApp *win = ( KBibApp * ) parent();
	int compareSetting = ( win->kbibConfigState ).compareRefSetting;
	int numRemoved = entry_list->removeDuplicated ( compareSetting );

	if ( numRemoved )
	{

		modified = true;
		win->slotDocChanged();
		//p_view->updateCurrentListView();
		p_view->updateListView ( entry_list );
		win->slotNumRefMsg();
		if ( win->isGroupViewVisible() )
			group_view->createGroupView();
	}
	return numRemoved;
}


int KBibDoc::deleteEntry ( int i )
{
	if ( i < 0 || i >= this->size() )
	{
		return 0;

	}
	else
	{
		entry_list->delete_entry ( i );
		entry_list->updateEntryIndices();

		KBibApp *win = ( KBibApp * ) parent();
		win->slotNumRefMsg();
		modified = true;
		win->slotDocChanged();
		if ( win->isGroupViewVisible() )
			group_view->createGroupView();
		//p_view->updateCurrentListView();

		return 1;

	}

}

int KBibDoc::deleteEntry ( vector<int> *i )
{
	std::sort ( i->begin(), i->end() );
	int numDelete = 0;
	vector<int>::iterator p;
	int j;
	for ( p=i->begin(); p!=i->end(); p++ )
	{
		j = *p-numDelete;
		if ( j >=0 && j < this->size() )
		{
			entry_list->delete_entry ( j );
			numDelete++;
		}
	}
	entry_list->updateEntryIndices();

	if ( numDelete )
	{
		KBibApp *win = ( KBibApp * ) parent();
		win->slotNumRefMsg();

		modified = true;
		win->slotDocChanged();
		if ( win->isGroupViewVisible() )
			group_view->createGroupView();
		//p_view->updateListView(entry_list);
	}

	return numDelete;

}


void KBibDoc::paste ( QString str )
{

	if ( str.isEmpty() )
		return;
	char *st = const_cast<char*> ( str.ascii() );
	int last_char = 0;
	while ( strlen ( st ) )
	{

		BibEntry *entry = 0;
		entry = processBibEntryFromString ( st, last_char );

		if ( entry )
		{
			//Add entry to the list
			int index = this->size();
			entry->setIndex ( index );

			entry_list->new_entry ( entry );




			modified = true;

			p_view->addNewItem ( entry );
			group_view->addEntry ( entry );


		}
		st = &st[last_char+1];

	}
	if ( modified )
	{
		KBibApp *win = ( KBibApp * ) parent();
		win->slotDocChanged();
		win->slotNumRefMsg();
	}

}








bool KBibDoc::newEntry()
{
	KBibApp* win = ( KBibApp * ) parent();
	win->slotStatusMsg ( i18n ( "Insert new BibTeX entry..." ) );

	if ( BibEntryDefTable::self()->numEntryDefs() < 1 )
	{
		KMessageBox::error ( win,i18n ( "No entry type define!" ) );
		return false;

	}
	BibEntry *entry = editEntry ( 0 );
	if ( entry==0 )
	{
		win->slotStatusMsg ( i18n ( "Ready" ) );
		return false;
	}

	//Add entry to the list
	addEntry ( entry );
	win->slotStatusMsg ( i18n ( "Ready" ) );
	return true;

}


void KBibDoc::addEntry ( BibEntry *entry, bool ignoreExisting, bool fixDuplicatedKey )
{
	KBibApp* win = ( KBibApp * ) parent();

	//Check for existing entry
	int index = this->size();
	bool insert = true;
	if ( ignoreExisting )
	{
		for ( int i = 0; i < index; i++ )
		{
			BibEntry *e = getEntry ( i );
			if ( compareBibEntry ( e, entry, ( win->kbibConfigState ).compareRefSetting ) == 0 )
			{
				insert = false;
				break;
			}
		}
	}
	if ( !insert )
		return;

	//Add entry to the list
	win->slotStatusMsg ( i18n ( "Insert new BibTeX entry..." ) );
	entry->setIndex ( index );
	entry_list->new_entry ( entry );
	if ( fixDuplicatedKey )
	{
		entry_list->createKey ( index, ( ( win->kbibConfigState ).keyConnecting ) );
	}



	modified = true;
	win->slotDocChanged();
	//p_view->updateListView(entry_list);
	p_view->addNewItem ( entry );
	group_view->addEntry ( entry );
	win->slotStatusMsg ( i18n ( "Ready" ) );
	win->slotNumRefMsg();
}

bool KBibDoc::modifyEntry ( int index )
{
	BibEntry* entry = entry_list->get_entry ( index );
	BibEntry* x_entry = editEntry ( entry );
	if ( x_entry )
	{
		entry->setEntryType ( x_entry->getEntryType() );
		entry->setFields ( *x_entry );
		entry->setIndex ( index );
		entry->setKey ( x_entry->getKey() );
		delete x_entry;
		//entry->setField("author", "test");

		//entry_list->change_entry(index, x_entry);

		modified = true;
		KBibApp* win = ( KBibApp * ) parent();
		win->slotDocChanged();
		//p_view->updateListView(entry_list);
		if ( win->isGroupViewVisible() )
			group_view->createGroupView();
		return true;
	}
	else
		return false;

}

BibEntry*  KBibDoc:: editEntry ( BibEntry * xentry )
{
	BibEntry* entry = xentry;
	bool edit = true;
	KBibApp* win = ( KBibApp * ) parent();
	while ( edit )
	{
		
		KBibEditEntryDlg* editEntryDlg = new KBibEditEntryDlg ( entry, win );
		if ( editEntryDlg->exec() ==QDialog::Accepted )
		{

			entry = new BibEntry ( * ( editEntryDlg->getEditedBibEntry() ) );
			
			int x = entry->getNoReqFields();
			bool emtyReqFields=false;
			edit = false;
			if ( QString ( entry->getKey() ) =="" )
				emtyReqFields = true;
			else
			{
				for ( int i = 0; i < x; i++ )
				{
					if ( QString ( entry->getField ( i ) ) =="" )
					{
						//cerr << "emtry fields\n";
						emtyReqFields = true;
						break;
					}
				}
			}

			if ( emtyReqFields )
			{

				if ( ( win->kbibConfigState ).askReqField )
				{

					int query = KMessageBox::warningYesNo ( win,i18n ( "Some required fields are empty.\nDo you want to correct?" ) );
					if ( query==KMessageBox::Yes )
					{
						edit =true;
					}
				}
			}

			if ( !edit )
			{
				delete editEntryDlg;
				return entry;
			}


			//    return editEntryDlg->getBibEntry();
		}
		else
		{
			delete editEntryDlg;
			return 0;
		}
	}
}


int KBibDoc::importBibTexFile ( const QString fn, bool import, bool notInsertExisting )
{

	int numRefs= 0;

	int numMacros = 0;
	FILE *inf = fopen ( fn.ascii(), "r" );
	bool ref = false;
	bool macro = false;

	if ( inf )
	{
		while ( !feof ( inf ) )
		{
			ref = false;
			macro = false;
			entry_list->readEntry ( inf, import, ref, macro, false, false, notInsertExisting );
			if ( macro )
				numMacros++;
			if ( ref )
			{
				numRefs++;
				BibEntry *bib = entry_list->get_entry ( size()-1 );
				p_view->addNewItem ( bib );
				group_view->addEntry ( bib );
			}

		}
		fclose ( inf );

	}

	if ( numRefs||numMacros )
	{
		KBibApp* win = ( KBibApp * ) parent();
		if ( numRefs )
		{
			win->slotNumRefMsg();
		}
		modified = true;
		win->slotDocChanged();
	}

	return numRefs;

}


QStringList KBibDoc::getValueList ( QString fn )
{
	if ( fn.isEmpty() )
	{
		return QStringList();
	}
	int listSize = entry_list->size();
	BibEntry* entry;
	QString value;
	QStringList list;
	RefField* field = BibEntryDefTable::self()->getRefField ( fn );
	if ( !field )
		return 0;
	for ( int i =0; i < listSize; i++ )
	{
		entry = entry_list->get_entry ( i );
		value = entry->getField ( fn );
		if ( !value.isEmpty() )
		{
			if ( field->type == MULTIPLE )
			{
				QStringList values = QStringList::split ( field->connectingString, value.simplifyWhiteSpace() );
				for ( QStringList::Iterator it = values.begin(); it != values.end(); ++it )
				{
					if ( list.findIndex ( *it ) == -1 )
						list.append ( *it );
				}
			}
			else
			{
				if ( list.findIndex ( value ) == -1 )
					list.append ( value );
			}
		}
	}
	return list;
}










