/* 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., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

#ifndef KFORMULACONTAINER_H
#define KFORMULACONTAINER_H

#include <qclipboard.h>
#include <qdom.h>
#include <qimage.h>
#include <qptrlist.h>
#include <qobject.h>
#include <qptrstack.h>
#include <qstring.h>

#include <kcommand.h>
//#include <kocommandhistory.h>
#include "kocommandhistory.h"
#include "kformuladefs.h"

class QColorGroup;
class QKeyEvent;
class QPainter;

class KCommand;
class KPrinter;

KFORMULA_NAMESPACE_BEGIN

class BasicElement;
class Document;
class FormulaCursor;
class FormulaElement;
class IndexElement;
class PlainCommand;
class SymbolTable;


/**
 * The interface the elements expect from its document.
 *
 * Please don't mistake this class for Document.
 * This one represents one formula, the other one provides
 * the context in which the formulae exist.
 */
class FormulaDocument {
    // not allowed
    FormulaDocument( const FormulaDocument& ) {}
    FormulaDocument& operator=( const FormulaDocument& ) { return *this; }
public:

    FormulaDocument() {}
    virtual ~FormulaDocument() {}

    virtual void elementRemoval(BasicElement* /*child*/) {}
    virtual void changed() {}
    virtual void cursorHasMoved( FormulaCursor* ) {}
    virtual void moveOutLeft( FormulaCursor* ) {}
    virtual void moveOutRight( FormulaCursor* ) {}
    virtual void moveOutAbove( FormulaCursor* ) {}
    virtual void moveOutBelow( FormulaCursor* ) {}
    virtual void tell( const QString& /*msg*/ ) {}
    virtual void insertFormula( FormulaCursor* ) {}
    virtual void removeFormula( FormulaCursor* ) {}
    virtual void baseSizeChanged( int, bool ) {}
    virtual const SymbolTable& getSymbolTable() const = 0;
};


/**
 * The document. Actually only one part of the whole.
 * Provides everything to edit the formula.
 */
class KOFORMULA_EXPORT Container : public QObject, public FormulaDocument {
    friend class MimeSource;
    Q_OBJECT

    // no copying
    Container( const Container& );
    Container& operator= ( const Container& );

public:

    enum ViewActions { EXIT_LEFT, EXIT_RIGHT,
                       EXIT_ABOVE, EXIT_BELOW,
                       INSERT_FORMULA, REMOVE_FORMULA };

    /**
     * Constructs a new formula and register it with the document.
     *
     * @param doc the document we belong to.
     * @param pos the formulas position inside its document.
     * @param registerMe whether the formula is to be registered
     * with the document.
     */
    Container( Document* doc, int pos, bool registerMe=true );
    ~Container();

    /**
     * Needs to be called before anything else can be done with a
     * newly created formula! This is required to allow polymorphic
     * formulas. (The virtual method problem.)
     */
    void initialize();

    /**
     * Returns a new cursor. It points to the beginning of the
     * formula. The cursor gets no messages if the formula changes
     * so use this with care!
     */
    FormulaCursor* createCursor();

    /**
     * Gets called just before the child is removed from
     * the element tree.
     */
    void elementRemoval(BasicElement* child);

    /**
     * Gets called when ever something changes and we need to
     * recalc.
     */
    void changed();

    /**
     * Gets called when a request has the side effect of moving the
     * cursor. In the end any operation that moves the cursor should
     * call this.
     */
    void cursorHasMoved( FormulaCursor* );

    /**
     * Inside the formula occurred an event that must be handled
     * outside.
     */
    void moveOutLeft( FormulaCursor* );
    void moveOutRight( FormulaCursor* );
    void moveOutAbove( FormulaCursor* );
    void moveOutBelow( FormulaCursor* );
    void tell( const QString& msg );
    void removeFormula( FormulaCursor* );

    /**
     * Register and unregister this formula with its document.
     */
    void registerFormula( int pos=-1 );
    void unregisterFormula();

    /**
     * The base size changed. If not owned it uses the default one now.
     */
    void baseSizeChanged( int size, bool owned );

    /**
     * Draws the whole thing.
     */
    void draw( QPainter& painter, const QRect& r,
               const QColorGroup& cg, bool edit=false );

    /**
     * Draws the whole thing.
     */
    void draw( QPainter& painter, const QRect& r, bool edit=false );

    /**
     * Saves the data into the document.
     */
    void save( QDomElement &root );

    /**
     * Save formula as MathML.
     */
    void saveMathML( QTextStream& stream, bool oasisFormat = false );

    /**
     * Load function.
     * Load the formula from the specified file containing MathML .
     */
    bool loadMathML( const QDomDocument &doc, bool oasisFormat = false );

    /**
     * Loads a formula from the document.
     */
    bool load( const QDomElement &fe );

