/* This file is part of the KDE project
   Copyright (C) 2002   Lucijan Busch <lucijan@gmx.at>
   Copyright (C) 2003 Jaroslaw Staniek <js@iidea.pl>

   This program 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 program 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 program; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include <kdebug.h>

#include <tqstringlist.h>
#include <tqlayout.h>
#include <tqlabel.h>
#include <tqheader.h>
#include <tqevent.h>
#include <tqpainter.h>
#include <tqstyle.h>
#include <tqlineedit.h>
#include <tqpopupmenu.h>

#include <tdelocale.h>
#include <tdeaction.h>
#include <tdepopupmenu.h>
#include <tdeglobalsettings.h>
#include <tdemessagebox.h>

#include <kexidb/tableschema.h>
#include <kexidb/indexschema.h>
#include <kexidb/utils.h>

#include "kexirelationview.h"
#include "kexirelationviewtable.h"
#include "kexirelationviewconnection.h"
#include <kexi.h>

KexiRelationView::KexiRelationView(TQWidget *parent, const char *name)
 : TQScrollView(parent, name, WStaticContents)
{
//	m_relation=relation;
//	m_relation->incUsageCount();
	m_selectedConnection = 0;
	m_readOnly=false;
	m_focusedTableView = 0;
	setFrameStyle(TQFrame::WinPanel | TQFrame::Sunken);

//	connect(relation, TQ_SIGNAL(relationListUpdated(TQObject *)), this, TQ_SLOT(slotListUpdate(TQObject *)));

	viewport()->setPaletteBackgroundColor(colorGroup().mid());
	setFocusPolicy(TQWidget::WheelFocus);
	setResizePolicy(Manual);
/*MOVED TO KexiRelationDialog
	//actions
	m_tableQueryPopup = new TDEPopupMenu(this, "m_popup");
	m_tableQueryPopup->insertTitle(i18n("Table"));
	m_connectionPopup = new TDEPopupMenu(this, "m_connectionPopup");
	m_connectionPopup->insertTitle(i18n("Relation"));
	m_areaPopup = new TDEPopupMenu(this, "m_areaPopup");
	
	plugSharedAction("edit_delete", i18n("Hide Table"), m_tableQueryPopup);
	plugSharedAction("edit_delete",m_connectionPopup);
	plugSharedAction("edit_delete",this, TQ_SLOT(removeSelectedObject()));
*/	
#if 0
	m_removeSelectedTableQueryAction = new TDEAction(i18n("&Hide Selected Table/Query"), "edit-delete", "",
		this, TQ_SLOT(removeSelectedTableQuery()), parent->actionCollection(), "relationsview_removeSelectedTableQuery");
	m_removeSelectedConnectionAction = new TDEAction(i18n("&Remove Selected Relationship"), "button_cancel", "",
		this, TQ_SLOT(removeSelectedConnection()), parent->actionCollection(), "relationsview_removeSelectedConnection");
	m_openSelectedTableQueryAction = new TDEAction(i18n("&Open Selected Table/Query"), "", "",
		this, TQ_SLOT(openSelectedTableQuery()), 0/*parent->actionCollection()*/, "relationsview_openSelectedTableQuery");
#endif

//	invalidateActions();

#if 0


	m_popup = new TDEPopupMenu(this, "m_popup");
	m_openSelectedTableQueryAction->plug( m_popup );
	m_removeSelectedTableQueryAction->plug( m_popup );
	m_removeSelectedConnectionAction->plug( m_popup );

	invalidateActions();
#endif

	setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding, true);
}

KexiRelationView::~KexiRelationView()
{
}

/*KexiRelationViewTableContainer*
KexiRelationView::containerForTable(KexiDB::TableSchema* tableSchema)
{
	if (!tableSchema)
		return 0;
	for (TablesDictIterator it(m_tables); it.current(); ++it) {
		if (it.current()->schema()->table()==tableSchema)
			return it.current();
	}
	return 0;
}*/

KexiRelationViewTableContainer *
KexiRelationView::tableContainer(KexiDB::TableSchema *t) const
{
	return t ? m_tables.find(t->name()) : 0;
}

