//
// C++ Implementation: referencetypesdialog
//
// Description: 
//
//
// Author: Thach Nguyen <thach.nguyen@rmit.edu.au>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "referencetypesdialog.h"

#include <klocale.h>
#include <kcombobox.h>
#include <klineedit.h>
#include <kiconloader.h>
#include <kmessagebox.h>
#include <kpushbutton.h>
#include <kpopupmenu.h>
#include <kaccelmanager.h>

#include <qgroupbox.h>
#include <qlayout.h>
#include <qhbox.h>
#include <qvbox.h>
#include <qlabel.h>
#include <qbuttongroup.h>
#include <qcheckbox.h>
#include <qwhatsthis.h>

#include <iostream>


ReferenceTypesDialog::ReferenceTypesDialog(QWidget *parent, const char *name)
 : KDialogBase ( parent, name, false, i18n ( "Reference Types" ), Ok|Cancel, Ok, false )
{
	QWidget* page = new QWidget ( this );
	setMainWidget ( page );
	QHBoxLayout* topLayout = new QHBoxLayout ( page, 0, KDialog::spacingHint() );

	QGroupBox* typesGroup = new QGroupBox ( 1, Qt::Horizontal, i18n ( "Reference Types" ), page );
	topLayout->addWidget ( typesGroup, 1 );
	m_typesBox = new QListBox ( typesGroup );
	m_typesBox->setMinimumWidth ( 150 );
	connect(m_typesBox, SIGNAL(highlighted(int)), SLOT(slotTypeHighlightedChanged(int)));
	
	m_typesBox->setMinimumHeight(300);
	
	QHBox* hb1 = new QHBox ( typesGroup );
	hb1->setSpacing ( KDialog::spacingHint() );
	KPushButton *m_btnNewType = new KPushButton ( i18n ( "New Type", "&New" ), hb1 );
	m_btnNewType->setIconSet ( BarIcon ( QString::fromLatin1 ( "filenew" ), KIcon::SizeSmall ) );
	QWhatsThis::add ( m_btnNewType, i18n ( "Add a new type" ) );
	m_btnDeleteType = new KPushButton ( i18n ( "Delete Type", "&Delete" ), hb1 );
	m_btnDeleteType->setIconSet ( BarIconSet ( QString::fromLatin1 ( "editdelete" ), KIcon::SizeSmall ) );
	QWhatsThis::add ( m_btnDeleteType, i18n ( "Remove the selected type" ) );
	m_btnDeleteType->setEnabled(false);
	
	connect(m_btnNewType, SIGNAL(clicked()), SLOT(slotNewType()) );
	connect(m_btnDeleteType, SIGNAL(clicked()), SLOT(slotDeleteType()) );
	
	
	QVBoxLayout* rightLayout = new QVBoxLayout ( page, 0, KDialog::spacingHint() );
	topLayout->addLayout( rightLayout, 1 );
	
	QHBox* nameHb = new QHBox(page);
	nameHb->setSpacing(KDialog::spacingHint());
	rightLayout->addWidget(nameHb, 0);
	QLabel *label = new QLabel(i18n("Name:"), nameHb);
	m_nameEdit = new KLineEdit(nameHb);
	label->setBuddy(m_nameEdit);
 	QString st = i18n("The reference type name");
    QWhatsThis::add(label, st);
	QWhatsThis::add(m_nameEdit, st);
	nameHb->setStretchFactor(m_nameEdit, 0);
	
	QHBoxLayout* fieldLayout = new QHBoxLayout ( page, 0, KDialog::spacingHint() );
	rightLayout->addLayout( fieldLayout, 1 );
	
	QGroupBox* requiredGroup = new QGroupBox ( 1, Qt::Horizontal, i18n ( "Required Fields" ), page );
	fieldLayout->addWidget ( requiredGroup, 1 );
	m_requiredBox = new QListBox ( requiredGroup );
	m_requiredBox->setMinimumWidth ( 150 );
	connect(m_requiredBox, SIGNAL(highlighted(int)), SLOT(slotRequiredHighlightedChanged(int)));
	
	
	
	QHBox* hb2b = new QHBox(requiredGroup);
  	hb2b->setSpacing(KDialog::spacingHint());
  	m_btnRequiredUp = new KPushButton(hb2b);
  	m_btnRequiredUp->setPixmap(BarIcon(QString::fromLatin1("up"), KIcon::SizeSmall));
  	QWhatsThis::add(m_btnRequiredUp, i18n("Move this field up in the list."));
 	m_btnRequiredDown = new KPushButton(hb2b);
  	m_btnRequiredDown->setPixmap(BarIcon(QString::fromLatin1("down"), KIcon::SizeSmall));
  	QWhatsThis::add(m_btnRequiredDown, i18n("Move this field down in the list."));
	m_btnRequiredUp->setEnabled(false);
	m_btnRequiredDown->setEnabled(false);
	
  	connect(m_btnRequiredUp, SIGNAL(clicked()), SLOT(slotMoveRequiredUp()) );
  	connect(m_btnRequiredDown, SIGNAL(clicked()), SLOT(slotMoveRequiredDown()));
	
	QHBox* hb2 = new QHBox ( requiredGroup );
	hb2->setSpacing ( KDialog::spacingHint() );
	KPushButton *m_btnNewRequiredField = new KPushButton ( i18n ( "Add Required Field", "Add Field" ), hb2 );
	m_btnNewRequiredField->setIconSet ( BarIcon ( QString::fromLatin1 ( "add" ), KIcon::SizeSmall ) );

	QWhatsThis::add ( m_btnNewRequiredField, i18n ( "Add a new requried field" ) );
	m_btnDeleteRequiredField = new KPushButton ( i18n ( "Remove Required Field", "Remove" ), hb2 );
	m_btnDeleteRequiredField->setIconSet ( BarIconSet ( QString::fromLatin1 ( "remove" ), KIcon::SizeSmall ) );
	QWhatsThis::add ( m_btnDeleteType, i18n ( "Remove the selected required field" ) );
	m_btnDeleteRequiredField->setEnabled(false);
	connect(m_btnDeleteRequiredField, SIGNAL(clicked()), SLOT(slotDeleteRequiredField()) );
	
	QGroupBox* optionalGroup = new QGroupBox ( 1, Qt::Horizontal, i18n ( "Optional Fields" ), page );
	fieldLayout->addWidget ( optionalGroup, 1 );
	m_optionalBox = new QListBox ( optionalGroup );
	m_optionalBox->setMinimumWidth ( 150 );
	connect(m_optionalBox, SIGNAL(highlighted(int)), SLOT(slotOptionalHighlightedChanged(int)));
	
	
	
	QHBox* hb3b = new QHBox(optionalGroup);
  	hb3b->setSpacing(KDialog::spacingHint());
  	m_btnOptionalUp = new KPushButton(hb3b);
  	m_btnOptionalUp->setPixmap(BarIcon(QString::fromLatin1("up"), KIcon::SizeSmall));
  	QWhatsThis::add(m_btnOptionalUp, i18n("Move this field up in the list."));
 	m_btnOptionalDown = new KPushButton(hb3b);
  	m_btnOptionalDown->setPixmap(BarIcon(QString::fromLatin1("down"), KIcon::SizeSmall));
  	QWhatsThis::add(m_btnOptionalDown, i18n("Move this field down in the list."));

	m_btnOptionalUp->setEnabled(false);
	m_btnOptionalDown->setEnabled(false);
	
  	connect(m_btnOptionalUp, SIGNAL(clicked()), SLOT(slotMoveOptionalUp()) );
  	connect(m_btnOptionalDown, SIGNAL(clicked()), SLOT(slotMoveOptionalDown()));
	
	QHBox* hb3 = new QHBox ( optionalGroup );
	hb3->setSpacing ( KDialog::spacingHint() );
	KPushButton *m_btnNewOptionalField = new KPushButton ( i18n ( "Add Required Field", "Add Field" ), hb3 );
	m_btnNewOptionalField->setIconSet ( BarIcon ( QString::fromLatin1 ( "add" ), KIcon::SizeSmall ) );

	QWhatsThis::add ( m_btnNewOptionalField, i18n ( "Add a new optional field" ) );
	m_btnDeleteOptionalField = new KPushButton ( i18n ( "Remove Optinal Field", "Remove" ), hb3 );
	m_btnDeleteOptionalField->setIconSet ( BarIconSet ( QString::fromLatin1 ( "remove" ), KIcon::SizeSmall ) );
	QWhatsThis::add ( m_btnDeleteOptionalField, i18n ( "Remove the selected optional field" ) );
	m_btnDeleteOptionalField->setEnabled(false);
	connect(m_btnDeleteOptionalField, SIGNAL(clicked()), SLOT(slotDeleteOptionalField()) );
	
	m_newRequiredFieldMenu = new KPopupMenu(this);
	m_btnNewRequiredField->setPopup(m_newRequiredFieldMenu);
	
	m_newOptionalFieldMenu = new KPopupMenu(this);
	m_btnNewOptionalField->setPopup(m_newOptionalFieldMenu);
	
	connect(m_newRequiredFieldMenu, SIGNAL(activated(int)),
			this, SLOT(slotNewRequiredFieldMenu(int)));
		
	connect(m_newOptionalFieldMenu, SIGNAL(activated(int)),
			this, SLOT(slotNewOptionalFieldMenu(int)));
	
	int i = 0;
	QStringList fieldNameTab = BibEntryDefTable::self()->getFieldNames();
	for (QStringList::Iterator it = fieldNameTab.begin(); it != fieldNameTab.end(); ++it){
			m_newRequiredFieldMenu->insertItem(*it, i);
			m_newOptionalFieldMenu->insertItem(*it, i);
			i++;
	}

	//m_typesBox->insertStringList(entryDefTab.getEntryDefNames());
	//Make a copy of entry def table
	QStringList entryDefNames = BibEntryDefTable::self()->getEntryDefNames();
	for (QStringList::Iterator it = entryDefNames.begin(); it != entryDefNames.end(); ++it){
		BibEntryDef *def = new BibEntryDef();
		
		QString name = *it;
		BibEntryDef *def2 = BibEntryDefTable::self()->getBibEntryDef(name);
		def->name = def2->name;
		def->required = def2->required;
		def->optional = def2->optional;
		def->readOnly = def2->readOnly;
		m_typesBox->insertItem(name);
		m_refTypes.insert(name, def);
	}
	m_refTypes.setAutoDelete(true);
	prevIndex = -1;
	newTypeCnt = 1;
	
	
}


