/*
 * Kivio - Visual Modelling and Flowcharting
 * Copyright (C) 2000-2001 theKompany.com & Dave Marotti
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
#include <qdom.h>
#include <qtextstream.h>
#include <qbuffer.h>
#include <qtabwidget.h>
#include <qpaintdevicemetrics.h>

#include "kivio_doc.h"
#include "kivio_page.h"
#include "kivio_map.h"
#include "kivio_view.h"
#include "kivio_factory.h"
#include "export_page_dialog.h"
#include "kivio_common.h"
#include "kivio_group_stencil.h"
#include "kivio_icon_view.h"
#include "kivio_layer.h"
#include "kivio_painter.h"
#include "kivio_screen_painter.h"
#include "kivio_stencil.h"
#include "kivio_stencil_spawner_set.h"
#include "kivioglobal.h"
#include "kivio_config.h"
#include "polylineconnectorspawner.h"

#include "stencilbarbutton.h"

#include <unistd.h>
#include <kmessagebox.h>
#include <klocale.h>
#include <kprinter.h>
#include <kdebug.h>
#include <kurl.h>
#include <kapplication.h>
#include <assert.h>
#include <qdatetime.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <pwd.h>
#include <qdir.h>
#include <qfileinfo.h>
#include <qfile.h>
#include <qpixmap.h>

#include <qpainter.h>
#include <qpen.h>
#include <qfont.h>
#include <qvaluelist.h>


#include <kstandarddirs.h>
#include <kpopupmenu.h>
#include <kmenubar.h>
#include <kiconloader.h>
#include <khelpmenu.h>
#include <kconfig.h>
#include <koTemplateChooseDia.h>
#include <koFilterManager.h>
#include <koStoreDevice.h>
#include "KIvioDocIface.h"
#include <kcommand.h>
#include <kozoomhandler.h>
#include <koApplication.h>
#include <kglobal.h>
#include <kocommandhistory.h>
#include <koxmlwriter.h>
#include <koGenStyles.h>
#include <koOasisSettings.h>
#include <kodom.h>
#include <koxmlns.h>

//using namespace std;

/*****************************************************************************
 *
 * KivioDoc
 *
 *****************************************************************************/

QPtrList<KivioDoc>* KivioDoc::s_docs = 0;
int KivioDoc::s_docId = 0;

KivioDoc::KivioDoc( QWidget *parentWidget, const char* widgetName, QObject* parent, const char* name, bool singleViewMode )
: KoDocument( parentWidget, widgetName, parent, name, singleViewMode )
{
  dcop = 0;
  if (!s_docs)
    s_docs = new QPtrList<KivioDoc>;

  s_docs->append(this);

  m_pLstSpawnerSets = new QPtrList<KivioStencilSpawnerSet>;
  m_pLstSpawnerSets->setAutoDelete(true);
  m_loadTimer = 0;
  m_currentFile = 0;

  setInstance( KivioFactory::global(), false );

  if ( !name )
  {
    QString tmp( "Document%1" );	//lukas: FIXME
    tmp = tmp.arg( s_docId++ );
    setName( tmp.latin1() );
  }

  m_iPageId = 1;
  m_bLoading = false;
  m_pMap = new KivioMap( this, "Map" );

  // Load autoLoadStencils in internal StencilSpawnerSet
  m_pInternalSet = new KivioStencilSpawnerSet("Kivio_Internal");
  m_pInternalSet->setId("Kivio - Internal - Do Not Touch");
  QStringList list = instance()->dirs()->findAllResources("data",instance()->instanceName()+"/autoloadStencils/*",true,false);
  QStringList::ConstIterator pIt = list.begin();
  QStringList::ConstIterator pEnd = list.end();

  for (; pIt != pEnd; ++pIt )
  {
    m_pInternalSet->loadFile(*pIt);
  }

  // Add the polyline connector spawner to the internal stencil set.
  addInternalStencilSpawner(new Kivio::PolyLineConnectorSpawner(internalSpawnerSet()));

  initConfig();

  //laurent: Why don't use menu history for undo/redo command ? sync with other koffice application
  m_commandHistory = new KoCommandHistory( actionCollection(),  /*false*/true  ) ;
  connect( m_commandHistory, SIGNAL( documentRestored() ), this, SLOT( slotDocumentRestored() ) );
  connect( m_commandHistory, SIGNAL( commandExecuted() ), this, SLOT( slotCommandExecuted() ) );


  if ( name )
      dcopObject();
}

