/* 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 <tqfontmetrics.h>
#include <tqpainter.h>

#include <kdebug.h>

#include "basicelement.h"
#include "contextstyle.h"
#include "elementtype.h"
#include "elementvisitor.h"
#include "fontstyle.h"
#include "formulaelement.h"
#include "kformulacommand.h"
#include "sequenceelement.h"
#include "symboltable.h"
#include "textelement.h"


KFORMULA_NAMESPACE_BEGIN

TextElement::TextElement(TQChar ch, bool beSymbol, BasicElement* parent)
        : BasicElement(parent), character(ch), symbol(beSymbol)
{
    charStyle( anyChar );
    charFamily( anyFamily );
}


TextElement::TextElement( const TextElement& other )
    : BasicElement( other ),
      character( other.character ),
      symbol( other.symbol ),
      m_format( other.m_format )
{
}


bool TextElement::accept( ElementVisitor* visitor )
{
    return visitor->visit( this );
}


TokenType TextElement::getTokenType() const
{
    if ( isSymbol() ) {
        return getSymbolTable().charClass( character );
    }

    switch ( character.unicode() ) {
    case '+':
    case '-':
    case '*':
        //case '/':  because it counts as text -- no extra spaces
        return BINOP;
    case '=':
    case '<':
    case '>':
        return RELATION;
    case ',':
    case ';':
    case ':':
        return PUNCTUATION;
    case '\\':
        return SEPARATOR;
    case '\0':
        return ELEMENT;
    default:
        if ( character.isNumber() ) {
            return NUMBER;
        }
        else {
            return ORDINARY;
        }
    }
}


bool TextElement::isInvisible() const
{
    if (getElementType() != 0) {
        return getElementType()->isInvisible(*this);
    }
    return false;
}


/**
 * Calculates our width and height and
 * our children's parentPosition.
 */
void TextElement::calcSizes( const ContextStyle& context, 
                             ContextStyle::TextStyle tstyle, 
                             ContextStyle::IndexStyle /*istyle*/,
                             StyleAttributes& style )
{
    double factor = style.sizeFactor();
    luPt mySize = context.getAdjustedSize( tstyle, factor );

    setCharStyle( style.charStyle() );
    setCharFamily( style.charFamily() );

    TQFont font = getFont( context, style );
    double fontsize = context.layoutUnitPtToPt( mySize );
    double scriptsize = pow( style.scriptSizeMultiplier(), style.scriptLevel() );
    double size = fontsize * scriptsize;
    font.setPointSizeFloat( size < style.scriptMinSize() ? style.scriptMinSize() : size );

    TQFontMetrics fm( font );
    if ( character == applyFunctionChar || character == invisibleTimes || character == invisibleComma ) {
        setWidth( 0 );
        setHeight( 0 );
        setBaseline( getHeight() );
    }
    else {
        TQChar ch = getRealCharacter(context);
        if ( ch == TQChar::null ) {
            setWidth( tqRound( context.getEmptyRectWidth( factor ) * 2./3. ) );
            setHeight( tqRound( context.getEmptyRectHeight( factor ) * 2./3. ) );
            setBaseline( getHeight() );
        }
        else {
            TQRect bound = fm.boundingRect( ch );
            setWidth( context.ptToLayoutUnitPt( fm.width( ch ) ) );
            setHeight( context.ptToLayoutUnitPt( bound.height() ) );
            setBaseline( context.ptToLayoutUnitPt( -bound.top() ) );

            // There are some glyphs in TeX that have
            // baseline==0. (\int, \sum, \prod)
            if ( getBaseline() == 0 ) {
                //setBaseline( getHeight()/2 + context.axisHeight( tstyle ) );
                setBaseline( -1 );
            }
        }
    }
}

/**
 * Draws the whole element including its children.
 * The `parentOrigin' is the point this element's parent starts.
 * We can use our parentPosition to get our own origin then.
 */