ReferenceTypesDialog::~ReferenceTypesDialog()
{
	m_refTypes.clear();
}

void ReferenceTypesDialog::slotTypeHighlightedChanged(int index){
	//Update the current type
	if (!updateType())
		return;
	BibEntryDef *def;
	m_typesBox->blockSignals(true);
	m_typesBox->setSelected(index, true);
	m_typesBox->blockSignals(false);

	if (index >= 0 && index < m_typesBox->count() ){
		QString name = m_typesBox->text(index);
		BibEntryDef *def = m_refTypes[name];
		m_requiredBox->clear();
		m_optionalBox->clear();
		m_nameEdit->setText(name);
		m_requiredBox->insertStringList(def->required);
		m_optionalBox->insertStringList(def->optional);
		prevIndex = index;
		prevName = name;
		if (def->readOnly){
			m_btnDeleteType->setEnabled(false);
			m_nameEdit->setEnabled(false);
		}
		else{
			m_btnDeleteType->setEnabled(true);
			m_nameEdit->setEnabled(true);
		}
	}	
	else{
		m_nameEdit->clear();
		m_requiredBox->clear();
		m_optionalBox->clear();
	}
}


void ReferenceTypesDialog::slotNewType(){
	if (!updateType())
		return;
	BibEntryDef *def = new BibEntryDef();
	
	QString name=i18n("NewType") + QString::fromLatin1("%1").arg(newTypeCnt);
	
	while(m_refTypes[name]){
		newTypeCnt++;	
		name = i18n("NewType") + QString::fromLatin1("%1").arg(newTypeCnt);
	}
	
	def->name = name;
	def->readOnly = false;
	m_refTypes.insert(def->name, def);	
	m_typesBox->insertItem(def->name);
	m_typesBox->setSelected(m_typesBox->count()-1, true);
	newTypeCnt++;
	m_typesBox->ensureCurrentVisible();
  	m_nameEdit->setFocus();
  	m_nameEdit->selectAll();
}