DCOPObject* KivioDoc::dcopObject()
{
  if ( !dcop ) {
    dcop = new KIvioDocIface( this );
  }

  return dcop;
}

QPtrList<KivioDoc>& KivioDoc::documents()
{
  if ( s_docs == 0 ) {
    s_docs = new QPtrList<KivioDoc>;
  }

  return *s_docs;
}

bool KivioDoc::initDoc(InitDocFlags flags, QWidget* parentWidget)
{
  KivioPage *t = createPage();
  m_pMap->addPage( t );
  m_docOpened = false; // Used to for a hack that make kivio not crash if you cancel startup dialog.

  if(flags == KoDocument::InitDocEmpty) {
    setEmpty();
    m_docOpened = true; // Used to for a hack that make kivio not crash if you cancel startup dialog.
    return true;
  }

  QString f;
  KoTemplateChooseDia::ReturnType ret;
  KoTemplateChooseDia::DialogType dlgtype;

  if(flags != KoDocument::InitDocFileNew) {
    dlgtype = KoTemplateChooseDia::Everything;
    initConfig();
  } else {
    dlgtype = KoTemplateChooseDia::OnlyTemplates;
  }

  ret = KoTemplateChooseDia::choose( KivioFactory::global(), f,
                                     dlgtype, "kivio_template", parentWidget );

  if( ret == KoTemplateChooseDia::File ) {
    KURL url(f);
    bool ok = openURL(url);
    m_docOpened = ok; // Used to for a hack that make kivio not crash if you cancel startup dialog.
    return ok;
  } else if ( ret == KoTemplateChooseDia::Template ) {
    QFileInfo fileInfo( f );
    QString fileName( fileInfo.dirPath(true) + "/" + fileInfo.baseName() + ".kft" );
    resetURL();
    bool ok = loadNativeFormat( fileName );
    if ( !ok )
        showLoadingErrorDialog();
    setEmpty();
    m_docOpened = ok; // Used to for a hack that make kivio not crash if you cancel startup dialog.
    return ok;
  } else if ( ret == KoTemplateChooseDia::Empty ) {
    setEmpty();
    m_docOpened = true; // Used to for a hack that make kivio not crash if you cancel startup dialog.
    return true;
  } else {
    return false;
  }
}

KoView* KivioDoc::createViewInstance( QWidget* parent, const char* name )
{
  if (!name) {
    name = "View";
  }

  return new KivioView( parent, name, this );
}

QDomDocument KivioDoc::saveXML()
{
  QDomDocument doc( "kiviodoc" );
  doc.appendChild( doc.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ) );
  QDomElement kivio = doc.createElement( "kiviosheet" );
  kivio.setAttribute( "editor", "Kivio" );
  kivio.setAttribute( "mime", MIME_TYPE );

  kivio.setAttribute( "units", KoUnit::unitName(m_units) );
  gridData.save(kivio,"grid");

  QDomElement viewItemsElement = doc.createElement("ViewItems");
  kivio.appendChild(viewItemsElement);

  doc.appendChild(kivio);

  // Save the list of stencils spawners we have loaded.
  // We save these as the following:
  //
  // <KivioStencilSpawnerSet  id="whatever the id is">
  //   <KivioSMLStencilSpawner id="whatever the id is"/>
  //   <KivioSMLStencilSpawner id="whatever the id is"/>
  // </KivioStencilSpawnerSet>
  // ....
  //
  // This is so we can load them back in, and check that we actually
  // have all these spawners on disk.
  KivioStencilSpawnerSet *pSet = m_pLstSpawnerSets->first();
  while( pSet )
  {
    kivio.appendChild( pSet->saveXML( doc ) );

    pSet = m_pLstSpawnerSets->next();
  }


  QDomElement e = m_pMap->save(doc);
  kivio.appendChild(e);

  setModified(false);
  return doc;
}

