/* This file is part of the KDE project
   Copyright (C) 2001 Andrea Rizzi <rizzi@kde.org>
	              Ulrich Kuettler <ulrich.kuettler@mailbox.tu-dresden.de>

   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 <tdelocale.h>  //This is for undo descriptions

#include <tqvaluelist.h>

#include "formulacursor.h"
#include "formulaelement.h"
#include "indexelement.h"
#include "kformulacommand.h"
#include "matrixelement.h"
#include "sequenceelement.h"
#include "textelement.h"
#include "tokenelement.h"


KFORMULA_NAMESPACE_BEGIN

int PlainCommand::evilDestructionCount = 0;

PlainCommand::PlainCommand( const TQString& name )
    : KNamedCommand(name)
{
    evilDestructionCount++;
}

PlainCommand::~PlainCommand()
{
    evilDestructionCount--;
}

Command::Command(const TQString &name, Container* document)
        : PlainCommand(name), cursordata(0), undocursor(0), doc(document)
{
}

Command::~Command()
{
    delete undocursor;
    delete cursordata;
}

FormulaCursor* Command::getExecuteCursor()
{
    FormulaCursor* cursor = getActiveCursor();
    if (cursordata == 0) {
        setExecuteCursor(getActiveCursor());
    }
    else {
        cursor->setCursorData(cursordata);
    }
    return cursor;
}

void Command::setExecuteCursor(FormulaCursor* cursor)
{
    // assert(cursordata == 0);
    cursordata = cursor->getCursorData();
}

FormulaCursor* Command::getUnexecuteCursor()
{
    FormulaCursor* cursor = getActiveCursor();
    cursor->setCursorData(undocursor);
    destroyUndoCursor();
    return cursor;
}

void Command::setUnexecuteCursor(FormulaCursor* cursor)
{
    // assert(undocursor == 0);
    undocursor = cursor->getCursorData();
}


// ******  Generic Add command

KFCAdd::KFCAdd(const TQString &name, Container *document)
        : Command(name, document)
{
    addList.setAutoDelete( true );
}


void KFCAdd::execute()
{
    FormulaCursor* cursor = getExecuteCursor();
    cursor->insert(addList, beforeCursor);
    setUnexecuteCursor(cursor);
    cursor->setSelection(false);
    testDirty();
}


void KFCAdd::unexecute()
{
    FormulaCursor* cursor = getUnexecuteCursor();
    cursor->remove(addList, beforeCursor);
    //cursor->setSelection(false);
    cursor->normalize();
    testDirty();
}


// ******  Remove selection command

KFCRemoveSelection::KFCRemoveSelection(Container *document,
                                       Direction direction)
        : Command(i18n("Remove Selected Text"), document),
          dir(direction)
{
    removedList.setAutoDelete( true );
}

void KFCRemoveSelection::execute()
{
    FormulaCursor* cursor = getExecuteCursor();
    cursor->remove(removedList, dir);
    setUnexecuteCursor(cursor);
    testDirty();
}

void KFCRemoveSelection::unexecute()
{
    FormulaCursor* cursor = getUnexecuteCursor();
    cursor->insert(removedList);
    cursor->setSelection(false);
    testDirty();
}



KFCReplace::KFCReplace(const TQString &name, Container* document)
        : KFCAdd(name, document), removeSelection(0)
{
}

KFCReplace::~KFCReplace()
{
    delete removeSelection;
}

void KFCReplace::execute()
{
    if (getActiveCursor()->isSelection() && (removeSelection == 0)) {
        removeSelection = new KFCRemoveSelection(getDocument());
    }
    if (removeSelection != 0) {
        removeSelection->execute();
    }
    KFCAdd::execute();
}

void KFCReplace::unexecute()
{
    KFCAdd::unexecute();
    if (removeSelection != 0) {
        removeSelection->unexecute();
    }
}


// ******  Token Add command

KFCAddToken::KFCAddToken(const TQString &name, Container *document)
        : Command(name, document)
{
}

KFCAddToken::~KFCAddToken()
{
    TQPtrDictIterator< TQPtrList< BasicElement > > it( contentList );
    TQPtrList< BasicElement >* list;
    while ( ( list = it.current() ) != 0 ) {
        delete list;
        ++it;
    }
}

void KFCAddToken::execute()
{
    kdDebug( DEBUGID ) << k_funcinfo << endl;
    FormulaCursor* cursor = getExecuteCursor();
    TQPtrList<BasicElement> tokenListTmp = tokenList;
    cursor->insert( tokenList, beforeCursor );
    tokenList = tokenListTmp;
    TQPtrListIterator< BasicElement > it( tokenList );
    BasicElement* element;
    BasicElement* current = cursor->getElement();
    while ( (element = it.current()) != 0 ) {
        element->goInside( cursor );
        cursor->insert( *contentList.find( element ), beforeCursor );
        ++it;
    }
    setUnexecuteCursor( cursor );
    cursor->setSelection(false);
    testDirty();
}


void KFCAddToken::unexecute()
{
    kdDebug( DEBUGID ) << k_funcinfo << endl;
    FormulaCursor* cursor = getUnexecuteCursor();
    SequenceElement* parent = static_cast<SequenceElement*>(cursor->getElement()->getParent());

    for ( int i = 0; i < tokenList.count(); i++ ) {
        SequenceElement* current = static_cast<SequenceElement*>(cursor->getElement());
        TQPtrList< BasicElement > list;
        for ( uint i = 0; i < current->countChildren(); ++i ) {
            cursor->remove( list, beforeCursor );
        }
        if ( parent ) {
            int pos = parent->childPos( current );
            cursor->setTo( parent, pos + 1);
            cursor->remove( list, beforeCursor );
            if ( pos > 0 ) {
                BasicElement* element = parent->getChild( pos - 1 );
                if (element)
                    element->moveEnd( cursor );
            }
        }
    }
    testDirty();
}

/**
 * Collect all tokens that are to be added
 */
