/* This file is part of the KDE project
   Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org>
   Copyright (C) 2004, 2005 Dag Andersen <danders@get2net.dk>
   Copyright (C) 2006 Raphael Langerhorst <raphael.langerhorst@kdemail.net>
   Copyright (C) 2006 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; 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 "kptpart.h"
#include "kptview.h"
#include "kptfactory.h"
#include "kptproject.h"
#include "kptprojectdialog.h"
#include "kptresource.h"
#include "kptcontext.h"
#include "kptganttview.h"
#include "KDGanttViewTaskLink.h"

#include <tqpainter.h>
#include <tqfileinfo.h>

#include <kdebug.h>
#include <tdeconfig.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <tdestandarddirs.h>
#include <kcommand.h>
#include <KoTemplateChooseDia.h>
#include <KoCommandHistory.h>
#include <KoGlobal.h>

#define CURRENT_SYNTAX_VERSION "0.5"

namespace KPlato
{

Part::Part(TQWidget *parentWidget, const char *widgetName,
		 TQObject *parent, const char *name, bool singleViewMode)
    : KoDocument(parentWidget, widgetName, parent, name, singleViewMode),
      m_project(0), m_projectDialog(0), m_parentWidget(parentWidget), m_view(0),
      m_embeddedGanttView(new GanttView(parentWidget)),
      m_embeddedContext(new Context()), m_embeddedContextInitialized(false),
      m_context(0), m_xmlLoader()
{
    m_update = m_calculate = false;
    m_commandHistory = new KoCommandHistory(actionCollection());

    setInstance(Factory::global());
    setTemplateType("kplato_template");
    m_config.setReadWrite(isReadWrite()|| !isEmbedded());
    m_config.load();

    delete m_project;
    m_project = new Project(); // after config is loaded

    connect(m_commandHistory, TQ_SIGNAL(commandExecuted()), TQ_SLOT(slotCommandExecuted()));
    connect(m_commandHistory, TQ_SIGNAL(documentRestored()), TQ_SLOT(slotDocumentRestored()));

    //FIXME the following is really dirty, we should make KPlato::Context a real class
    //      with getter and setter and signals when content changes, thus we can keep track
    TQTimer* timer = new TQTimer(this,"context update timer");
    connect(timer,TQ_SIGNAL(timeout()),this,TQ_SLOT(slotCopyContextFromView()));
    timer->start(500);
}


Part::~Part() {
    m_config.save();
    delete m_commandHistory; // before project, in case of dependencies...
    delete m_project;
    delete m_projectDialog;
    if (m_embeddedGanttView) {
      delete m_embeddedGanttView;
      m_embeddedGanttView = 0L;
    }
    if (m_embeddedContext) {
      delete m_embeddedContext;
      m_embeddedContext = 0L;
    }
}


bool Part::initDoc(InitDocFlags flags, TQWidget* parentWidget) {
    bool result = true;

    if (flags==KoDocument::InitDocEmpty)
    {
        delete m_project;
        m_project = new Project();
        setAutoSave(0); // disable
        setModified(false);
        return true;
    }

    TQString templateDoc;
    KoTemplateChooseDia::ReturnType ret;
    KoTemplateChooseDia::DialogType dlgtype;
    if (flags != KoDocument::InitDocFileNew )
        dlgtype = KoTemplateChooseDia::Everything;
    else
        dlgtype = KoTemplateChooseDia::OnlyTemplates;

    ret = KoTemplateChooseDia::choose(Factory::global(), templateDoc,
                                      dlgtype,
                                      "kplato_template",
                                      parentWidget);
    if (ret == KoTemplateChooseDia::Template) {
        resetURL();
        result = loadNativeFormat(templateDoc);
        if ( !result )
            showLoadingErrorDialog();
    } else if (ret == KoTemplateChooseDia::File) {
        KURL url(templateDoc);
        kdDebug() << "Part::initDoc opening URL " << url.prettyURL() <<endl;
        result = openURL(url);
    } else if (ret == KoTemplateChooseDia::Empty) {
        // Make a fresh project and let the user enter some info
        delete m_project;
        m_project = new Project();
       // an emty project should be empty
        // m_projectDialog = new ProjectDialog(*m_project, m_view);
        // m_projectDialog->exec();

        result = true;
    } else {
        result = false;
    }
    setAutoSave(0); // disable
    setModified(false);
    return result;
}


KoView *Part::createViewInstance(TQWidget *parent, const char *name) {
    m_view = new View(this, parent, name);
    connect(m_view,TQ_SIGNAL(destroyed()),this,TQ_SLOT(slotViewDestroyed()));

    // If there is a project dialog this should be deleted so it will
    // use the m_view as parent. If the dialog will be needed again,
    // it will be made at that point
    if (m_projectDialog != 0) {
	kdDebug() << "Deleting m_projectDialog because of new ViewInstance\n";
	delete m_projectDialog;
	m_projectDialog = 0;
    }
    if (m_context)
        m_view->setContext( *m_context );
    else if (m_embeddedContext && m_embeddedContextInitialized)
        m_view->setContext( *m_embeddedContext );
    else {
        // Activate menu actions. Assumes ganttview, we don't get any 
        // 'aboutToShow' signal. Need to redo action control.
        m_view->setTaskActionsEnabled(true);
    }
    //m_view->setBaselineMode(getProject().isBaselined()); FIXME: Removed for this release  
    return m_view;
}


void Part::editProject() {

    TQWidget* parent = m_parentWidget;
    if (m_view)
      parent = m_view;
      
    if (m_projectDialog == 0)
	// Make the dialog
	m_projectDialog = new ProjectDialog(*m_project, parent);

    m_projectDialog->exec();
}


bool Part::loadXML(TQIODevice *, const TQDomDocument &document) {
    TQTime dt;
    dt.start();
    emit sigProgress( 0 );

    TQString value;
    TQDomElement plan = document.documentElement();

    // Check if this is the right app
    value = plan.attribute("mime", TQString());
    if (value.isEmpty()) {
        kdError() << "No mime type specified!" << endl;
        setErrorMessage(i18n("Invalid document. No mimetype specified."));
        return false;
    }
    else if (value != "application/x-vnd.kde.kplato") {
        kdError() << "Unknown mime type " << value << endl;
        setErrorMessage(i18n("Invalid document. Expected mimetype application/x-vnd.kde.kplato, got %1").arg(value));
        return false;
    }
    TQString m_syntaxVersion = plan.attribute("version", CURRENT_SYNTAX_VERSION);
    if (m_syntaxVersion > CURRENT_SYNTAX_VERSION) {
        int ret = KMessageBox::warningContinueCancel(
            0, i18n("This document was created with a newer version of KPlato (syntax version: %1)\n"
                    "Opening it in this version of KPlato will lose some information.").arg(m_syntaxVersion),
            i18n("File-Format Mismatch"), i18n("Continue") );
        if (ret == KMessageBox::Cancel)
        {
            setErrorMessage("USER_CANCELED");
            return false;
        }
    }
    emit sigProgress(5);

    TQDomNodeList list = plan.childNodes();
    if (list.count() > 2) {
        // TODO: Make a proper bitching about this
        kdDebug() << "*** Error ***\n";
        kdDebug() << "  Children count should be 1 but is " << list.count()
                << "\n";
        return false;
    }
    m_xmlLoader.startLoad();
    for (unsigned int i = 0; i < list.count(); ++i) {
        if (list.item(i).isElement()) {
            TQDomElement e = list.item(i).toElement();

            if (e.tagName() == "context") {
                delete m_context;
                m_context = new Context();
                m_context->load(e);
            } else if (e.tagName() == "project") {
                Project *newProject = new Project();
                if (newProject->load(e)) {
                    // The load went fine. Throw out the old project
                    delete m_project;
                    m_project = newProject;
                    delete m_projectDialog;
                    m_projectDialog = 0;
                }
                else {
                    delete newProject;
                    m_xmlLoader.addMsg(XMLLoaderObject::Errors, "Loading of project failed");
                    //TODO add some ui here
                }
            }
        }
    }
    m_xmlLoader.stopLoad();
    emit sigProgress(100); // the rest is only processing, not loading

    kdDebug() << "Loading took " << (float)(dt.elapsed()) / 1000 << " seconds" << endl;

    // do some sanity checking on document.
    emit sigProgress(-1);

    m_commandHistory->clear();
    m_commandHistory->documentSaved();
    setModified( false );
    if (m_view)
        m_view->slotUpdate(false);
    return true;
}

TQDomDocument Part::saveXML() {
    TQDomDocument document("kplato");

    document.appendChild(document.createProcessingInstruction(
			"xml",
			"version=\"1.0\" encoding=\"UTF-8\""));

    TQDomElement doc = document.createElement("kplato");
    doc.setAttribute("editor", "KPlato");
    doc.setAttribute("mime", "application/x-vnd.kde.kplato");
    doc.setAttribute("version", CURRENT_SYNTAX_VERSION);
    document.appendChild(doc);

    delete m_context;
    m_context = 0;
    if (m_view) {
        m_context = new Context();
        m_view->getContext(*m_context);
    }
    if (m_context) {
        m_context->save(doc);
    }
    // Save the project
    m_project->save(doc);

    m_commandHistory->documentSaved();
    return document;
}


void Part::slotDocumentRestored() {
    //kdDebug()<<k_funcinfo<<endl;
    setModified(false);
}


void Part::paintContent(TQPainter &painter, const TQRect &rect,
			   bool /*transparent*/,
			   double zoomX, double /*zoomY*/)
{
    kdDebug() << "----------- KPlato: Part::paintContent ------------" << endl;
    if (isEmbedded() && m_embeddedGanttView && m_project)
    {
        if (m_embeddedContext)
        {
            int ganttsize = m_embeddedContext->ganttview.ganttviewsize;
            int tasksize = m_embeddedContext->ganttview.taskviewsize;
            bool showtaskname = m_embeddedContext->ganttview.showTaskName;
            
//            m_embeddedContext->ganttview.ganttviewsize += m_embeddedContext->ganttview.taskviewsize;
//            m_embeddedContext->ganttview.taskviewsize = 0;  //TODO this doesn't have any effect?! (bug?)
            m_embeddedContext->ganttview.showTaskName = true;  //since task view is not shown(?), show name in the gantt itself
            
            m_embeddedGanttView->setContext( m_embeddedContext->ganttview, *m_project );
            
            m_embeddedContext->ganttview.ganttviewsize = ganttsize;
            m_embeddedContext->ganttview.taskviewsize = tasksize;
            m_embeddedContext->ganttview.showTaskName = showtaskname;
        }
        else
        {
            kdWarning() << "Don't have any context to set!" << endl;
        }
        painter.setClipRect(rect, TQPainter::CoordPainter);
        // We don't support zoom yet, so use the painters scaling
        double d_zoom = 1.0;
        setZoomAndResolution(100, KoGlobal::dpiX(), KoGlobal::dpiY());
        if ( m_zoomedResolutionX != zoomX ) {
            d_zoom *= ( zoomX / m_zoomedResolutionX );
            painter.scale(d_zoom, d_zoom);
        }
        
        m_embeddedGanttView->clear();
        m_embeddedGanttView->draw(*m_project);
        m_embeddedGanttView->drawOnPainter(&painter,rect);
    }
    // ####### handle transparency

    // Need to draw only the document rectangle described in the parameter rect.
//     int left = rect.left() / 20;
//     int right = rect.right() / 20 + 1;
//     int top = rect.top() / 20;
//     int bottom = rect.bottom() / 20 + 1;

//     for( int x = left; x < right; ++x )
//         painter.drawLine( x * 40, top * 20, 40 * 20, bottom * 20 );
//     for( int y = left; y < right; ++y )
//         painter.drawLine( left * 20, y * 20, right * 20, y * 20 );
}