bool KivioDoc::saveOasis(KoStore* store, KoXmlWriter* manifestWriter)
{
    KoStoreDevice storeDev(store);
    KoGenStyles styles;

    KoGenStyle pageLayout = Kivio::Config::defaultPageLayout().saveOasis();
    QString layoutName = styles.lookup(pageLayout, "PL");
    KoGenStyle masterPage(KoGenStyle::STYLE_MASTER);
    masterPage.addAttribute("style:page-layout-name", layoutName);
    styles.lookup(masterPage, "Standard", false);

    if(!store->open("content.xml")) {
        return false;
    }

    KoXmlWriter* docWriter = createOasisXmlWriter(&storeDev, "office:document-content");

    docWriter->startElement("office:body");
    docWriter->startElement("office:drawing");

    m_pMap->saveOasis(store, docWriter, &styles); // Save contents

    docWriter->endElement(); // office:drawing
    docWriter->endElement(); // office:body
    docWriter->endElement(); // Root element
    docWriter->endDocument();
    delete docWriter;

    if(!store->close()) {
        return false;
    }

    manifestWriter->addManifestEntry("content.xml", "text/xml");

    if(!store->open("styles.xml")) {
        return false;
    }

    KoXmlWriter* styleWriter = createOasisXmlWriter(&storeDev, "office:document-styles");

    styleWriter->startElement("office:automatic-styles");

    QValueList<KoGenStyles::NamedStyle> styleList = styles.styles(KoGenStyle::STYLE_PAGELAYOUT);
    QValueList<KoGenStyles::NamedStyle>::const_iterator it = styleList.begin();

    for ( ; it != styleList.end(); ++it) {
        (*it).style->writeStyle(styleWriter, styles, "style:page-layout", (*it).name, "style:page-layout-properties");
    }

    styleList = styles.styles(Kivio::STYLE_PAGE);
    it = styleList.begin();

    for ( ; it != styleList.end(); ++it) {
        (*it).style->writeStyle(styleWriter, styles, "style:style", (*it).name, "style:properties");
    }

    styleWriter->endElement(); // office:automatic-styles

    styleList = styles.styles(KoGenStyle::STYLE_MASTER);
    it = styleList.begin();
    styleWriter->startElement("office:master-styles");

    for ( ; it != styleList.end(); ++it) {
        (*it).style->writeStyle(styleWriter, styles, "style:master-page", (*it).name, "");
    }

    styleWriter->endElement(); // office:master-styles

    styleWriter->endElement(); // Root element
    styleWriter->endDocument();
    delete styleWriter;

    if(!store->close()) {
        return false;
    }

    manifestWriter->addManifestEntry("styles.xml", "text/xml");

    if(!store->open("settings.xml")) {
        return false;
    }


    KoXmlWriter* settingsWriter = createOasisXmlWriter(&storeDev, "office:document-settings");
    settingsWriter->startElement("office:settings");
    settingsWriter->startElement("config:config-item-set");
    settingsWriter->addAttribute("config:name", "view-settings");

    KoUnit::saveOasis( settingsWriter, units() );
    saveOasisSettings( *settingsWriter );

    settingsWriter->endElement(); // config:config-item-set
    settingsWriter->endElement(); // office:settings
    settingsWriter->endDocument();

    delete settingsWriter;

    if(!store->close()) {
        return false;
    }

    manifestWriter->addManifestEntry("settings.xml", "text/xml");

    setModified(false);
    return true;
}

void KivioDoc::saveOasisSettings( KoXmlWriter &/*settingsWriter*/ )
{
    //todo
}

