/***************************************************************************
 *   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 "elementselection.h"

// Core
// #include "core/direktorinstance.h"
// #include "core/elementdatabase.h"

// Elements
#include "elements/elementnode.h"
#include "elements/elementtreeiterator.h"
#include "elements/vectorelement.h"

// Visitors
#include "visitors/visitor.h"

// KCanvas
#include <kcanvas/KCanvasItem.h>

// KDE
#include <kdebug.h>

ElementSelection::ElementSelection()
    : Element(), ElementListenerInterface(), ElementSelectionInterface()
{
  for (unsigned int i = 0; i < ElementSelection::node_rb; ++i)
    m_context[i] = 0;
  init();
}

ElementSelection::~ElementSelection()
{
  delete m_list;
  delete m_visualList;
}

ElementSelection::ElementSelection( const ElementSelection &other )
    : Element(), ElementListenerInterface(), ElementSelectionInterface()
{
  (*this) = other;
}

ElementSelection& ElementSelection::operator=(const ElementSelection &other)
{
  delete m_list;
  delete m_visualList;
  m_list = new QPtrList<ElementNode>( other.elements() );
  m_visualList = new QPtrList<VectorElement>( other.visualElements() );
  setDirty();
  notifyChange();
  return *this;
}

ElementSelection* ElementSelection::clone()
{
  ElementSelection* retElem = new ElementSelection();
  *retElem = *this;
  return ( retElem );
}

void ElementSelection::init( )
{
  m_list = new QPtrList<ElementNode>();
  m_visualList = new QPtrList<VectorElement>();
  m_pathNodes = new QPtrList<KCanvasItem>();
}


/**
 * Adds an object to the selection.
 */
void ElementSelection::append( ElementNode& element, bool emitChanged )
{
  //   kdDebug() << k_funcinfo << endl;
  element.setSelected(true);
  m_list->append(&element);

  // Append to visuals if it's a VectorElement
  VectorElement* ve = 0;
  ve = ::qt_cast<VectorElement*>(&element);
  if (ve)
    m_visualList->append(ve);

  setDirty();
  if(emitChanged)
    notifyChange();
}

/**
 * Adds a list of objects to the selection.
 * @param elements 
 * @param emitChanged 
 */
void ElementSelection::append( QPtrList<ElementNode>& elements, bool emitChanged )
{
  ElementNode *element;
  QPtrListIterator<ElementNode> it( elements );
  while ( (element = it.current()) != 0 )
  {
    append(*element, false);
    element->setSelected(true);
    ++it;
  }
  setDirty();
  if (emitChanged)
    notifyChange();
}

/**
 * Removes the reference to the object, not the object itself.
 */
void ElementSelection::take( ElementNode& element, bool emitChanged )
{
  //   kdDebug() << k_funcinfo << endl;
  element.setSelected(false);
  m_list->remove(&element);

  // take from visuals if it's a VectorElement
  VectorElement* ve = 0;
  ve = ::qt_cast<VectorElement*>(&element);
  if (ve)
    m_visualList->remove(ve);

  setDirty();
  if(emitChanged)
    notifyChange();
}

/**
 * Removes the references to all objects, not the objects themselves.
 */
void ElementSelection::clear(bool emitChanged)
{
  // Deselect all
  ElementNode *element;
  QPtrListIterator<ElementNode> it( *m_list );
  while ( (element = it.current()) != 0 )
  {
    element->setSelected(false);
    ++it;
  }

  // clear
  m_list->clear();
  m_visualList->clear();
  setDirty();
  if (emitChanged)
    notifyChange();
}


Element::Hit ElementSelection::hits( const QPoint &p )
{
  //   kdDebug() << k_funcinfo << endl;
  if ( !m_context )
    return Element::NOHIT;

  if ( m_context[node_body-1]->strokeContains(p) )
    return Element::PATHHIT;
  else if ( m_context[node_body-1]->fillContains(p) )
    return Element::FILLHIT;
  else
    return Element::NOHIT;
}


void ElementSelection::accept( Visitor& v)
{
  v.visit( *this );
}

QRect ElementSelection::bbox( KCBBoxFlags flags ) const
{
  QRect bbox = QRect(0,0,10,10);
  if (!m_visualList->isEmpty())
  {
    // TODO: calc the real bbox of all elements
    ElementNode *element;
    QPtrListIterator<VectorElement> it( *m_visualList );
    while ( (element = it.current()) != 0 )
    {
      return ( it.current()->bbox(flags) );
      ++it;
    }
  }
  return bbox;
}

/**
 * Overloaded State fkt.
 * Returns hidden when containing no elements
 */
Element::State ElementSelection::getState()
{
  if ( m_visualList->isEmpty() || Element::getState() == Element::hidden )
  {
    return Element::hidden;
  }
  else
    return Element::normal;
}

void ElementSelection::onElementInserted( ElementNode * )
{}

void ElementSelection::onElementRemoved( ElementNode *e )
{
  // Remove Elements from
  ElementTreeIterator itr( e ); // TODO: constiterator
  for( ; itr.current(); itr.next() )
    if ( m_list->containsRef( itr.current() ) )
    {
      kdDebug() << "ElementSelection::onElementRemoved() - Removing element " << itr.current()->className() << endl;
      take( *itr.current(), false );
    }
  notifyChange();
}

void ElementSelection::onElementModified( ElementNode *e )
{
  setDirty(); // Just to redraw with a maybe changed bbox
}

void ElementSelection::addElementSelectionListener( ElementSelectionListenerInterface *l )
{
  if ( m_listeners.findRef( l ) < 0 )
    m_listeners.append( l );

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

void ElementSelection::removeElementSelectionListener( ElementSelectionListenerInterface *l )
{
  kdDebug() << "ElementSelection::addElementListener() - Removing listener " << l << endl;
  m_listeners.removeRef( l );
}

void ElementSelection::notifyChange()
{
  ElementSelectionListenerInterface *listener;
  for ( listener = m_listeners.first(); listener; listener = m_listeners.next() )
    listener->onSelectionModified();
}

KCanvasItem * ElementSelection::context( unsigned int node ) const
{
  if ( node >= (uint)ElementSelection::node_body && node <= (uint)ElementSelection::node_rb )
    return m_context[node-1];
  else
  {
    kdWarning() << "ElementSelection::context() - Invalid HandleNode given" << endl;
    return 0;
  }
}

void ElementSelection::setContext( HandleNode node, KCanvasItem * context )
{
  if ( (uint)node >= (uint)ElementSelection::node_body && (uint)node <= (uint)ElementSelection::node_rb )
    m_context[node-1] = context;
  else
    kdWarning() << "ElementSelection::setContext() - Invalid HandleNode given" << endl;
}

unsigned int ElementSelection::handleNode( const QPoint &p )
{
  for (unsigned int i = 1; i < ElementSelection::node_rb; ++i)
  {
    if ( m_context[i] && (m_context[i]->fillContains(p) || m_context[i]->strokeContains(p) ) )
      return i+1;
  }
  if ( m_context[ElementSelection::node_body-1] &&
       ( m_context[ElementSelection::node_body-1]->fillContains(p) ||
         m_context[ElementSelection::node_body-1]->strokeContains(p) ) )
    return ElementSelection::node_body;
  return 0;
}



#include "elementselection.moc"