void KFCAddToken::addToken( BasicElement* element )
{ 
    tokenList.append( element ); 
    contentList.insert( element, new TQPtrList< BasicElement > );
    contentList.find( element )->setAutoDelete( true );
}

KFCReplaceToken::KFCReplaceToken(const TQString &name, Container* document)
    : KFCAddToken(name, document), removeSelection(0)
{
}

KFCReplaceToken::~KFCReplaceToken()
{
    delete removeSelection;
}

void KFCReplaceToken::execute()
{
    kdDebug( DEBUGID ) << k_funcinfo << endl;
    if (getActiveCursor()->isSelection() && (removeSelection == 0)) {
        removeSelection = new KFCRemoveSelection(getDocument());
    }
    if (removeSelection != 0) {
        removeSelection->execute();
    }
    KFCAddToken::execute();
}

void KFCReplaceToken::unexecute()
{
    kdDebug( DEBUGID ) << k_funcinfo << endl;
    KFCAddToken::unexecute();
    if (removeSelection != 0) {
        removeSelection->unexecute();
    }
}


KFCSplitToken::KFCSplitToken(const TQString &name, Container* document)
    : KFCAddToken(name, document), removeSelection(0)
{
    splitList.setAutoDelete( true );
}

KFCSplitToken::~KFCSplitToken()
{
    delete splitCursor;
    delete removeSelection;
}

void KFCSplitToken::execute()
{
    FormulaCursor* cursor = getExecuteCursor();
    if (getActiveCursor()->isSelection() && (removeSelection == 0)) {
        removeSelection = new KFCRemoveSelection(getDocument());
    }
    if (removeSelection != 0) {
        removeSelection->execute();
    }
    splitCursor = cursor->getCursorData();
    SequenceElement* parent = static_cast<SequenceElement*>( cursor->getElement() );
    if ( parent ) {
        cursor->setMark( parent->countChildren() );
        cursor->setSelection( true );
    }
    cursor->remove( splitList, afterCursor );
    TokenElement *token = new TokenElement();// TODO 
    addToken( token );
    TQPtrListIterator< BasicElement > it ( splitList );
    BasicElement* element;
    while ( ( element = it.current() ) != 0 ) {
        addContent( token, element );
        ++it;
    }
    KFCAddToken::execute();
    cursor = getExecuteCursor();
    if ( parent ) {
        BasicElement* child = parent->getChild( cursor->getPos() );
        if ( child ) {
            child->moveEnd( cursor );
        }
    }
}