bool KivioDoc::loadOasis( const QDomDocument& doc, KoOasisStyles& oasisStyles, const QDomDocument& settings, KoStore* )
{
  kdDebug(43000) << "Start loading OASIS document..." << endl;
  m_bLoading = true;

  QDomElement contents = doc.documentElement();
  QDomElement body(KoDom::namedItemNS( contents, KoXmlNS::office, "body"));

  if(body.isNull()) {
    kdDebug(43000) << "No office:body found!" << endl;
    setErrorMessage(i18n("Invalid OASIS document. No office:body tag found."));
    m_bLoading = false;
    return false;
  }

  body = KoDom::namedItemNS( body, KoXmlNS::office, "drawing");

  if(body.isNull()) {
    kdDebug(43000) << "No office:drawing found!" << endl;
    setErrorMessage(i18n("Invalid OASIS document. No office:drawing tag found."));
    m_bLoading = false;
    return false;
  }

  QDomNode node = body.firstChild();
  QString localName;
  m_pMap->clear();

  // TODO: port to forEachElement
  while(!node.isNull()) {
    localName = node.localName();

    if(localName == "page" /* && and namespace is KoXmlNS::draw*/) {
      KivioPage* p = createPage();
      addPage(p);

      if(!p->loadOasis(node.toElement(), oasisStyles)) {
        m_bLoading = false;
        return false;
      }
    }

    node = node.nextSibling();
  }

  loadOasisSettings( settings );

  emit updateActivePage(m_pMap->firstPage());
  return true;
}


void KivioDoc::loadOasisSettings( const QDomDocument&settingsDoc )
{
    if ( settingsDoc.isNull() )
        return ; //not a error some file doesn't have settings.xml
    KoOasisSettings settings( settingsDoc );
    KoOasisSettings::Items viewSettings = settings.itemSet( "view-settings" );
    if ( !viewSettings.isNull() )
    {
        setUnits(KoUnit::unit(viewSettings.parseConfigItemString("unit")));
        //todo add other config here.
    }
}

bool KivioDoc::loadXML( QIODevice *, const QDomDocument& doc )
{
  m_bLoading = true;

  if ( doc.doctype().name() != "kiviodoc" ) {
    m_bLoading = false;
    return false;
  }

  QDomElement kivio = doc.documentElement();
  if ( kivio.attribute( "mime" ) != "application/x-kivio" &&
    kivio.attribute( "mime" ) != "application/vnd.kde.kivio" )
  {
    kdDebug(43000) << "KivioDoc::loadXML() - Invalid mime type" << endl;
    m_bLoading = false;
    return false;
  }

  QDomNode node = kivio.firstChild();
  while( !node.isNull() )
  {
    QString name = node.nodeName();
    if( name == "KivioMap" )
    {
        if( !m_pMap->loadXML( node.toElement() ) )
        {
            m_bLoading = false;
            return false;
        }
    }
    else if( name == "KivioStencilSpawnerSet" )
    {
        QString id = XmlReadString( node.toElement(), "id", "" );

        if( id.isEmpty() )
        {
            kdDebug(43000) << "KivioDoc::loadXML() - Bad KivioStencilSpawnerSet found, it contains no id!" << endl;
        }
        else
        {
            loadStencilSpawnerSet( id );
        }
    }
    else if( name == "Options" )
    {
        // Not used anymore use users default page instead from Settings to create new pages...
    }
    else
    {
       kdDebug(43000) << "KivioDoc::loadXML() - Unknown node " <<  name << endl;
    }

    node = node.nextSibling();
  }

  QString us = kivio.attribute("units", Kivio::Config::unit());
  bool isInt = false;
  int u = us.toInt(&isInt);

  if(!isInt) {
    setUnits(KoUnit::unit(us));
  } else {
    setUnits(Kivio::convToKoUnit(u));
  }

  if(kivio.hasAttribute("gridIsShow")) {
    gridData.load(kivio,"grid");
  }

  emit updateActivePage(m_pMap->firstPage());
  return true;
}

