/* This file is part of the KDE project
   Copyright (C) 2003-2007 Jaroslaw Staniek <js@iidea.pl>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include <kexidb/fieldlist.h>
#include <kexidb/object.h>

#include <kdebug.h>

#include <assert.h>

using namespace KexiDB;

FieldList::FieldList(bool owner)
 //reasonable sizes: TODO
 : m_fields_by_name(1009, false)
{
	m_fields.setAutoDelete( owner );
	m_fields_by_name.setAutoDelete( false );
	m_autoinc_fields = 0;
}

FieldList::FieldList(const FieldList& fl, bool deepCopyFields)
 : m_fields_by_name( fl.m_fields_by_name.size() )
{
	m_fields.setAutoDelete( fl.m_fields.autoDelete() );
	m_fields_by_name.setAutoDelete( false );
	m_autoinc_fields = 0;

	if (deepCopyFields) {
		//deep copy for the fields
		for (Field::ListIterator f_it(fl.m_fields); f_it.current(); ++f_it) {
			Field *f = f_it.current()->copy();
			if (f_it.current()->m_parent == &fl)
				f->m_parent = this;
			addField( f );
		}
	}
}

FieldList::~FieldList()
{
	delete m_autoinc_fields;
}

void FieldList::clear()
{
//	m_name = TQString();
	m_fields.clear();
	m_fields_by_name.clear();
	m_sqlFields = TQString();
	delete m_autoinc_fields;
	m_autoinc_fields = 0;
}

FieldList& FieldList::insertField(uint index, KexiDB::Field *field)
{
	assert(field);
	if (!field)
		return *this;
	if (index>m_fields.count()) {
		KexiDBFatal << "FieldList::insertField(): index (" << index << ") out of range" << endl;
		return *this;
	}
	if (!m_fields.insert(index, field))
		return *this;
	if (!field->name().isEmpty())
		m_fields_by_name.insert(field->name().lower(),field);
	m_sqlFields = TQString();
	return *this;
}

void FieldList::renameField(const TQString& oldName, const TQString& newName)
{
	renameField( m_fields_by_name[ oldName ], newName );
}

void FieldList::renameField(KexiDB::Field *field, const TQString& newName)
{
	if (!field || field != m_fields_by_name[ field->name() ]) {
		KexiDBFatal << "FieldList::renameField() no field found " 
			<< (field ? TQString("\"%1\"").arg(field->name()) : TQString()) << endl;
		return;
	}
	m_fields_by_name.take( field->name() );
	field->setName( newName );
	m_fields_by_name.insert( field->name(), field );
}


FieldList& FieldList::addField(KexiDB::Field *field)
{
	return insertField(m_fields.count(), field);
}

void FieldList::removeField(KexiDB::Field *field)
{
	assert(field);
	if (!field)
		return;
	m_fields_by_name.remove(field->name());
	m_fields.remove(field);
	m_sqlFields = TQString();
}

Field* FieldList::field(const TQString& name)
{
	return m_fields_by_name[name];
}

TQString FieldList::debugString()
{
	TQString dbg;
	dbg.reserve(512);
	Field::ListIterator it( m_fields );
	Field *field;
	bool start = true;
	if (!it.current())
		dbg = "<NO FIELDS>";
	for (; (field = it.current())!=0; ++it) {
		if (!start)
			dbg += ",\n";
		else
			start = false;
		dbg += "  ";
		dbg += field->debugString();
	}
	return dbg;
}

void FieldList::debug()
{
	KexiDBDbg << debugString() << endl;
}

#define _ADD_FIELD(fname) \
{ \
	if (fname.isEmpty()) return fl; \
	f = m_fields_by_name[fname]; \
	if (!f) { KexiDBWarn << subListWarning1(fname) << endl; delete fl; return 0; } \
	fl->addField(f); \
}

static TQString subListWarning1(const TQString& fname) {
	return TQString("FieldList::subList() could not find field \"%1\"").arg(fname);
}

FieldList* FieldList::subList(const TQString& n1, const TQString& n2, 
	const TQString& n3, const TQString& n4,
	const TQString& n5, const TQString& n6,
	const TQString& n7, const TQString& n8,
	const TQString& n9, const TQString& n10,
	const TQString& n11, const TQString& n12,
	const TQString& n13, const TQString& n14,
	const TQString& n15, const TQString& n16,
	const TQString& n17, const TQString& n18)
{
	if (n1.isEmpty())
		return 0;
	Field *f;
	FieldList *fl = new FieldList(false);
	_ADD_FIELD(n1);
	_ADD_FIELD(n2);
	_ADD_FIELD(n3);
	_ADD_FIELD(n4);
	_ADD_FIELD(n5);
	_ADD_FIELD(n6);
	_ADD_FIELD(n7);
	_ADD_FIELD(n8);
	_ADD_FIELD(n9);
	_ADD_FIELD(n10);
	_ADD_FIELD(n11);
	_ADD_FIELD(n12);
	_ADD_FIELD(n13);
	_ADD_FIELD(n14);
	_ADD_FIELD(n15);
	_ADD_FIELD(n16);
	_ADD_FIELD(n17);
	_ADD_FIELD(n18);
	return fl;
}

FieldList* FieldList::subList(const TQStringList& list)
{
	Field *f;
	FieldList *fl = new FieldList(false);
	for(TQStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) {
		_ADD_FIELD( (*it) );
	}
	return fl;
}

FieldList* FieldList::subList(const TQValueList<uint>& list)
{
	Field *f;
	FieldList *fl = new FieldList(false);
	foreach(TQValueList<uint>::ConstIterator, it, list) {
		f = field(*it);
		if (!f) {
			KexiDBWarn << TQString("FieldList::subList() could not find field at position %1").arg(*it) << endl;
			delete fl;
			return 0;
		}
		fl->addField(f);
	}
	return fl;
}

TQStringList FieldList::names() const
{
	TQStringList r;
//	for (TQDictIterator<Field> it(m_fields_by_name);it.current();++it) {
//		r += it.currentKey().lower();
//	}
	for (Field::ListIterator it(m_fields); it.current(); ++it) {
		r += it.current()->name().lower();
	}
	return r;
}

//static
TQString FieldList::sqlFieldsList(Field::List* list, Driver *driver, 
	const TQString& separator, const TQString& tableAlias, int drvEscaping)
{
	if (!list)
		return TQString();
	TQString result;
	result.reserve(256);
	bool start = true;
	const TQString tableAliasAndDot( tableAlias.isEmpty() ? TQString() : (tableAlias + ".") );
	for (Field::ListIterator it( *list ); it.current(); ++it) {
		if (!start)
			result += separator;
		else
			start = false;
		result += (tableAliasAndDot + driver->escapeIdentifier( it.current()->name(), drvEscaping ));
	}
	return result;
}

TQString FieldList::sqlFieldsList(Driver *driver, 
	const TQString& separator, const TQString& tableAlias, int drvEscaping)
{
	if (!m_sqlFields.isEmpty())
		return m_sqlFields;

	m_sqlFields = FieldList::sqlFieldsList( &m_fields, driver, separator, tableAlias, drvEscaping );
	return m_sqlFields;
}

Field::List* FieldList::autoIncrementFields()
{
	if (!m_autoinc_fields) {
		m_autoinc_fields = new Field::List();
		Field *f;
		for (Field::ListIterator f_it(m_fields); (f = f_it.current()); ++f_it) {
			if (f->isAutoIncrement()) {
				m_autoinc_fields->append( f_it.current() );
			}
		}
	}
	return m_autoinc_fields;
}
