/***************************************************************************
 *   Copyright (C) 2004 by Hans Oischinger                                 *
 *   hans.oischinger@kdemail.net                                           *
 *                                                                         *
 *   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.,                                       *
 *   51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA.             *
 ***************************************************************************/
#include "elementdatabase.h"

#include "documentbuilder.h"

// Elements
#include "elements/element.h"
#include "elements/elementnode.h"
#include "elements/documentelement.h"
#include "elements/animatedlengthattr.h"
#include "elements/lengthattribute.h"
#include "elements/elementselection.h"
#include "elements/elementtreeiterator.h"
#include "elements/defselement.h"

// Qt
#include <qdatetime.h>

// KDE
#include <kurl.h>
#include <kdebug.h>

ElementDatabase::ElementDatabase(QObject *parent, const char *name)
    : QObject(parent, name),
    m_defs(0)
{
  m_documentElement = new DocumentElement();
  m_documentElement->width().baseVal().setValue(500);
  m_documentElement->height().baseVal().setValue(500);

  m_sel = new ElementSelection();
  addElementListener( m_sel );
}


ElementDatabase::~ElementDatabase()
{}

void ElementDatabase::addElementListener( ElementListenerInterface* l )
{
  if ( m_listeners.findRef( l ) < 0 )
    m_listeners.append( l );

  kdDebug() << "ElementDatabase::addElementListener() - Adding listener " << l << endl;

  // Notify listener about all objects in tree
  l->onElementInserted( m_documentElement );
}

void ElementDatabase::removeElementListener( ElementListenerInterface* l )
{
  kdDebug() << "ElementDatabase::addElementListener() - Removing listener " << l << endl;
  m_listeners.removeRef( l );
}

bool ElementDatabase::openURL( const KURL& url )
{
  DocumentBuilder docBuilder(url);
  return true;  // FIXME: check if parsing was successfull
}

void ElementDatabase::setDocumentRoot( DocumentElement *doc )
{
  kdDebug() << "ElementDatabase::setDocumentRoot() - New Root Element" << endl;
  if (!doc)
  {
    kdWarning() << "ElementDatabase::setDocumentRoot() - Invalid new document... ignoring" << endl;
    return;
  }

  if (m_documentElement)
  {
    removeEntry(m_documentElement);
  }
  m_documentElement = doc;

  // Check for the Defs Element
  DefsElement* defs = 0;
  ElementTreeIterator *it = new ElementTreeIterator(m_documentElement);
  while (it->current())
  {
    defs = ::qt_cast<DefsElement*>(it->current());
    if ( defs )
      break;
    it->next();
  }
  if (!defs)
  {
    kdWarning() << "ElementDatabase::setDocumentRoot() - No DefsElement found. Creating..." << endl;
    defs = new DefsElement();
    m_documentElement->insertBefore(defs, m_documentElement->getFirstChild());
  }
  m_defs = defs;

  insertEntry(m_documentElement);
}

/**
 * Notify Listeners
 */
void ElementDatabase::notifyRemove( ElementNode *e )
{
  ElementListenerInterface *listener;
  for ( listener = m_listeners.first(); listener; listener = m_listeners.next() )
    listener->onElementRemoved( e );
}

/**
 * Notify Listeners
 */
void ElementDatabase::notifyInsert( ElementNode *e )
{
  ElementListenerInterface *listener;
  for ( listener = m_listeners.first(); listener; listener = m_listeners.next() )
    listener->onElementInserted( e );
}

/**
 * Notify Listeners
 */
void ElementDatabase::notifyModified( ElementNode *e )
{
  ElementListenerInterface *listener;
  for ( listener = m_listeners.first(); listener; listener = m_listeners.next() )
    listener->onElementModified( e );
}

void ElementDatabase::insertEntry( ElementNode *e )
{
  kdDebug() << "ElementDatabase::insertEntry() - Inserting " <<
  "<" << e->className() << "::" << e->name() << ">" << endl;

  // Notify everyone
  notifyInsert( e );
}

void ElementDatabase::removeEntry( ElementNode *e )
{
  // kdDebug() << "ElementDatabase::removeEntry() - Removing " << "<" << e->className() << "::" << e->name() << ">" << endl;

  // Set the deleted state for e and it's children
  ElementTreeIterator itr( e );
  for( ; itr.current(); itr.next() )
  {
    // kdDebug() << "ElementDatabase::removeEntry() -   Removing nested child of type " << itr.current()->className() << ", ID: " << itr.current()->id() << endl;
    itr.current()->setState( Element::deleted );
  }

  // Notify everyone
  notifyRemove( e );

  // Did we remove the document element?
  if (e==m_documentElement)
    m_documentElement = 0;
}

void ElementDatabase::reviveEntry( ElementNode *e )
{
  kdDebug() << "ElementDatabase::reviveEntry() - Reviving " <<
  "<" << e->className() << "::" << e->name() << ">" << endl;

  // Set the deleted state for e and it's children
  ElementTreeIterator itr( e );
  for( ; itr.current(); itr.next() )
  {
    itr.current()->setState( Element::normal );
  }

  // Notify everyone
  notifyInsert( e );

  // Did we revive the document element?
  if (e==m_documentElement)
    m_documentElement = static_cast<DocumentElement*>(e);
}

void ElementDatabase::deleteEntry( ElementNode *e )
{
  if (!e)
  {
    kdDebug() << "ElementDatabase::deleteEntry() - Called for NULL Ptr" << endl;
    return;
  }

  kdDebug() << "ElementDatabase::deleteEntry() - Deleting " <<
  "<" << e->className() << "::" << e->name() << ">" << endl;
  if ( e->getFirstChild() )
  {
    ElementNode* node = e->getFirstChild();
    while( node )
    {
      ElementNode* delNodeNext = node->getNext();
      e->removeChild( node );
      node->deleteSubTree();
      node = delNodeNext;
    }
  }
}

/**
 * Connects the Element's destroy signal
 */
void ElementDatabase::establishConnections( ElementNode* e )
{
  connect( e, SIGNAL( destroyed(QObject*) ), this, SLOT( slotOnDestroyed(QObject*)) );
}

/**
 * Called when a object has been illegally destroyed
 */
void ElementDatabase::slotOnDestroyed(QObject* pObj)
{
  kdWarning() << "ElementDatabase::slotOnDestroyed() -  " <<
  "<" << pObj->className() << "::" << pObj->name() << ">"
  << " has been deleted from outside!!!" << endl;
  removeEntry( static_cast<ElementNode*>(pObj) );
}

void ElementDatabase::ElementDatabase::entryModified( ElementNode *e )
{
  notifyModified(e);
}

QString ElementDatabase::genUniqueID( )
{
  // FIXME: This is only unique to some extent :)
  return QString("kast%1").arg( QTime::currentTime().msecsTo( QTime( 0, 0 ) ) );
}

#include "elementdatabase.moc"
