/* This file is part of the KDE project
   Copyright (C) 2003 - 2004 Dag Andersen <danders@get2net.dk>

   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;
   version 2 of the License.

   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 "kptpertcanvas.h"
#include "kptnode.h"
#include "kptrelation.h"
#include "kptrelationdialog.h"
#include "kptcanvasitem.h"

#include <tqbuffer.h>
#include <tqtimer.h>
#include <tqclipboard.h>
#include <tqprogressdialog.h>
#include <tqobjectlist.h>
#include <tqpainter.h>
#include <tqheader.h>
#include <tqcursor.h>
#include <tqrect.h>
#include <tqsize.h>
#include <tqptrlist.h>

#include <KoStore.h>
#include <tdetempfile.h>
#include <tdelocale.h>
#include <kdebug.h>
#include <tdeapplication.h>
#include <tdemessagebox.h>
#include <assert.h>
#include <tdemultipledrag.h>
#include <tdelistview.h>

namespace KPlato
{

PertCanvas::PertCanvas( TQWidget *parent )
    : TQCanvasView( parent, "Pert canvas" /*WNorthWestGravity WStaticContents| WResizeNoErase | WRepaintNoErase */),
	m_verticalGap(20),
	m_horizontalGap(10),
	m_itemSize(100,30)

{
    //setHScrollBarMode(TQScrollView::AlwaysOn);
    m_canvas = new TQCanvas( this );
    setCanvas( m_canvas );
}

PertCanvas::~PertCanvas()
{
}

void PertCanvas::draw(Project& project)
{
    //kdDebug()<<k_funcinfo<<endl;
    clear();
    updateContents();

    // First make node items
    TQPtrListIterator<Node> nit(project.childNodeIterator());
    for ( ; nit.current(); ++nit ) {
        createChildItems(createNodeItem(nit.current()));
    }

    // First all items with relations
    TQPtrDictIterator<PertNodeItem> it(m_nodes);
    for(; it.current(); ++it)
    {
        if (!(it.current()->hasParent()) && it.current()->hasChild())
        {
            m_rows.append(new TQMemArray<bool>(1)); // New node always goes into new row, first column
            it.current()->move(this, m_rows.count()-1, 0); // item also moves it's children
        }
    }
    // now items without relations
    for(it.toFirst(); it.current(); ++it)
    {
        if (!(it.current()->hasParent() || it.current()->hasChild()))
        {
            m_rows.append(new TQMemArray<bool>(1)); // New node always goes into new row, first column
            it.current()->move(this, m_rows.count()-1, 0);
        }
    }
    drawRelations(); // done _after_ all nodes are drawn
    TQSize s = canvasSize();
    m_canvas->resize(s.width(), s.height());
    update();
}

PertNodeItem *PertCanvas::createNodeItem(Node *node)
{
    PertNodeItem *item = m_nodes.find(node);
    if (!item)
    {
        if ( node->type() == Node::Type_Project)
            kdDebug()<<k_funcinfo<<"Project nodes should not have relations"<<endl;
        else if (node->type() == Node::Type_Subproject)
            item  = new PertProjectItem(this, *node);
        else if (node->type()== Node::Type_Summarytask)
            item  = new PertTaskItem(this, *node);
        else if (node->type()== Node::Type_Task)
            item  = new PertTaskItem(this, *node);
        else if (node->type() == Node::Type_Milestone)
            item  = new PertMilestoneItem(this, *node);
        else
            kdDebug()<<k_funcinfo<<"Not implemented yet"<<endl;

        if (item)
            m_nodes.insert(node, item);
    }
    return item;
}

void PertCanvas::createChildItems(PertNodeItem *parentItem)
{
    //kdDebug()<<k_funcinfo<<"parentItem="<<(parentItem ? parentItem->node().name() : "nil")<<endl;
    if (!parentItem)
        return;

    TQPtrListIterator<Relation> it(parentItem->node().dependChildNodes());
    for (; it.current(); ++it)
    {
        PertNodeItem *childItem = createNodeItem(it.current()->child());
        if (childItem)
            parentItem->addChildRelation(it.current(), childItem);
            m_relations.append(it.current());
    }

    // Now my children
	TQPtrListIterator<Node> nit(parentItem->node().childNodeIterator());
    for ( ; nit.current(); ++nit ) {
        createChildItems(createNodeItem(nit.current()));
	}
}