void ReferenceTypesDialog::slotDeleteType(){
	int index = m_typesBox->currentItem();
	if (index < 0)
		return;
	m_refTypes.remove(m_typesBox->text(index));
	m_typesBox->blockSignals(true);
	m_typesBox->removeItem(index);
	prevIndex = -1;
	prevName = QString();
	m_typesBox->blockSignals(false);
	m_btnDeleteType->setEnabled(false);
	slotTypeHighlightedChanged(-1);
}


void ReferenceTypesDialog::slotNewRequiredFieldMenu(int index){
	QString field = m_newRequiredFieldMenu->text(index);
	if (fieldExist(field) )
		return;
	m_requiredBox->insertItem(field);	
	m_requiredBox->setSelected(m_requiredBox->count()-1, true);
}

void ReferenceTypesDialog::slotNewOptionalFieldMenu(int index){
	QString field = m_newOptionalFieldMenu->text(index);
	if (fieldExist(field) )
		return;
	m_optionalBox->insertItem(field);	
	m_optionalBox->setSelected(m_optionalBox->count()-1, true);
}

bool ReferenceTypesDialog::fieldExist(QString field){
	for (int i = 0; i < m_requiredBox->count(); i++) {
		if (field.lower() == (m_requiredBox->text(i)).lower() )
			return true;		
	}
	for (int i = 0; i < m_optionalBox->count(); i++){
		if (field.lower() == (m_optionalBox->text(i)).lower() )
			return true;		
	}
	return false;
}