KexiRelationViewTableContainer*
KexiRelationView::addTableContainer(KexiDB::TableSchema *t, const TQRect &rect)
{
	if(!t)
		return 0;

	kdDebug() << "KexiRelationView::addTable(): " << t->name() << ", " << viewport() << endl;

	KexiRelationViewTableContainer* c = tableContainer(t);
	if (c) {
		kdWarning() << "KexiRelationView::addTable(): table already added" << endl;
		return c;
	}

	c = new KexiRelationViewTableContainer(this, 
/*! @todo what about query? */
		new KexiDB::TableOrQuerySchema(t)
	);
	connect(c, TQ_SIGNAL(endDrag()), this, TQ_SLOT(slotTableViewEndDrag()));
	connect(c, TQ_SIGNAL(gotFocus()), this, TQ_SLOT(slotTableViewGotFocus()));
//	connect(c, TQ_SIGNAL(headerContextMenuRequest(const TQPoint&)), 
//		this, TQ_SLOT(tableHeaderContextMenuRequest(const TQPoint&)));
	connect(c, TQ_SIGNAL(contextMenuRequest(const TQPoint&)), 
		this, TQ_SIGNAL(tableContextMenuRequest(const TQPoint&)));
	
	addChild(c, 100,100);
	if (rect.isValid()) {//predefined size
		TQSize finalSize = c->size().expandedTo( c->sizeHint() );
		TQRect r = rect;
		r.setSize( finalSize + TQSize(0,10) );
		moveChild( c, rect.left(), rect.top() );
		//we're doing this instead of setGeometry(rect)
		//because the geomenty might be saved on other system with bigger fonts :)
		c->resize(c->sizeHint());
//		c->setGeometry(r);
//TODO

//		moveChild( c, rect.left(), rect.top() ); // setGeometry(rect);
//		c->resize( finalSize );
//		c->updateGeometry();
	}
	c->show();
	updateGeometry();
	if (!rect.isValid()) {
		c->updateGeometry();
		c->resize(c->sizeHint());
	}
	int x, y;

	if(m_tables.count() > 0)
	{
		int place = -10;
		TQDictIterator<KexiRelationViewTableContainer> it(m_tables);
		for(; it.current(); ++it)
		{
			int right = (*it)->x() + (*it)->width();
			if(right > place)
				place = right;
		}

		x = place + 30;
	}
	else
	{
		x = 5;
	}

	y = 5;
	TQPoint p = viewportToContents(TQPoint(x, y));
	recalculateSize(p.x() + c->width(), p.y() + c->height());
	if (!rect.isValid()) {
		moveChild(c, x, y);
	}

	m_tables.insert(t->name(), c);

	connect(c, TQ_SIGNAL(moved(KexiRelationViewTableContainer *)), this,
            TQ_SLOT(containerMoved(KexiRelationViewTableContainer *)));
	
	if (hasFocus()) //ok?
		c->setFocus();

	return c;
}