bool KivioDoc::loadStencilSpawnerSet( const QString &id )
{
  KStandardDirs *dirs = KGlobal::dirs();
  QStringList dirList = dirs->findDirs("data", "kivio/stencils");
  QString rootDir;

  // Iterate through all data directories
  for( QStringList::Iterator it = dirList.begin(); it != dirList.end(); ++it )
  {
    rootDir = (*it);

    // Within each data directory, iterate through all directories looking
    // for a filename (dir) that matches the parameter
    QDir d(rootDir);
    d.setFilter( QDir::Dirs );
    d.setSorting( QDir::Name );

    const QFileInfoList *list = d.entryInfoList();
    QFileInfoListIterator listIT( *list );
    QFileInfo *fi;

    // Loop through the outer directories (like BasicFlowcharting)
    while( (fi=listIT.current()) )
    {
      if( fi->fileName() != "." &&
          fi->fileName() != ".." )
      {
        QDir innerD(fi->absFilePath() );
        innerD.setFilter( QDir::Dirs );
        innerD.setSorting( QDir::Name );

        const QFileInfoList *innerList = innerD.entryInfoList();
        QFileInfoListIterator innerIT( *innerList );
        QFileInfo *innerFI;

        // Loop through the inner directories (like FlowChartingShapes1)
        while( (innerFI = innerIT.current()) )
        {
          if( innerFI->fileName() != ".." &&
              innerFI->fileName() != "." )
          {
            // Compare the descriptions
            QString foundId;

            // TODO: use ID system here for loading
            foundId = KivioStencilSpawnerSet::readId(innerFI->absFilePath());
            if( foundId == id)
            {

              // Load the spawner set with  rootDir + "/" + fi.fileName()
              addSpawnerSetDuringLoad( innerFI->absFilePath() );
              return true;
            }
          }

          ++innerIT;
        }
      }

      ++listIT;
    }
  }

  return false;
}

bool KivioDoc::completeLoading( KoStore* )
{
  m_bLoading = false;
  m_pMap->update();
  setModified( false );
  return true;
}

KivioPage* KivioDoc::createPage()
{
  QString s( i18n("Page%1") );
  s = s.arg( m_iPageId++ );

  KivioPage* t = new KivioPage(m_pMap, s);
  t->setPageName(s,true);

  return t;
}

void KivioDoc::addPage( KivioPage* page )
{
  m_pMap->addPage(page);
  setModified(true);
  emit sig_addPage(page);
}

void KivioDoc::insertPage( KivioPage * page )
{
    QPtrListIterator<KoView> it( views() );
    for (; it.current(); ++it )
	((KivioView*)it.current())->insertPage( page );
}

void KivioDoc::takePage( KivioPage * page )
{
    QPtrListIterator<KoView> it( views() );
    for (; it.current(); ++it )
	((KivioView*)it.current())->removePage( page );
}


void KivioDoc::paintContent( QPainter& painter, const QRect& rect, bool transparent, double /*zoomX*/, double /*zoomY*/ )
{
  KivioPage* page = m_pMap->firstPage();
  if ( !page )
    return;

  KoZoomHandler zoom;
  zoom.setZoomAndResolution(100, KoGlobal::dpiX(),
    KoGlobal::dpiY());
  KoRect r = page->getRectForAllStencils();

  float zw = (float) rect.width() / (float)zoom.zoomItX(r.width());
  float zh = (float) rect.height() / (float)zoom.zoomItY(r.height());
  float z = QMIN(zw, zh);
  //kdDebug(43000) << "paintContent: w = " << rect.width() << " h = " << rect.height() << endl;

  zoom.setZoomAndResolution(qRound(z * 100), KoGlobal::dpiX(),
    KoGlobal::dpiY());
  KivioScreenPainter ksp(&painter);
  ksp.painter()->translate( - zoom.zoomItX(r.x()), - zoom.zoomItY(r.y()) );
  paintContent(ksp,rect,transparent,page, QPoint(zoom.zoomItX(r.x()), zoom.zoomItY(r.y())), &zoom, false);
  ksp.setPainter(0L); // Important! Don't delete the QPainter!!!
}

void KivioDoc::paintContent( KivioPainter& painter, const QRect& rect, bool transparent, KivioPage* page, QPoint p0, KoZoomHandler* zoom, bool drawHandles )
{
  if ( isLoading() )
    return;

  page->paintContent(painter,rect,transparent,p0,zoom, drawHandles);
}