void ReferenceTypesDialog::slotDeleteRequiredField(){
	int index = m_requiredBox->currentItem();
	if (index < 0)
		return;
	m_requiredBox->removeItem(index);
	if (index < m_requiredBox->count())
		m_requiredBox->setSelected(index, true);
	else{
		m_btnDeleteRequiredField->setEnabled(false);
		slotRequiredHighlightedChanged(-1);
	}
}

void ReferenceTypesDialog::slotDeleteOptionalField(){
	int index = m_optionalBox->currentItem();
	if (index < 0)
		return;
	m_optionalBox->removeItem(index);
	if (index < m_optionalBox->count())
		m_optionalBox->setSelected(index, true);
	else{
		m_btnDeleteOptionalField->setEnabled(false);
		slotOptionalHighlightedChanged(-1);
	}
}

void ReferenceTypesDialog::slotRequiredHighlightedChanged(int index){
	if (index < 0)
		m_btnDeleteRequiredField->setEnabled(false);
	else
		m_btnDeleteRequiredField->setEnabled(true);
	if (index < 1)
		m_btnRequiredUp->setEnabled(false);
	else
		m_btnRequiredUp->setEnabled(true);
	if (index < m_requiredBox->count()-1)
		m_btnRequiredDown->setEnabled(true);
	else
		m_btnRequiredDown->setEnabled(false);
}

void ReferenceTypesDialog::slotOptionalHighlightedChanged(int index){
	if (index < 0)
		m_btnDeleteOptionalField->setEnabled(false);
	else
		m_btnDeleteOptionalField->setEnabled(true);
	if (index < 1)
		m_btnOptionalUp->setEnabled(false);
	else
		m_btnOptionalUp->setEnabled(true);
	if (index < m_optionalBox->count()-1)
		m_btnOptionalDown->setEnabled(true);
	else
		m_btnOptionalDown->setEnabled(false);
}