void PertCanvas::drawRelations()
{
	//kdDebug()<<k_funcinfo<<endl;
    TQPtrListIterator<Relation> it(m_relations);
    for (; it.current(); ++it)
    {
        PertNodeItem *parentItem = m_nodes.find(it.current()->parent());
        PertNodeItem *childItem = m_nodes.find(it.current()->child());
        if (parentItem && childItem)
        {
            PertRelationItem *item = new PertRelationItem(this, parentItem, childItem, it.current());
            item->show();
        }
    }
}

void PertCanvas::mapNode(PertNodeItem *item)
{
	//kdDebug()<<k_funcinfo<<endl;
    if (! m_rows.at(item->row()) || (item->column() >= 0 && m_rows.at(item->row())->count() <= uint(item->column())))
    {
        kdError()<<k_funcinfo<<item->node().name()<<": non existing map for: ("<<item->row()<<","<<item->column()<<")"<<endl;
        return;
    }
    m_rows.at(item->row())->at(item->column()) = true;
}

void PertCanvas::mapChildNode(PertNodeItem *parentItem, PertNodeItem *childItem, Relation::Type type)
{
	//kdDebug()<<k_funcinfo<<"Parent: "<<parentItem->node().name()<<" to child: "<<(childItem ? childItem->node().name() : "None")<<endl;
    if (!childItem)
    {   // shouldn't happen...
        kdError()<<k_funcinfo<<"No childItem"<<endl;
        return;
    }
    int row = parentItem->row();
    int col = parentItem->column();
    int chRow = childItem->row();
    int chCol = childItem->column();
    bool chMapped = (chRow > -1 && chCol > -1);
	//kdDebug()<<k_funcinfo<<"Parent: "<<parentItem->node().name()<<" at ("<<row<<","<<col<<"): Moving "<<childItem->node().name()<<" from: "<<chRow<<","<<chCol<<endl;

    if (type == Relation::StartStart ||
        type == Relation::FinishFinish)
    {
        // node goes into row below parent, at least same col
        if (chMapped)
        {
            m_rows.at(chRow)->at(chCol) = false;
            //kdDebug()<<k_funcinfo<<" Moving "<<childItem->node().name()<<" from: "<<chRow<<","<<chCol<<endl;
            if (chRow <= row)
            {
                chRow = row+1;
                if (chRow >= 0 && m_rows.count() <= uint(chRow)) {
                    m_rows.append(new TQMemArray<bool>(1)); // make a new row
                    chRow = m_rows.count()-1;  // to be safe
                }
                //kdDebug()<<k_funcinfo<<" Moving "<<childItem->node().name()<<" to row: "<<chRow<<endl;
            }
            if (chCol < col)
            {
                chCol = col;
                if (chCol >= 0 && m_rows.at(chRow)->count() <= uint(chCol))  // col does not exist
                    m_rows.at(chRow)->resize(chCol+1);
                
                //kdDebug()<<k_funcinfo<<" Moved "<<childItem->node().name()<<" to col: "<<chCol<<endl;
            }

        }
        else
        {
            if (!(m_rows.at(row+1)) ||                        // next row does not exists
                m_rows.at(row+1)->at(col) == true)  // col is not free
            {
                m_rows.append(new TQMemArray<bool>(col+1)); // make a new row
            }
            else if (col >= 0 && m_rows.at(row+1)->count() <= uint(col))  // col does not exist
                m_rows.at(row)->resize(col+1);

            chRow = m_rows.count() -1;
            chCol = col;
        }
    }
    else if (type == Relation::FinishStart)
    {
        // node goes into same row, next col if col free
        if (chMapped)
        {
            m_rows.at(chRow)->at(chCol) = false;
            if (chRow < row)
                chRow = row;
            if (chCol <= col)
            {
                chCol = col+1;
            }
            if (chCol >= 0 && m_rows.at(chRow)->count() <= uint(chCol))  // col does not exist
                m_rows.at(chRow)->resize(chCol+1);
        }
        else
        {
            ++col;
            if (col >= 0 && m_rows.at(row)->count() <= uint(col))
                m_rows.at(row)->resize(col+1); // make new column
            else if (m_rows.at(row)->at(col) = true)
                m_rows.append(new TQMemArray<bool>(col+1)); // col not free, so make a new row

            chRow = m_rows.count() -1;
            chCol = col;
        }
    }
    else
    {
        kdError()<<k_funcinfo<<"Unknow relation type"<<endl;
        return;
    }
    childItem->move(this, chRow, chCol);
}

TQSize PertCanvas::canvasSize()
{
    //kdDebug()<<k_funcinfo<<endl;
	TQSize s(0,0);
    TQCanvasItemList list = canvas()->allItems();
    TQCanvasItemList::Iterator it = list.begin();
    for (; it != list.end(); ++it)
    {
	    TQRect r = (*it)->boundingRect();
		s.setWidth(TQMAX(s.width(), r.right()));
		s.setHeight(TQMAX(s.height(), r.bottom()));
	}
	s.setWidth(s.width()+20);
	s.setHeight(s.height()+20);
	return s;
}

