/***************************************************************************
 *   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.                                   *
 *                                                                         *
 *   copyright (C) 2003      Brian Thomas <thomas@mail630.gsfc.nasa.gov>   *
 *   copyright (C) 2004-2008                                               *
 *   Umbrello UML Modeller Authors <uml-devel@uml.sf.net>                  *
 ***************************************************************************/

#ifndef TEXTBLOCK_H
#define TEXTBLOCK_H

#include <QtXml/QDomDocument>
#include <QtXml/QDomElement>
#include <QtCore/QObject>

class CodeDocument;

/**
 * The fundemental unit of text within an output file containing code.
 */
class TextBlock
{
    friend class CodeGenObjectWithTextBlocks;
    friend class ClassifierCodeDocument;
public:

    /**
     * Constructor.
     */
    explicit TextBlock ( CodeDocument * parent, const QString & text = "");

    /**
     * Destructor.
     */
    virtual ~TextBlock ( );

    /**
     * Set the value of m_text
     * The actual text of this code block.
     * @param text   the new value of m_text
     */
    void setText ( const QString & text );

    /**
     * Add text to this object.
     * @param text   the text to add
     */
    void appendText ( const QString & text );

    /**
     * Get the value of m_text
     * The actual text of this code block.
     * @return   the value of m_text
     */
    QString getText() const;

    /**
     * Get the tag of this text block. This tag
     * may be used to find this text block in the code document
     * to which it belongs.
     * @return   the tag
     */
    QString getTag() const;

    /**
     * Set the tag of this text block. This tag
     * may be used to find this text block in the code document
     * to which it belongs.
     * @param value   the new value for the tag
     */
    void setTag( const QString & value );

    /**
     * Get the value of m_parentDoc
     * @return the value of m_parentDoc
     */
    CodeDocument * getParentDocument() const;

    /**
     * Set the value of m_writeOutText
     * Whether or not to include the text of this TextBlock into a file.
     * @param write   the new value of m_writeOutText
     */
    void setWriteOutText( bool write );

    /**
     * Get the value of m_writeOutText
     * Whether or not to include the text of this TextBlock into a file.
     * @return the value of m_writeOutText
     */
    bool getWriteOutText() const;

    /**
     * Set how many times to indent this text block.
     * The amount of each indenatation is determined from the parent
     * codedocument codegeneration policy.
     * @param level   the new value for the indentation level
     */
    void setIndentationLevel( int level );

    /**
     * Get how many times to indent this text block.
     * The amount of each indenatation is determined from the parent
     * codedocument codegeneration policy.
     * @return   the indentation level
     */
    int getIndentationLevel() const;

    /**
     * Get the actual amount of indentation for a given level of indentation.
     * @param level   the level of interest
     * @return        the indentation string
     */
    QString getIndentationString ( int level = 0) const;

    /**
     * Get how much a single "level" of indentation will actually indent.
     * @return   the unit of indentation (for one level)
     */
    static QString getIndentation();

    /**
     * Get the new line chars which ends the line.
     * @return   the ending chars for new line
     */
    static QString getNewLineEndingChars();

    /**
     * Format a long text string to be more readable.
     * @param text               the original text for formatting
     * @param linePrefix         a line prefix
     * @param breakStr           a break string
     * @param alwaysAddBreak     control to add always a break string
     * @param lastLineHasBreak   control to add a break string to the last line
     * @return                   the new formatted text
     */
    static QString formatMultiLineText ( const QString & text, const QString & linePrefix,
                                  const QString & breakStr,
                                  bool alwaysAddBreak = true, bool lastLineHasBreak = true);

    /**
     * UnFormat a long text string. Typically, this means removing
     * the indentaion (linePrefix) and/or newline chars from each line.
     * If an indentation is not specified, then the current indentation is used.
     * @param text     the original text for unformatting
     * @param indent   the indentation
     * @return         the unformatted text
     */
    virtual QString unformatText ( const QString & text, const QString & indent = "");

    /**
     * Return the text in the right format. Returned string is empty
     * if m_writeOutText is false.
     * @return  QString
     */
    virtual QString toString() const;

    /**
     * Encode text for XML storage.
     * We simply convert all types of newLines to the "\n" or &#010;
     * entity.
     * @param text       the not yet encoded text
     * @param endChars   the chars at the end of each line
     * @return           the encoded text
     */
    static QString encodeText(const QString & text , const QString & endChars);

    /**
     * Decode text from XML storage.
     * We simply convert all newLine entity &#010; to chosen line ending.
     * @param text       the not yet decoded text
     * @param endChars   the chars at the end of each line
     * @return           the decoded text
     */
    static QString decodeText(const QString & text, const QString & endChars);

    /**
     * Save the XMI representation of this object
     * @param doc    the xmi document
     * @param root   the starting point to append
     */
    virtual void saveToXMI ( QDomDocument & doc, QDomElement & root ) = 0;

    /**
     * Load params from the appropriate XMI element node.
     * @param root   the starting point in the xmi document to load from
     */
    virtual void loadFromXMI ( QDomElement & root ) = 0;

    /**
     * Determine if its OK to delete this textblock from the document.
     * Used by the text editor to know if deletion could cause a crash of
     * the program.
     * @return   the value of m_canDelete
     */
    bool canDelete() const;

    /**
     * Set the class attributes from a passed object.
     * @param obj   text block from which the attributes are taken
     */
    virtual void setAttributesFromObject (TextBlock * obj);

    /**
     * Used by the CodeEditor. It provides it with an appropriate
     * starting string for a new line of text within the given textblock
     * (for example a string with the proper indentation).
     * If the indentation amount is '0' the current indentation string will
     * be used.
     * <p>
     * TODO: Can be refactored away and replaced with
     * <a href="#getIndentationString">getIndentationString</a>.
     * @param indentAmount   the number of indent steps to use
     * @return               the new line
     */
    virtual QString getNewEditorLine( int indentAmount = 0 );

    /**
     * Ush. These are terrifically bad and must one day go away.
     * Both methods indicate the range of lines in this textblock
     * which may be edited by the codeeditor (assuming that any are
     * actually editable). The default case is no lines are editable.
     * The line numbering starts with '0' and a '-1' means no line
     * qualifies.
     * @return   line number
     */
    virtual int firstEditableLine();

    /**
     * @see firstEditableLine
     */
    virtual int lastEditableLine();

protected:

    /**
     * Set the attribute m_canDelete.
     * @param canDelete   the new value to set
     */
    void setCanDelete(bool canDelete);

    /**
     * Causes the text block to release all of its connections
     * and any other text blocks that it 'owns'.
     * needed to be called prior to deletion of the textblock.
     */
    virtual void release ();

    /**
     * Set attributes of the node that represents this class
     * in the XMI document.
     * @param doc            the xmi document
     * @param blockElement   the xmi element holding the attributes
     */
    virtual void setAttributesOnNode ( QDomDocument & doc, QDomElement & blockElement);

    /**
     * Set the class attributes of this object from
     * the passed element node.
     * @param element   the xmi element from which to load
     */
    virtual void setAttributesFromNode ( QDomElement & element);

    friend QTextStream& operator<<(QTextStream& os, const TextBlock& obj);

private:

    // The actual text of this code block.
    QString m_text;
    QString m_tag;

    bool m_canDelete;

    // Whether or not to include the text of this TextBlock into a file.
    bool m_writeOutText;

    int m_indentationLevel;
    CodeDocument * m_parentDocument;

};

QTextStream& operator<<(QTextStream& os, const TextBlock& obj);

#endif // TEXTBLOCK_H