void ReferenceTypesDialog::slotMoveRequiredUp(){
	int index = m_requiredBox->currentItem();
	if (index <= 0)
		return;
	QString prev = m_requiredBox->text(index-1);
	m_requiredBox->blockSignals(true);
	m_requiredBox->removeItem(index-1);
	m_requiredBox->insertItem(prev, index);
	m_requiredBox->setSelected(index-1, true);
	slotRequiredHighlightedChanged(index-1);
	m_requiredBox->blockSignals(false);
}
void ReferenceTypesDialog::slotMoveRequiredDown(){
	int index = m_requiredBox->currentItem();
	if (index >= m_requiredBox->count()-1)
		return;
	QString prev = m_requiredBox->text(index);
	m_requiredBox->blockSignals(true);
	m_requiredBox->removeItem(index);
	m_requiredBox->insertItem(prev, index+1);
	m_requiredBox->setSelected(index+1, true);
	slotRequiredHighlightedChanged(index+1);
	m_requiredBox->blockSignals(false);
}
void ReferenceTypesDialog::slotMoveOptionalUp(){
	int index = m_optionalBox->currentItem();
	if (index <= 0)
		return;
	QString prev = m_optionalBox->text(index-1);
	m_optionalBox->blockSignals(true);
	m_optionalBox->removeItem(index-1);
	m_optionalBox->insertItem(prev, index);
	m_optionalBox->setSelected(index-1, true);
	slotOptionalHighlightedChanged(index-1);
	m_optionalBox->blockSignals(false);
}
void ReferenceTypesDialog::slotMoveOptionalDown(){
	int index = m_optionalBox->currentItem();
	if (index >= m_optionalBox->count()-1)
		return;
	QString prev = m_optionalBox->text(index);
	m_optionalBox->blockSignals(true);
	m_optionalBox->removeItem(index);
	m_optionalBox->insertItem(prev, index+1);
	m_optionalBox->setSelected(index+1, true);
	slotOptionalHighlightedChanged(index+1);
	m_optionalBox->blockSignals(false);
}


/*
QStringList ReferenceTypesDialog::getTypeNames(){
	QStringList types;
	for (int i = 0; i < m_typesBox->count(); i++)
		types.append(m_typesBox->text(i));	
	return types;
}
*/

bool ReferenceTypesDialog::checkType(){
	//Check for empty name
	QString name = m_nameEdit->text().stripWhiteSpace();
	if (name.isEmpty()){
		KMessageBox::sorry(this, i18n("The reference type name cannot be empty."));
			return false;	
	}
	
	for (int i = 0; i < m_typesBox->count(); i++){
		QString xname = m_typesBox->text(i);
		if (name.lower() == xname.lower() && prevIndex != i){
			KMessageBox::sorry(this, i18n("A reference type with name \"%1\" already exists. Please enter a different name.").arg(name));
			return false;
		}
	}
	//Check for valid name
	QChar c = name.at(0);
	if (!c.isLetter()){
		KMessageBox::sorry(this, i18n("The reference type must start with a letter."));
			return false;		
	}
	
	if (name.contains(' ') ){
		KMessageBox::sorry(this, i18n("The reference type cannot contain white spaces."));
			return false;		 
	}
	
	
	
	return true;
	
	
	
}

bool ReferenceTypesDialog::updateType(){
	BibEntryDef *def;
	if (prevIndex >= 0 && prevIndex < m_typesBox->count()){
		//Check for existing type
		if (!checkType()){
			m_typesBox->blockSignals(true);
			m_typesBox->setSelected(prevIndex, true);
			m_typesBox->blockSignals(false);
			return false;
		}
		else{
			bool readOnly = false;
			if ( m_refTypes.find( prevName ) ){
            	def = m_refTypes[prevName];
				readOnly = def->readOnly;
				m_refTypes.remove( prevName );
			}
			def = new BibEntryDef();
			QStringList required, optional;
			for (int i = 0; i < m_requiredBox->count(); i++)
				required.append(m_requiredBox->text(i));
			for (int i = 0; i < m_optionalBox->count(); i++)
				optional.append(m_optionalBox->text(i));
			QString name = (m_nameEdit->text()).stripWhiteSpace();
			def->name = name;
			def->required = required;
			def->optional = optional;
			def->readOnly = readOnly;
			m_refTypes.insert(name, def);
			
			
			m_typesBox->blockSignals(true);
			m_typesBox->changeItem(name, prevIndex);
			m_typesBox->blockSignals(false);
			return true;
		}
			
	}
	return true;	
	
}

void ReferenceTypesDialog::slotOk(){
	if (!updateType())
		return;
	accept();
}

void ReferenceTypesDialog::saveTypes(){
	BibEntryDefTable::self()->clear();
	QString name;
	QStringList newNameList;
	for (int i = 0; i < m_typesBox->count(); i++){
		name = m_typesBox->text(i);
		newNameList << name;
		BibEntryDefTable::self()->newEntryDef(name, m_refTypes[name]->required, m_refTypes[name]->optional, m_refTypes[name]->readOnly, true);
	}	
	
}

#include "referencetypesdialog.moc"