void
KexiRelationView::addConnection(const SourceConnection& _conn)
{
	SourceConnection conn = _conn;
	kdDebug() << "KexiRelationView::addConnection()" << endl;

	KexiRelationViewTableContainer *master = m_tables[conn.masterTable];
	KexiRelationViewTableContainer *details = m_tables[conn.detailsTable];
	if (!master || !details)
		return;

/*! @todo what about query? */
	KexiDB::TableSchema *masterTable = master->schema()->table();
/*! @todo what about query? */
	KexiDB::TableSchema *detailsTable = details->schema()->table();
	if (!masterTable || !detailsTable)
		return;

	// ok, but we need to know where is the 'master' and where is the 'details' side:
	KexiDB::Field *masterFld = masterTable->field(conn.masterField);
	KexiDB::Field *detailsFld = detailsTable->field(conn.detailsField);
	if (!masterFld || !detailsFld)
		return;
	
	if (!masterFld->isUniqueKey()) {
		if (detailsFld->isUniqueKey()) {
			//SWAP:
			KexiDB::Field *tmpFld = masterFld;
			masterFld = detailsFld;
			detailsFld = tmpFld;
			KexiDB::TableSchema *tmpTable = masterTable;
			masterTable = detailsTable;
			detailsTable = tmpTable;
			KexiRelationViewTableContainer *tmp = master;
			master = details;
			details = tmp;
			TQString tmp_masterTable = conn.masterTable;
			conn.masterTable = conn.detailsTable;
			conn.detailsTable = tmp_masterTable;
			TQString tmp_masterField = conn.masterField;
			conn.masterField = conn.detailsField;
			conn.detailsField = tmp_masterField;
		}
	}

//	kdDebug() << "KexiRelationView::addConnection(): finalSRC = " << m_tables[conn.srcTable] << endl;

	KexiRelationViewConnection *connView = new KexiRelationViewConnection(master, details, conn, this);
	m_connectionViews.append(connView);
	updateContents(connView->connectionRect());

/*js: will be moved up to relation/query part as this is only visual class
	KexiDB::TableSchema *mtable = m_conn->tableSchema(conn.srcTable);
	KexiDB::TableSchema *ftable = m_conn->tableSchema(conn.rcvTable);
	KexiDB::IndexSchema *forign = new KexiDB::IndexSchema(ftable);

	forign->addField(mtable->field(conn.srcField));
	new KexiDB::Reference(forign, mtable->primaryKey());
*/
#if 0
	if(!interactive)
	{
		kdDebug() << "KexiRelationView::addConnection: adding self" << endl;
		RelationList l = m_relation->projectRelations();
		l.append(conn);
		m_relation->updateRelationList(this, l);
	}
#endif
}

void
KexiRelationView::drawContents(TQPainter *p, int cx, int cy, int cw, int ch)
{
	KexiRelationViewConnection *cview;
//	p->translate(0, (double)contentsY());

	TQRect clipping(cx, cy, cw, ch);
	for(cview = m_connectionViews.first(); cview; cview = m_connectionViews.next())
	{
		if(clipping.intersects(cview->oldRect() | cview->connectionRect()))
			cview->drawConnection(p);
	}
}

void
KexiRelationView::slotTableScrolling(const TQString& table)
{
	KexiRelationViewTableContainer *c = m_tables[table];

	if(c)
		containerMoved(c);
}

void
KexiRelationView::containerMoved(KexiRelationViewTableContainer *c)
{
	KexiRelationViewConnection *cview;
	TQRect r;
	for (ConnectionListIterator it(m_connectionViews); ((cview=it.current())); ++it) {
//! @todo	optimize
		if(cview->masterTable() == c || cview->detailsTable() == c
			|| cview->connectionRect().intersects(r)) 
		{
			r |= cview->oldRect();
			kdDebug() << r << endl;
			r |= cview->connectionRect();
			kdDebug() << r << endl;
		}
//			updateContents(cview->oldRect());
//			updateContents(cview->connectionRect());
//		}
	}
//! @todo	optimize!
//didn't work well:	updateContents(r);
	updateContents();

//	TQRect w(c->x() - 5, c->y() - 5, c->width() + 5, c->height() + 5);
//	updateContents(w);

	TQPoint p = viewportToContents(TQPoint(c->x(), c->y()));
	recalculateSize(p.x() + c->width(), p.y() + c->height());

	emit tablePositionChanged(c);
}

void
KexiRelationView::setReadOnly(bool b)
{
	m_readOnly=b;
//TODO
//	invalidateActions();
/*	TableList::Iterator it, end( m_tables.end() );	
 	for ( it=m_tables.begin(); it != end; ++it)
	{
//		(*it)->setReadOnly(b);
#ifndef TQ_WS_WIN
		#warning readonly needed
#endif
	}*/
}

void
KexiRelationView::slotListUpdate(TQObject *)
{
#if 0
	if(s != this)
	{
		m_connectionViews.clear();
		RelationList rl = m_relation->projectRelations();
		if(!rl.isEmpty())
		{
			RelationList::ConstIterator it, end( rl.constEnd() );
			for( it = rl.begin(); it != end; ++it)
			{
				addConnection((*it), true);
			}
		}
	}

	updateContents();
#endif
}