void KivioDoc::printContent( KPrinter &prn )
{
  KivioScreenPainter p;
  QValueList<int> pages = prn.pageList();
  KivioPage *pPage;

  // ### HACK: disable zooming-when-printing if embedded parts are used.
  // No koffice app supports zooming in paintContent currently.
  // Disable in ALL cases now
  bool doZoom = false;
  int dpiX = doZoom ? 300 : KoGlobal::dpiX();
  int dpiY = doZoom ? 300 : KoGlobal::dpiY();

  p.start(&prn);

  QPaintDeviceMetrics metrics( &prn );
  p.painter()->scale( (double)metrics.logicalDpiX() / (double)dpiX,
    (double)metrics.logicalDpiY() / (double)dpiY );

  QValueList<int>::iterator it;

  for(it = pages.begin(); it != pages.end(); ++it) {
    pPage = m_pMap->pageList().at((*it)-1);
    pPage->printContent(p, dpiX, dpiY);

    if( it != (--pages.end()) ) {
      prn.newPage();
    }
  }

  p.stop();
}

/* TODO:
 *
 * This entire function should probably be encapsulated in some sort of
 * object or other class.
 */
bool KivioDoc::exportPage(KivioPage *pPage,const QString &fileName, ExportPageDialog *dlg)
{
  KoZoomHandler zoom;
  zoom.setZoomAndResolution(100, KoGlobal::dpiX(),
    KoGlobal::dpiY());
  QPixmap buffer;

  if(dlg->crop())
  {
    if(dlg->fullPage()) {
      buffer.resize(zoom.zoomItX(pPage->getRectForAllStencils().width()) + dlg->border()*2,
        zoom.zoomItY(pPage->getRectForAllStencils().height()) + dlg->border()*2);
    } else {
      buffer.resize(zoom.zoomItX(pPage->getRectForAllSelectedStencils().width()) + dlg->border()*2,
        zoom.zoomItY(pPage->getRectForAllSelectedStencils().height()) + dlg->border()*2);
    }
  }
  else
  {
    buffer.resize(zoom.zoomItX(pPage->paperLayout().ptWidth) + dlg->border()*2,
      zoom.zoomItY(pPage->paperLayout().ptHeight) + dlg->border()*2);
  }

  kdDebug(43000) << "KivioDoc::exportCurPage() to " << fileName << "\n";

  KivioScreenPainter p;

  buffer.fill(Qt::white);

  p.start( &buffer );
  p.setTranslation( dlg->border(), dlg->border() );

  if( dlg->fullPage()==true )
  {
    if(dlg->crop()) {
      p.setTranslation(-zoom.zoomItX(pPage->getRectForAllStencils().x()),
        -zoom.zoomItY(pPage->getRectForAllStencils().y()));
    }

    pPage->printContent(p);
  }
  else
  {
    if(dlg->crop()) {
      p.setTranslation(-zoom.zoomItX(pPage->getRectForAllSelectedStencils().x()),
        -zoom.zoomItY(pPage->getRectForAllSelectedStencils().y()));
    }

    pPage->printSelected(p);
  }

  p.stop();


  QFileInfo finfo(fileName);

  return buffer.save( fileName, finfo.extension(false).upper().latin1(), dlg->quality());
}


// TODO: Fix for id system
bool KivioDoc::setIsAlreadyLoaded( QString dirName, QString id )
{
    KivioStencilSpawnerSet *pSet = m_pLstSpawnerSets->first();
    while(pSet)
    {
        if( pSet->dir() == dirName || pSet->id() == id )
        {
            return true;
        }

        pSet = m_pLstSpawnerSets->next();
    }

    return false;
}

void KivioDoc::addSpawnerSet( const QString &dirName )
{
  QString id = KivioStencilSpawnerSet::readId( dirName );

  if( setIsAlreadyLoaded( dirName, id ) )
  {
      kdDebug(43000) << "KivioDoc::addSpawnerSet() - Cannot load duplicate stencil sets" << endl;
      return;
  }


  KivioStencilSpawnerSet* set = new KivioStencilSpawnerSet();

  if( set->loadDir(dirName)==false )
  {
      kdDebug(43000) << "KivioDoc::addSpawnerSet() - Error loading dir set" << endl;
      delete set;
      return;
  }

  // Queue set for loading stencils
  m_stencilSetLoadQueue.append(set);

  if(!m_loadTimer) {
    m_loadTimer = new QTimer(this);
    connect(m_loadTimer, SIGNAL(timeout()), this, SLOT(loadStencil()));
  }

  if(!m_loadTimer->isActive()) {
    emit initProgress();
    m_loadTimer->start(0, false);
  }
}