void Part::addCommand(KCommand * cmd, bool execute)
{
    m_commandHistory->addCommand(cmd, execute);
}

void Part::slotCommandExecuted() {
    //kdDebug()<<k_funcinfo<<endl;
    setModified(true);
    kdDebug() << "------- KPlato, is embedded: " << isEmbedded() << endl;
    if (m_view == NULL)
      return;
      
    if (m_calculate)
        m_view->slotUpdate(false/*config().behavior().calculationMode == Behavior::OnChange*/);
    else if (m_update)
        m_view->slotUpdate(false);

    if (m_baseline)
        m_view->setBaselineMode(getProject().isBaselined());

    m_update = m_calculate = m_baseline = false;
}

void Part::slotCopyContextFromView()
{
    if (m_view)
    {
//         kdDebug() << "Updating embedded context from view context." << endl;
        this->m_view->getContext( *m_embeddedContext );
        this->m_embeddedContextInitialized = true;
    }
//     else
//     {
//         kdDebug() << "Not updating the context." << endl;
//         if (m_context)
//           kdDebug() << "Current View: " << m_context->currentView << endl;
//     }
}

void Part::slotViewDestroyed()
{
    m_view = NULL;
}

void Part::setCommandType(int type) {
    //kdDebug()<<k_funcinfo<<"type="<<type<<endl;
    if (type == 0)
        m_update = true;
    else if (type == 1)
        m_calculate = true;
    else if (type == 2)
        m_baseline = true;
}

void Part::generateWBS() {
    m_project->generateWBS(1, m_wbsDefinition);
}

}  //KPlato namespace

#include "kptpart.moc"