void TextElement::draw( TQPainter& painter, const LuPixelRect& /*r*/,
                        const ContextStyle& context,
                        ContextStyle::TextStyle tstyle,
                        ContextStyle::IndexStyle /*istyle*/,
                        StyleAttributes& style,
                        const LuPixelPoint& parentOrigin )
{
    if ( character == applyFunctionChar || character == invisibleTimes || character == invisibleComma ) {
        return;
    }

    LuPixelPoint myPos( parentOrigin.x()+getX(), parentOrigin.y()+getY() );
    //if ( !LuPixelRect( myPos.x(), myPos.y(), getWidth(), getHeight() ).intersects( r ) )
    //    return;

    // Let container set the color, instead of elementType
    //setUpPainter( context, painter );
    painter.setPen( style.color() );

    setCharStyle( style.charStyle() );
    setCharFamily( style.charFamily() );

    double factor = style.sizeFactor();
    luPt mySize = context.getAdjustedSize( tstyle, factor );
    TQFont font = getFont( context, style );
    double fontsize = context.layoutUnitPtToPt( mySize );
    double scriptsize = pow( style.scriptSizeMultiplier(), style.scriptLevel() );
    double size = fontsize * scriptsize;
    font.setPointSizeFloat( size < style.scriptMinSize() ? style.scriptMinSize() : size );
    painter.setFont( font );

    //kdDebug( DEBUGID ) << "TextElement::draw font=" << font.rawName() << endl;
    //kdDebug( DEBUGID ) << "TextElement::draw size=" << mySize << endl;
    //kdDebug( DEBUGID ) << "TextElement::draw size=" << context.layoutUnitToFontSize( mySize, false ) << endl;
    //kdDebug( DEBUGID ) << "TextElement::draw height: " << getHeight() << endl;
    //kdDebug( DEBUGID ) << "TextElement::draw width: " << getWidth() << endl;
    //kdDebug( DEBUGID ) << endl;

    // Each starting element draws the whole token
    /*
    ElementType* token = getElementType();
    if ( ( token != 0 ) && !symbol ) {
        TQString text = token->text( static_cast<SequenceElement*>( getParent() ) );
//         kdDebug() << "draw text: " << text[0].unicode()
//                   << " " << painter.font().family().latin1()
//                   << endl;
        painter.fillRect( context.layoutUnitToPixelX( parentOrigin.x() ),
                          context.layoutUnitToPixelY( parentOrigin.y() ),
                          context.layoutUnitToPixelX( getParent()->getWidth() ),
                          context.layoutUnitToPixelY( getParent()->getHeight() ),
                          style.background() );
        painter.drawText( context.layoutUnitToPixelX( myPos.x() ),
                          context.layoutUnitToPixelY( myPos.y()+getBaseline() ),
                          text );
    }
    else {
    */
        TQChar ch = getRealCharacter(context);
        if ( ch != TQChar::null ) {
            luPixel bl = getBaseline();
            if ( bl == -1 ) {
                // That's quite hacky and actually not the way it's
                // meant to be. You shouldn't calculate a lot in
                // draw. But I don't see how else to deal with
                // baseline==0 glyphs without yet another flag.
                bl = -( getHeight()/2 + context.axisHeight( tstyle, factor ) );
            }
            painter.drawText( context.layoutUnitToPixelX( myPos.x() ),
                              context.layoutUnitToPixelY( myPos.y()+bl ),
                              TQString(ch) );
        }
        else {
            painter.setPen( TQPen( context.getErrorColor(),
                                  context.layoutUnitToPixelX( context.getLineWidth( factor ) ) ) );
            painter.drawRect( context.layoutUnitToPixelX( myPos.x() ),
                              context.layoutUnitToPixelY( myPos.y() ),
                              context.layoutUnitToPixelX( getWidth() ),
                              context.layoutUnitToPixelY( getHeight() ) );
        }
//    }

    // Debug
    //painter.setBrush(TQt::NoBrush);
//     if ( isSymbol() ) {
//         painter.setPen( TQt::red );
//         painter.drawRect( context.layoutUnitToPixelX( myPos.x() ),
//                           context.layoutUnitToPixelX( myPos.y() ),
//                           context.layoutUnitToPixelX( getWidth() ),
//                           context.layoutUnitToPixelX( getHeight() ) );
//         painter.setPen(TQt::green);
//         painter.drawLine(myPos.x(), myPos.y()+axis( context, tstyle ),
//                          myPos.x()+getWidth(), myPos.y()+axis( context, tstyle ));
//     }
}


void TextElement::dispatchFontCommand( FontCommand* cmd )
{
    cmd->addTextElement( this );
}

void TextElement::setCharStyle( CharStyle cs )
{
    charStyle( cs );
    formula()->changed();
}

void TextElement::setCharFamily( CharFamily cf )
{
    charFamily( cf );
    formula()->changed();
}