    /**
     * @returns Tex string for the formula
     */
    QString texString();

    QString formulaString();

    /**
     * Prints the formula.
     */
    void print(KPrinter& printer);

    /**
     * @returns an image that looks like out formula.
     */
    QImage drawImage( int width, int height );

    /**
     * @returns the cursor to be used for editing.
     */
    FormulaCursor* activeCursor();
    const FormulaCursor* activeCursor() const;

    /**
     * Sets the cursor that is to be used for any editing.
     *
     * The active cursor might 0. In this case you must not
     * request any change from the formula.
     */
    void setActiveCursor(FormulaCursor* cursor);

    /**
     * @returns the formula's size.
     */
    QRect boundingRect() const;

    /**
     * @returns the formula's size including its active cursor.
     */
    QRect coveredRect();

    double width() const;
    double height() const;

    /**
     * @returns the distance between the upper bound and the formulas
     * base line.
     */
    double baseline() const;

    /**
     * Moves the formula to a new location. This location will be the
     * upper left corner of the rectangle that is drawn by the painter.
     */
    void moveTo( int x, int y );

    /**
     * KWord uses a transformed painter to draw formulas, so every
     * formula has the internal position (0,0). But we might need to
     * sort our formulas according to their position inside the
     * document. (This is only needed for math formulas.)
     */
    virtual double getDocumentX() const { return -1; }
    virtual double getDocumentY() const { return -1; }
    virtual void setDocumentPosition( double /*x*/, double /*y*/ ) {}

    /**
     * Start the documents evaluation at this formula. This must be the
     * formula that changed. The formulas above it won't be affected
     * by this change.
     *
     * This has no meaning in not evaluating formulas.
     */
    virtual void startEvaluation() {}

    /**
     * Recalcs the formula and emits the .*Changed signals if needed.
     */
    void testDirty();

    /**
     * Recalc the formula.
     */
    virtual void recalc();

    /**
     * @returns true if there is no element.
     */
    bool isEmpty();

    /**
     * @returns the document this formula belongs to.
     */
    virtual Document* document() const;

    virtual const SymbolTable& getSymbolTable() const;

    int fontSize() const;

    /**
     * Sets the base font size of this formula.
     */
    void setFontSize( int pointSize, bool forPrint = false );

    void setFontSizeDirect( int pointSize );

    /**
        If the cursor is in a matrix, the matrix actions will be enabled, otherwise disabled.
    */
    void updateMatrixActions();

signals:

    /**
     * The cursor has been moved but the formula hasn't been
     * changed. The view that owns the cursor needs to know this.
     */
    void cursorMoved(FormulaCursor* cursor);

    /**
     * The cursor wants to leave this formula.
     */
    void leaveFormula( Container* formula, FormulaCursor* cursor, int cmd );

    /**
     * The formula has changed and needs to be redrawn.
     */
    void formulaChanged( int width, int height );
    void formulaChanged( double width, double height );

    /**
     * A message that might be a useful hint. Meant for the statusbar.
     */
    void statusMsg( const QString& msg );

    /**
     * A message that describes an error. Meant for a message box. (?)
     */
    void errorMsg( const QString& );

    /**
     * The element is going to leave the formula with and all its children.
     */
    void elementWillVanish(BasicElement* element);

    /**
     * Tells the cursors that we have just loaded a new formula.
     */
    void formulaLoaded(FormulaElement*);

    /**
     * We've got a new base size.
     */
    void baseSizeChanged( int );

public:

    /**
     * General input.
     */
    void input( QKeyEvent* event );

    void performRequest( Request* request );

    // There are a lot of thing we can do with the formula.

    /**
     * Insert data from the clipboard.
     */
    void paste();

    /**
     * Insert data from the document.
     */
    void paste( const QDomDocument& document, QString desc );

    /**
     * Copy the current selection to the clipboard.
     */
    void copy();

    /**
     * Copy and remove.
     */
    void cut();

protected:

    KoCommandHistory* getHistory() const;

    /**
     * @returns the root of our element tree. That's the formula's structure.
     */
    FormulaElement* rootElement() const;

    /**
     * Factory method.
     */
    virtual FormulaElement* createMainSequence();

    void emitErrorMsg( const QString& );

private:

    /**
     * Execute the command if it makes sense.
     */
    void execute(KCommand *command);

    /**
     * Emits a signal if the cursor had moved.
     */
    void checkCursor();

    /**
     * @returns true if there is a cursor that is allowed to edit the formula.
     */
    bool hasValidCursor() const;

    struct Container_Impl;
    Container_Impl* impl;

    // debug
    friend class TestFormulaCursor;
    friend class TestFormulaElement;
    friend class TestIndexElement;
    friend class TestCommands;
};

KFORMULA_NAMESPACE_END

#endif // KFORMULACONTAINER_H