void KFCSplitToken::unexecute()
{
    kdDebug( DEBUGID ) << k_funcinfo << endl;
    KFCAddToken::unexecute();
    FormulaCursor *cursor = getUnexecuteCursor();
    cursor->setCursorData( splitCursor );
    cursor->insert( splitList, afterCursor );
    if (removeSelection != 0) {
        removeSelection->unexecute();
    }
    testDirty();
}


KFCRemove::KFCRemove(Container *document,
                     Direction direction)
        : Command(i18n("Remove Selected Text"), document),
          element(0), simpleRemoveCursor(0), dir(direction)
{
    removedList.setAutoDelete( true );
}

KFCRemove::~KFCRemove()
{
    delete simpleRemoveCursor;
    delete element;
}

void KFCRemove::execute()
{
    FormulaCursor* cursor = getExecuteCursor();
    cursor->remove(removedList, dir);
    if (cursor->elementIsSenseless()) {
        simpleRemoveCursor = cursor->getCursorData();
        element = cursor->replaceByMainChildContent();
    }
    setUnexecuteCursor(cursor);
    cursor->normalize( dir );
    testDirty();
}

void KFCRemove::unexecute()
{
    FormulaCursor* cursor = getUnexecuteCursor();
    if (element != 0) {
        cursor->replaceSelectionWith(element);
        element = 0;

        cursor->setCursorData(simpleRemoveCursor);
        delete simpleRemoveCursor;
        simpleRemoveCursor = 0;
    }
    cursor->insert(removedList, dir);
    cursor->setSelection(false);
    testDirty();
}


KFCRemoveEnclosing::KFCRemoveEnclosing(Container* document,
                                       Direction dir)
        : Command(i18n("Remove Enclosing Element"), document),
          element(0), direction(dir)
{
}

KFCRemoveEnclosing::~KFCRemoveEnclosing()
{
    delete element;
}

void KFCRemoveEnclosing::execute()
{
    FormulaCursor* cursor = getExecuteCursor();
    element = cursor->removeEnclosingElement(direction);
    setUnexecuteCursor(cursor);
    //cursor->normalize();
    cursor->setSelection(false);
    testDirty();
}

void KFCRemoveEnclosing::unexecute()
{
    FormulaCursor* cursor = getUnexecuteCursor();
    if ( element ) {
        cursor->replaceSelectionWith(element);
    }
    cursor->normalize();
    cursor->setSelection(false);
    element = 0;
    testDirty();
}


// ******  Add root, bracket etc command

KFCAddReplacing::KFCAddReplacing(const TQString &name, Container* document)
        : Command(name, document), element(0)
{
}

KFCAddReplacing::~KFCAddReplacing()
{
    delete element;
}


void KFCAddReplacing::execute()
{
    FormulaCursor* cursor = getExecuteCursor();
    cursor->replaceSelectionWith(element);
    setUnexecuteCursor(cursor);
    cursor->goInsideElement(element);
    element = 0;
    testDirty();
}


void KFCAddReplacing::unexecute()
{
    FormulaCursor* cursor = getUnexecuteCursor();
    element = cursor->replaceByMainChildContent();
    cursor->normalize();
    testDirty();
}


// ******  Add index command

KFCAddGenericIndex::KFCAddGenericIndex(Container* document, ElementIndexPtr _index)
        : KFCAdd(i18n("Add Index"), document), index(_index)
{
    addElement(new SequenceElement());
}

void KFCAddGenericIndex::execute()
{
    index->setToIndex(getActiveCursor());
    KFCAdd::execute();
}


KFCAddIndex::KFCAddIndex(Container* document,
                         IndexElement* element, ElementIndexPtr index)
        : KFCAddReplacing(i18n("Add Index"), document),
          addIndex(document, index)
{
    setElement(element);
}