TQChar TextElement::getRealCharacter(const ContextStyle& context)
{
    return character;
/*
    if ( !isSymbol() ) {
        const FontStyle& fontStyle = context.fontStyle();
        const AlphaTable* alphaTable = fontStyle.alphaTable();
        if ( alphaTable != 0 ) {
            AlphaTableEntry ate = alphaTable->entry( character,
                                                     charFamily(),
                                                     charStyle() );
            if ( ate.valid() ) {
                return ate.pos;
            }
        }
        return character;
    }
    else {
        return getSymbolTable().character(character, charStyle());
    }
*/
}


TQFont TextElement::getFont(const ContextStyle& context, const StyleAttributes& style)
{
    const FontStyle& fontStyle = context.fontStyle();
    TQFont font;
    if ( style.customFont() ) {
        font = style.font();
    }
    else if (getElementType() != 0) {
        font = getElementType()->getFont(context);
    }
    else {
        font = context.getDefaultFont();
    }
    switch ( charStyle() ) {
    case anyChar:
        font.setItalic( false );
        font.setBold( false );
        break;
    case normalChar:
        font.setItalic( false );
        font.setBold( false );
        break;
    case boldChar:
        font.setItalic( false );
        font.setBold( true );
        break;
    case italicChar:
        font.setItalic( true );
        font.setBold( false );
        break;
    case boldItalicChar:
        font.setItalic( true );
        font.setBold( true );
        break;
    }
    return fontStyle.symbolTable()->font( character, font );
}


void TextElement::setUpPainter(const ContextStyle& context, TQPainter& painter)
{
    if (getElementType() != 0) {
        getElementType()->setUpPainter(context, painter);
    }
    else {
        painter.setPen(TQt::red);
    }
}

const SymbolTable& TextElement::getSymbolTable() const
{
    return formula()->getSymbolTable();
}


void TextElement::writeMathML( TQDomDocument& doc, TQDomNode& parent, bool ) const
{
    parent.appendChild( doc.createTextNode( getCharacter() ) );
}

/**
 * Appends our attributes to the dom element.
 */
void TextElement::writeDom(TQDomElement element)
{
    BasicElement::writeDom(element);
    element.setAttribute("CHAR", TQString(character));
    //TQString s;
    //element.setAttribute("CHAR", s.sprintf( "#x%05X", character ) );
    if (symbol) element.setAttribute("SYMBOL", "3");

    switch ( charStyle() ) {
    case anyChar: break;
    case normalChar: element.setAttribute("STYLE", "normal"); break;
    case boldChar: element.setAttribute("STYLE", "bold"); break;
    case italicChar: element.setAttribute("STYLE", "italic"); break;
    case boldItalicChar: element.setAttribute("STYLE", "bolditalic"); break;
    }

    switch ( charFamily() ) {
    case normalFamily: element.setAttribute("FAMILY", "normal"); break;
    case scriptFamily: element.setAttribute("FAMILY", "script"); break;
    case frakturFamily: element.setAttribute("FAMILY", "fraktur"); break;
    case doubleStruckFamily: element.setAttribute("FAMILY", "doublestruck"); break;
    case anyFamily: break;
    }
}

/**
 * Reads our attributes from the element.
 * Returns false if it failed.
 */