void
KexiRelationView::contentsMousePressEvent(TQMouseEvent *ev)
{
	KexiRelationViewConnection *cview;
	for(cview = m_connectionViews.first(); cview; cview = m_connectionViews.next())
	{
		if(!cview->matchesPoint(ev->pos(), 3))
			continue;
		clearSelection();
		setFocus();
		cview->setSelected(true);
		updateContents(cview->connectionRect());
		m_selectedConnection = cview;
		emit connectionViewGotFocus();
//		invalidateActions();

		if(ev->button() == TQt::RightButton) {//show popup
			kdDebug() << "KexiRelationView::contentsMousePressEvent(): context" << endl;
//			TQPopupMenu m;
//				m_removeSelectedTableQueryAction->plug( &m );
//				m_removeSelectedConnectionAction->plug( &m );
			emit connectionContextMenuRequest( ev->globalPos() );
//			executePopup( ev->globalPos() );
		}
		return;
	}
	//connection not found
	clearSelection();
//	invalidateActions();
	if(ev->button() == TQt::RightButton) {//show popup on view background area
//		TQPopupMenu m;
//			m_removeSelectedConnectionAction->plug( &m );
		emit emptyAreaContextMenuRequest( ev->globalPos() );
//		executePopup(ev->globalPos());
	}
	else {
		emit emptyAreaGotFocus();
	}
	setFocus();
//	TQScrollView::contentsMousePressEvent(ev);
}

void KexiRelationView::clearSelection()
{
	if (m_focusedTableView) {
		m_focusedTableView->unsetFocus();
		m_focusedTableView = 0;
//		setFocus();
//		invalidateActions();
	}
	if (m_selectedConnection) {
		m_selectedConnection->setSelected(false);
		updateContents(m_selectedConnection->connectionRect());
		m_selectedConnection = 0;
//		invalidateActions();
	}
}

void
KexiRelationView::keyPressEvent(TQKeyEvent *ev)
{
	kdDebug() << "KexiRelationView::keyPressEvent()" << endl;

	if (ev->key()==TDEGlobalSettings::contextMenuKey()) {
		if (m_selectedConnection) {
			emit connectionContextMenuRequest(
				mapToGlobal(m_selectedConnection->connectionRect().center()) );
		}
//		m_popup->exec( mapToGlobal( m_focusedTableView ? m_focusedTableView->pos() + m_focusedTableView->rect().center() : rect().center() ) );
//		executePopup();
	}
	else {
		if(ev->key() == Key_Delete)
			removeSelectedObject();
	}
}

void
KexiRelationView::recalculateSize(int width, int height)
{
	kdDebug() << "recalculateSize(" << width << ", " << height << ")" << endl;
	int newW = contentsWidth(), newH = contentsHeight();
	kdDebug() << "contentsSize(" << newW << ", " << newH << ")" << endl;

	if(newW < width)
		newW = width;

	if(newH < height)
		newH = height;

	resizeContents(newW, newH);
}

/*! Resizes contents to size exactly enough to fit tableViews.
	Executed on every tableView's drop event. 
*/
void
KexiRelationView::stretchExpandSize()
{
	int max_x=-1, max_y=-1;
	TQDictIterator<KexiRelationViewTableContainer> it(m_tables);
	for (;it.current(); ++it) {
		if (it.current()->right()>max_x)
			max_x = it.current()->right();
		if (it.current()->bottom()>max_y)
			max_y = it.current()->bottom();
	}
	TQPoint p = viewportToContents(TQPoint(max_x, max_y) + TQPoint(3,3)); //3 pixels margin
	resizeContents(p.x(), p.y());
}

void KexiRelationView::slotTableViewEndDrag()
{
	kdDebug() << "END DRAG!" <<endl;
	stretchExpandSize();

}