KFCAddIndex::~KFCAddIndex()
{
}

void KFCAddIndex::execute()
{
    KFCAddReplacing::execute();
    addIndex.execute();
}

void KFCAddIndex::unexecute()
{
    addIndex.unexecute();
    KFCAddReplacing::unexecute();
}


KFCChangeBaseSize::KFCChangeBaseSize( const TQString& name, Container* document,
                                      FormulaElement* formula, int size )
    : PlainCommand( name ), m_document( document ), m_formula( formula ), m_size( size )
{
    m_oldSize = formula->getBaseSize();
}

void KFCChangeBaseSize::execute()
{
    m_formula->setBaseSize( m_size );
    m_document->recalc();
}

void KFCChangeBaseSize::unexecute()
{
    m_formula->setBaseSize( m_oldSize );
    m_document->recalc();
}


FontCommand::FontCommand( const TQString& name, Container* document )
    : Command( name, document )
{
    list.setAutoDelete( false );
    elementList.setAutoDelete( false );
}


void FontCommand::collectChildren()
{
    list.clear();
    uint count = elementList.count();
    for ( uint i=0; i<count; ++i ) {
        elementList.at( i )->dispatchFontCommand( this );
    }
}


void FontCommand::parseSequences( const TQMap<SequenceElement*, int>& parents )
{
    TQValueList<SequenceElement*> sequences = parents.keys();
    for ( TQValueList<SequenceElement*>::iterator i = sequences.begin();
          i != sequences.end();
          ++i ) {
        ( *i )->parse();
    }
}


CharStyleCommand::CharStyleCommand( CharStyle cs, const TQString& name, Container* document )
    : FontCommand( name, document ), charStyle( cs )
{
}

void CharStyleCommand::execute()
{
    collectChildren();
    TQMap<SequenceElement*, int> parentCollector;

    styleList.clear();
    uint count = childrenList().count();
    styleList.reserve( count );
    for ( uint i=0; i<count; ++i ) {
        TextElement* child = childrenList().at( i );
        styleList[i] = child->getCharStyle();
        child->setCharStyle( charStyle );
        parentCollector[static_cast<SequenceElement*>( child->getParent() )] = 1;
    }
    parseSequences( parentCollector );
    testDirty();
}

void CharStyleCommand::unexecute()
{
    TQMap<SequenceElement*, int> parentCollector;
    uint count = childrenList().count();
    //styleList.reserve( count );
    for ( uint i=0; i<count; ++i ) {
        TextElement* child = childrenList().at( i );
        child->setCharStyle( styleList[i] );
        parentCollector[static_cast<SequenceElement*>( child->getParent() )] = 1;
    }
    parseSequences( parentCollector );
    testDirty();
}


CharFamilyCommand::CharFamilyCommand( CharFamily cf, const TQString& name, Container* document )
    : FontCommand( name, document ), charFamily( cf )
{
}

void CharFamilyCommand::execute()
{
    collectChildren();

    TQMap<SequenceElement*, int> parentCollector;

    familyList.clear();
    uint count = childrenList().count();
    familyList.reserve( count );
    for ( uint i=0; i<count; ++i ) {
        TextElement* child = childrenList().at( i );
        familyList[i] = child->getCharFamily();
        child->setCharFamily( charFamily );
        parentCollector[static_cast<SequenceElement*>( child->getParent() )] = 1;
    }
    parseSequences( parentCollector );
    testDirty();
}

void CharFamilyCommand::unexecute()
{
    TQMap<SequenceElement*, int> parentCollector;
    uint count = childrenList().count();
    //familyList.reserve( count );
    for ( uint i=0; i<count; ++i ) {
        TextElement* child = childrenList().at( i );
        child->setCharFamily( familyList[i] );
        parentCollector[static_cast<SequenceElement*>( child->getParent() )] = 1;
    }
    parseSequences( parentCollector );
    testDirty();
}


KFORMULA_NAMESPACE_END