void KivioDoc::addSpawnerSetDuringLoad( const QString &dirName )
{
  KivioStencilSpawnerSet *set;

  set = new KivioStencilSpawnerSet();
  if( set->loadDir(dirName)==false )
  {
    kdDebug(43000) << "KivioDoc::addSpawnerSetDuringLoad() - Error loading dir set" << endl;
    delete set;
    return;
  }

  QStringList::iterator it;
  QStringList files = set->files();

  for(it = files.begin(); it != files.end(); ++it) {
    QString fileName = set->dir() + "/" + (*it);
    set->loadFile(fileName);
  }

  m_pLstSpawnerSets->append(set);
}

KivioDoc::~KivioDoc()
{
  if(m_docOpened) {
    saveConfig();
  }

  // ***MUST*** Delete the pages first because they may
  // contain plugins which will be unloaded soon.  The stencils which are
  // spawned by plugins NEED the plugins still loaded when their destructor
  // is called or the program will slit it's throat.
  delete m_pMap;
  delete dcop;
  delete m_commandHistory;

  delete m_pLstSpawnerSets;
  m_pLstSpawnerSets = 0;

  s_docs->removeRef(this);
}

void KivioDoc::saveConfig()
{
  Kivio::Config::self()->writeConfig();
}

void KivioDoc::initConfig()
{
  m_units = KoUnit::unit(Kivio::Config::unit());
  m_font = KoGlobal::defaultFont();
  m_pageLayout = Kivio::Config::defaultPageLayout();
}

bool KivioDoc::removeSpawnerSet( KivioStencilSpawnerSet *pSet )
{
    return m_pLstSpawnerSets->removeRef( pSet );
}

/**
 * Iterates through all spawner objects in the stencil set checking if
 * they exist in any of the pages.
 */
void KivioDoc::slotDeleteStencilSet( DragBarButton *pBtn, QWidget *w, KivioStackBar *pBar )
{
    // Iterate through all spawners in the set checking if they exist in any of
    // the pages
    KivioIconView *pIconView = (KivioIconView *)w;
    KivioStencilSpawnerSet *pSet = pIconView->spawnerSet();

    // Start the iteration
    KivioStencilSpawner *pSpawner = pSet->spawners()->first();
    while( pSpawner )
    {
        // Check for a spawner.  If there is one, the set cannot be deleted
        if( checkStencilsForSpawner( pSpawner )==true )
        {
            KMessageBox::error(NULL, i18n("Cannot delete stencil set because there are still stencils in use."),
                i18n("Cannot Delete Stencil Set"));
            return;
        }

        pSpawner = pSet->spawners()->next();
    }



    // If we made it this far, it's ok to delete this stencil set, so do it
//    if( KMessageBox::questionYesNo(NULL, i18n("Are you sure you want to delete this stencil set?"),
//        i18n("Delete Stencil Set?"))==KMessageBox::Yes )
    {
        // Destroying the IconView does not destroy the spawner set, so we remove
        // it here
        removeSpawnerSet( pIconView->spawnerSet() );

        // And emit the signal to kill the set (page & button)
        emit sig_deleteStencilSet( pBtn, w, pBar );
    }
}

/**
 * Checks if any stencils in the document use this spawner
 */
bool KivioDoc::checkStencilsForSpawner( KivioStencilSpawner *pSpawner )
{
    KivioPage *pPage;
    KivioLayer *pLayer;
    KivioStencil *pStencil;

    // Iterate across all the pages
    pPage = m_pMap->firstPage();
    while( pPage )
    {
        pLayer = pPage->layers()->first();
        while( pLayer )
        {
            pStencil = pLayer->stencilList()->first();
            while( pStencil )
            {
                // If this is a group stencil, then we must check all child stencils
                if( pStencil->groupList() && pStencil->groupList()->count() > 0 )
                {
                    if( checkGroupForSpawner( pStencil, pSpawner )==true )
                        return true;
                }
                else if( pStencil->spawner() == pSpawner )
                    return true;

                pStencil = pLayer->stencilList()->next();
            }

            pLayer = pPage->layers()->next();
        }

        pPage = m_pMap->nextPage();
    }

    return false;
}