void
KexiRelationView::removeSelectedObject()
{
	if (m_selectedConnection) {
		removeConnection(m_selectedConnection);

#if 0
	RelationList l = m_relation->projectRelations();
	RelationList nl;
	for(RelationList::Iterator it = l.begin(); it != l.end(); ++it)
	{
		if((*it).srcTable == m_selectedConnection->connection().srcTable
			&& (*it).rcvTable == m_selectedConnection->connection().rcvTable
			&& (*it).srcField == m_selectedConnection->connection().srcField
			&& (*it).rcvField == m_selectedConnection->connection().rcvField)
		{
			kdDebug() << "KexiRelationView::removeSelectedConnection(): matching found!" << endl;
//			l.remove(it);
		}
		else
		{
			nl.append(*it);
		}
	}

	kdDebug() << "KexiRelationView::removeSelectedConnection(): d2" << endl;
	m_relation->updateRelationList(this, nl);
	kdDebug() << "KexiRelationView::removeSelectedConnection(): d3" << endl;
#endif
		delete m_selectedConnection;
		m_selectedConnection = 0;
//		invalidateActions();
	}
	else if (m_focusedTableView) {
		KexiRelationViewTableContainer *tmp = m_focusedTableView;
		m_focusedTableView = 0;
		hideTable(tmp);
	}
}

void
KexiRelationView::hideTable(KexiRelationViewTableContainer* tableView)
{
/*! @todo what about query? */
	KexiDB::TableSchema *ts = tableView->schema()->table();
	//for all connections: find and remove all connected with this table
	TQPtrListIterator<KexiRelationViewConnection> it(m_connectionViews);
	for (;it.current();) {
		if (it.current()->masterTable() == tableView 
			|| it.current()->detailsTable() == tableView)
		{
			//remove this
			removeConnection(it.current());
		}
		else {
			++it;
		}
	}
	m_tables.take(tableView->schema()->name());
	delete tableView;
	emit tableHidden( *ts );
}

void
KexiRelationView::hideAllTablesExcept( KexiDB::TableSchema::List* tables )
{
//! @todo what about queries?
	for (TablesDictIterator it(m_tables); it.current();) {
		KexiDB::TableSchema *table = it.current()->schema()->table();
		if (!table || tables->findRef( table )!=-1) {
			++it;
			continue;
		}
		hideTable(it.current());
	}
}

void
KexiRelationView::removeConnection(KexiRelationViewConnection *conn)
{
	emit aboutConnectionRemove(conn);
	m_connectionViews.remove(conn);
	updateContents(conn->connectionRect());
	kdDebug() << "KexiRelationView::removeConnection()" << endl;
}

void KexiRelationView::slotTableViewGotFocus()
{
	if (m_focusedTableView == sender())
		return;
	kdDebug() << "GOT FOCUS!" <<endl;
	clearSelection();
//	if (m_focusedTableView)
//		m_focusedTableView->unsetFocus();
	m_focusedTableView = (KexiRelationViewTableContainer*)sender();
//	invalidateActions();
	emit tableViewGotFocus();
}

TQSize KexiRelationView::sizeHint() const
{
	return TQSize(TQScrollView::sizeHint());//.width(), 600);
}

void KexiRelationView::clear()
{
	removeAllConnections();
	m_tables.setAutoDelete(true);
	m_tables.clear();
	m_tables.setAutoDelete(false);
	updateContents();
}

void KexiRelationView::removeAllConnections()
{
	clearSelection(); //sanity
	m_connectionViews.setAutoDelete(true);
	m_connectionViews.clear();
	m_connectionViews.setAutoDelete(false);
	updateContents();
}

/*

void KexiRelationView::tableHeaderContextMenuRequest(const TQPoint& pos)
{
	if (m_focusedTableView != sender())
		return;
	kdDebug() << "HEADER CTXT MENU!" <<endl;
	invalidateActions();
	m_tableQueryPopup->exec(pos);	
}

//! Invalidates all actions availability
void KexiRelationView::invalidateActions()
{
	setAvailable("edit_delete", m_selectedConnection || m_focusedTableView);
}

void KexiRelationView::executePopup( TQPoint pos )
{
	if (pos==TQPoint(-1,-1)) {
		pos = mapToGlobal( m_focusedTableView ? m_focusedTableView->pos() + m_focusedTableView->rect().center() : rect().center() );
	}
	if (m_focusedTableView)
		m_tableQueryPopup->exec(pos);
	else if (m_selectedConnection)
		m_connectionPopup->exec(pos);
}
*/

#include "kexirelationview.moc"