bool TextElement::readAttributesFromDom(TQDomElement element)
{
    if (!BasicElement::readAttributesFromDom(element)) {
        return false;
    }
    TQString charStr = element.attribute("CHAR");
    if(!charStr.isNull()) {
        character = charStr.at(0);
    }
    TQString symbolStr = element.attribute("SYMBOL");
    if(!symbolStr.isNull()) {
        int symbolInt = symbolStr.toInt();
        if ( symbolInt == 1 ) {
            character = getSymbolTable().unicodeFromSymbolFont(character);
        }
        if ( symbolInt == 2 ) {
            switch ( character.unicode() ) {
            case 0x03D5: character = 0x03C6; break;
            case 0x03C6: character = 0x03D5; break;
            case 0x03Ba: character = 0x03BA; break;
            case 0x00B4: character = 0x2032; break;
            case 0x2215: character = 0x2244; break;
            case 0x00B7: character = 0x2022; break;
            case 0x1D574: character = 0x2111; break;
            case 0x1D579: character = 0x211C; break;
            case 0x2219: character = 0x22C5; break;
            case 0x2662: character = 0x26C4; break;
            case 0x220B: character = 0x220D; break;
            case 0x224C: character = 0x2245; break;
            case 0x03DB: character = 0x03C2; break;
            }
        }
        symbol = symbolInt != 0;
    }

    TQString styleStr = element.attribute("STYLE");
    if ( styleStr == "normal" ) {
        charStyle( normalChar );
    }
    else if ( styleStr == "bold" ) {
        charStyle( boldChar );
    }
    else if ( styleStr == "italic" ) {
        charStyle( italicChar );
    }
    else if ( styleStr == "bolditalic" ) {
        charStyle( boldItalicChar );
    }
    else {
        charStyle( anyChar );
    }

    TQString familyStr = element.attribute( "FAMILY" );
    if ( familyStr == "normal" ) {
        charFamily( normalFamily );
    }
    else if ( familyStr == "script" ) {
        charFamily( scriptFamily );
    }
    else if ( familyStr == "fraktur" ) {
        charFamily( frakturFamily );
    }
    else if ( familyStr == "doublestruck" ) {
        charFamily( doubleStruckFamily );
    }
    else {
        charFamily( anyFamily );
    }

//     kdDebug() << "charStyle=" << charStyle()
//               << "  charFamily=" << charFamily()
//               << "  format=" << int( format() ) << endl;

    return true;
}

/**
 * Reads our content from the node. Sets the node to the next node
 * that needs to be read.
 * Returns false if it failed.
 */
bool TextElement::readContentFromDom(TQDomNode& node)
{
    return BasicElement::readContentFromDom(node);
}

TQString TextElement::toLatex()
{
    if ( isSymbol() ) {
        TQString texName = getSymbolTable().name( character );
        if ( !texName.isNull() )
            return " \\" + texName + " ";
        return  " ? ";
    }
    else {
        return TQString(character);
    }
}

TQString TextElement::formulaString()
{
    if ( isSymbol() ) {
        TQString texName = getSymbolTable().name( character );
        if ( !texName.isNull() )
            return " " + texName + " ";
        return " ? ";
    }
    else {
        return character;
    }
}


EmptyElement::EmptyElement( BasicElement* parent )
    : BasicElement( parent )
{
}

EmptyElement::EmptyElement( const EmptyElement& other )
    : BasicElement( other )
{
}


bool EmptyElement::accept( ElementVisitor* visitor )
{
    return visitor->visit( this );
}


void EmptyElement::calcSizes( const ContextStyle& context,
                              ContextStyle::TextStyle tstyle,
                              ContextStyle::IndexStyle /*istyle*/,
                              StyleAttributes& style )
{
    luPt mySize = context.getAdjustedSize( tstyle, style.sizeFactor() );
    //kdDebug( DEBUGID ) << "TextElement::calcSizes size=" << mySize << endl;

    TQFont font = context.getDefaultFont();
    font.setPointSizeFloat( context.layoutUnitPtToPt( mySize ) );

    TQFontMetrics fm( font );
    TQChar ch = 'A';
    TQRect bound = fm.boundingRect( ch );
    setWidth( 0 );
    setHeight( context.ptToLayoutUnitPt( bound.height() ) );
    setBaseline( context.ptToLayoutUnitPt( -bound.top() ) );
}

void EmptyElement::draw( TQPainter& painter, const LuPixelRect& /*r*/,
                         const ContextStyle& context,
                         ContextStyle::TextStyle /*tstyle*/,
                         ContextStyle::IndexStyle /*istyle*/,
                         StyleAttributes& /*style*/ ,
                         const LuPixelPoint& parentOrigin )
{
    LuPixelPoint myPos( parentOrigin.x()+getX(), parentOrigin.y()+getY() );
    /*
    if ( !LuPixelRect( myPos.x(), myPos.y(), getWidth(), getHeight() ).intersects( r ) )
        return;
    */

    if ( context.edit() ) {
        painter.setPen( context.getHelpColor() );
        painter.drawLine( context.layoutUnitToPixelX( myPos.x() ),
                          context.layoutUnitToPixelY( myPos.y() ),
                          context.layoutUnitToPixelX( myPos.x() ),
                          context.layoutUnitToPixelY( myPos.y()+getHeight() ) );
    }
}

TQString EmptyElement::toLatex()
{
    return "{}";
}

KFORMULA_NAMESPACE_END