void PertCanvas::clear()
{
    m_nodes.clear();
    m_relations.clear();
    m_rows.clear();
    TQCanvasItemList list = canvas()->allItems();
    TQCanvasItemList::Iterator it = list.begin();
    for (; it != list.end(); ++it)
    {
        if ( *it )
            delete *it;
    }
}

void PertCanvas::contentsMousePressEvent ( TQMouseEvent * e )
{
    //kdDebug()<<k_funcinfo<<" gl.X,gl.Y="<<e->globalX()<<","<<e->globalY()<<" x,y="<<e->x()<<","<<e->y()<<endl;
    switch (e->button())
    {
        case TQt::LeftButton:
        {
            break;
        }
        case TQt::RightButton:
        {
            PertNodeItem *item = selectedItem();
            if (item)
                item->setSelected(false);
            canvas()->update();
            
            TQCanvasItemList l = canvas()->collisions(e->pos());
            for (TQCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it)
            {
                if ( (*it)->rtti() == PertProjectItem::RTTI ||
                     (*it)->rtti() == PertTaskItem::RTTI  ||
                     (*it)->rtti() == PertMilestoneItem::RTTI )
                {
                    PertNodeItem *item = (PertNodeItem *)(*it);
                    {
                        item->setSelected(true);
                        canvas()->update();
                        emit rightButtonPressed(&(item->node()), e->globalPos());
                        if (item == selectedItem()) {
                            // item maybe deleted
                            item->setSelected(false);
                        }
                        canvas()->update();
                        break;
                    }
                }
            }
            break;
        }
        case TQt::MidButton:
            break;
        default:
            break;
    }
}

void PertCanvas::contentsMouseReleaseEvent ( TQMouseEvent * e )
{
    //kdDebug()<<k_funcinfo<<" gl.X,gl.Y="<<e->globalX()<<","<<e->globalY()<<" x,y="<<e->x()<<","<<e->y()<<endl;
    switch (e->button())
    {
        case TQt::LeftButton:
        {
            bool hit = false;
            TQCanvasItemList l = canvas()->collisions(e->pos());
            for (TQCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it)
		    {
	            if ( (*it)->rtti() == PertProjectItem::RTTI ||
	                 (*it)->rtti() == PertTaskItem::RTTI  ||
	                 (*it)->rtti() == PertMilestoneItem::RTTI )
				{
                    hit = true;
				    PertNodeItem *item = (PertNodeItem *)(*it);
					PertNodeItem *par = selectedItem();
					if ( !par)
					{
						//kdDebug()<<k_funcinfo<<" First node="<<item->node().name()<<endl;
						item->setSelected(true);
						canvas()->update();
						return;
					}
					par->setSelected(false);
					if (&(item->node()) == &(par->node()))
					{
						break;
					}
					//kdDebug()<<k_funcinfo<<" Second node="<<item->node().name()<<endl;
					// open relation dialog
					if (!par->node().legalToLink(&(item->node()))) {
                        KMessageBox::sorry(this, i18n("Cannot link these nodes"));
					} else {
						Relation *rel = item->node().findRelation(&(par->node()));
						if (rel)
                            emit modifyRelation(rel);
						else
							emit addRelation(&(par->node()), &(item->node()));
					}
					break;
				}
			}
            if (!hit) {
                PertNodeItem *i = selectedItem();
                if (i) i->setSelected(false);
            }
            canvas()->update();
            break;
        }
        case TQt::RightButton:
        {
            break;
        }
        case TQt::MidButton:
            break;
        default:
            break;
    }
}

PertNodeItem *PertCanvas::selectedItem()
{
    TQCanvasItemList list = canvas()->allItems();
    TQCanvasItemList::Iterator it = list.begin();
    for (; it != list.end(); ++it)
    {
        if ( (*it)->isSelected() )
		{
		if ( (*it)->rtti() == PertProjectItem::RTTI ||
			(*it)->rtti() == PertTaskItem::RTTI  ||
			(*it)->rtti() == PertMilestoneItem::RTTI )
                return (PertNodeItem *)(*it);
		}
    }
	return 0;
}

Node *PertCanvas::selectedNode() { 
    return selectedItem() ? &(selectedItem()->node()) : 0; 
}
 
#ifndef NDEBUG
void PertCanvas::printDebug( int /*info*/ )
{
}
#endif

}  //KPlato namespace

#include "kptpertcanvas.moc"