bool KivioDoc::checkGroupForSpawner( KivioStencil *pGroup, KivioStencilSpawner *pSpawner )
{
    KivioStencil *pStencil;

    pStencil = pGroup->groupList()->first();
    while( pStencil )
    {
        if( pStencil->groupList() && pStencil->groupList()->count() > 0 )
        {
            if( checkGroupForSpawner( pStencil, pSpawner )==true )
                return true;
        }
        else if( pStencil->spawner() == pSpawner )
        {
            return true;
        }

        pStencil = pGroup->groupList()->next();
    }

    return false;
}

void KivioDoc::slotSelectionChanged()
{
    emit sig_selectionChanged();
}

KivioStencilSpawner* KivioDoc::findStencilSpawner( const QString& setId, const QString& stencilId )
{
    KivioStencilSpawnerSet *pSpawnerSet = m_pLstSpawnerSets->first();
    while( pSpawnerSet )
    {
        if( pSpawnerSet->id() == setId && pSpawnerSet->find(stencilId) )
        {
            return pSpawnerSet->find(stencilId);
	    // return pSpawnerSet->find(name)
        }
        pSpawnerSet = m_pLstSpawnerSets->next();
    }

    if( m_pInternalSet->id() == setId && m_pInternalSet->find(stencilId) )
    {
        return m_pInternalSet->find(stencilId);
    }

    return NULL;
}

KivioStencilSpawner* KivioDoc::findInternalStencilSpawner( const QString& stencilId )
{
    return m_pInternalSet->find(stencilId);
}

void KivioDoc::addInternalStencilSpawner(KivioStencilSpawner* spawner)
{
  m_pInternalSet->addSpawner(spawner);
}

void KivioDoc::setUnits(KoUnit::Unit unit)
{
  if (m_units == unit)
    return;

  m_units = unit;
  emit unitsChanged(unit);
}

void KivioDoc::updateView(KivioPage* page)
{
  emit sig_updateView(page);
}

void KivioDoc::updateButton()
{
    QPtrListIterator<KoView> it( views() );
    for (; it.current(); ++it )
	((KivioView*)it.current())->updateButton();
}

void KivioDoc::resetLayerPanel()
{
    QPtrListIterator<KoView> it( views() );
    for (; it.current(); ++it )
	((KivioView*)it.current())->resetLayerPanel();
}

void KivioDoc::addCommand( KCommand * cmd )
{
    kdDebug(43000) << "KivioDoc::addCommand " << cmd->name() << endl;
    m_commandHistory->addCommand( cmd, false );
    setModified( true );
}

int KivioDoc::undoRedoLimit() const
{
    return m_commandHistory->undoLimit();
}

void KivioDoc::setUndoRedoLimit(int val)
{
    m_commandHistory->setUndoLimit(val);
    m_commandHistory->setRedoLimit(val);
}

void KivioDoc::slotDocumentRestored()
{
    setModified( false );
}

void KivioDoc::slotCommandExecuted()
{
    setModified( true );
}

void KivioDoc::updateProtectPanelCheckBox()
{
    QPtrListIterator<KoView> it( views() );
    for (; it.current(); ++it )
      ((KivioView*)it.current())->updateProtectPanelCheckBox();
}

void KivioDoc::loadStencil()
{
  KivioStencilSpawnerSet* set = m_stencilSetLoadQueue.first();
  QString fileName = set->dir() + "/" + set->files()[m_currentFile];
  set->loadFile(fileName);
  m_currentFile++;
  emit progress(qRound(((float)m_currentFile / (float)set->files().count()) * 100.0));

  if(m_currentFile >= set->files().count()) {
    m_pLstSpawnerSets->append(set);

    if(!m_bLoading) {
      setModified(true);
      emit sig_addSpawnerSet(set);
    }

    m_currentFile = 0;
    set = 0;
    m_stencilSetLoadQueue.pop_front();

    if(m_stencilSetLoadQueue.isEmpty()) {
      m_loadTimer->stop();
      emit endProgress();
    } else {
      emit initProgress();
    }
  }
}

#include "kivio_doc.moc"
