/* 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 <qstring.h>
#include <kdebug.h>

#include "contextstyle.h"
#include "basicelement.h"
#include "formulacursor.h"
#include "formulaelement.h"
#include "sequenceelement.h"

KFORMULA_NAMESPACE_BEGIN
using namespace std;


int BasicElement::evilDestructionCount = 0;

BasicElement::BasicElement( BasicElement* p )
        : parent( p ), m_baseline( 0 ), elementType( 0 )
{
    setX( 0 );
    setY( 0 );
    setWidth( 0 );
    setHeight( 0 );
    evilDestructionCount++;
}

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

BasicElement::BasicElement( const BasicElement& other )
    : parent( 0 ),
      m_baseline( other.m_baseline ),
      elementType( other.elementType )
{
    setX( other.getX() );
    setY( other.getY() );
    setWidth( other.getWidth() );
    setHeight( other.getHeight() );
    evilDestructionCount++;
}


bool BasicElement::readOnly( const BasicElement* /*child*/ ) const
{
    return parent->readOnly( this );
}


FormulaElement* BasicElement::formula()
{
    //if ( parent != 0 ) {
        return parent->formula();
        //}
        //return 0;
}


/**
 * Returns the element the point is in.
 */
BasicElement* BasicElement::goToPos( FormulaCursor*, bool&,
                                     const LuPixelPoint& point, const LuPixelPoint& parentOrigin )
{
    luPixel x = point.x() - (parentOrigin.x() + getX());
    if ((x >= 0) && (x < getWidth())) {
        luPixel y = point.y() - (parentOrigin.y() + getY());
        if ((y >= 0) && (y < getHeight())) {
            return this;
        }
    }
    return 0;
}

/**
 * Returns our position inside the widget.
 */
LuPixelPoint BasicElement::widgetPos()
{
    luPixel x = 0;
    luPixel y = 0;
    for (BasicElement* element = this; element != 0; element = element->parent) {
        x += element->getX();
        y += element->getY();
    }
    return LuPixelPoint(x, y);
}


/**
 * Sets the cursor inside this element to its start position.
 * For most elements that is the main child.
 */
void BasicElement::goInside(FormulaCursor* cursor)
{
    BasicElement* mainChild = getMainChild();
    if (mainChild != 0) {
        mainChild->goInside(cursor);
    }
}


void BasicElement::entered( SequenceElement* /*child*/ )
{
    formula()->tell( "" );
}


/**
 * Enters this element while moving to the left starting inside
 * the element `from'. Searches for a cursor position inside
 * this element or to the left of it.
 */
void BasicElement::moveLeft(FormulaCursor* cursor, BasicElement*)
{
    getParent()->moveLeft(cursor, this);
}


/**
 * Enters this element while moving to the right starting inside
 * the element `from'. Searches for a cursor position inside
 * this element or to the right of it.
 */
void BasicElement::moveRight(FormulaCursor* cursor, BasicElement*)
{
    getParent()->moveRight(cursor, this);
}


/**
 * Moves the cursor to a normal place where new elements
 * might be inserted.
 */
void BasicElement::normalize(FormulaCursor* cursor, Direction direction)
{
    BasicElement* element = getMainChild();
    if (element != 0) {
        if (direction == beforeCursor) {
            element->moveLeft(cursor, this);
        }
        else {
            element->moveRight(cursor, this);
        }
    }
}


QDomElement BasicElement::getElementDom( QDomDocument& doc)
{
    QDomElement de = doc.createElement(getTagName());
    writeDom(de);
    return de;
}


void BasicElement::writeMathML( QDomDocument& doc, QDomNode& parent, bool oasisFormat ) const
{
    QDomElement de = doc.createElement( oasisFormat ? "math:" + getElementName() : getElementName() );
    writeMathMLAttributes( de );
    writeMathMLContent( doc, de, oasisFormat );
    parent.appendChild( de );
}

bool BasicElement::buildFromDom(QDomElement element)
{
    if (element.tagName() != getTagName()) {
        kdWarning( DEBUGID ) << "Wrong tag name " << element.tagName().latin1() << " for " << getTagName().latin1() << ".\n";
        return false;
    }
    if (!readAttributesFromDom(element)) {
        return false;
    }
    QDomNode node = element.firstChild();
    return readContentFromDom(node);
}

int BasicElement::buildFromMathMLDom(QDomElement element)
{/*
    if (element.tagName() != getTagName()) {
        kdWarning( DEBUGID ) << "Wrong tag name " << element.tagName().latin1() << " for " << getTagName().latin1() << ".\n";
        return false;
		}*/
    if (!readAttributesFromMathMLDom(element)) {
        return -1;
        }
    QDomNode node = element.firstChild();
    return readContentFromMathMLDom(node);
}

/**
 * Appends our attributes to the dom element.
 */
void BasicElement::writeDom(QDomElement)
{
}

/**
 * Reads our attributes from the element.
 * Returns false if it failed.
 */
bool BasicElement::readAttributesFromDom(QDomElement)
{
    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 BasicElement::readContentFromDom(QDomNode&)
{
    return true;
}

/**
 * Returns a SequenceElement constructed from the nodes first child
 * if the nodes name matches the given name.
 */
bool BasicElement::buildChild( SequenceElement* child, QDomNode node, QString name )
{
    if (node.isElement()) {
        QDomElement e = node.toElement();
        if (e.tagName().upper() == name) {
            QDomNode nodeInner = e.firstChild();
            if (nodeInner.isElement()) {
                QDomElement element = nodeInner.toElement();
                return child->buildFromDom( element );
            }
        }
    }
    return false;
}

/**
 * Reads our attributes from the MathML element.
 * Returns false if it failed.
 */
bool BasicElement::readAttributesFromMathMLDom(const QDomElement& )
{
    return true;
}

/**
 * Reads our content from the MathML node. Sets the node to the next node
 * that needs to be read.
 * Returns false if it failed.
 */
int BasicElement::readContentFromMathMLDom(QDomNode&)
{
    return 1;
}

QString BasicElement::toLatex()
{
    return "{}";
}

/**
 * Utility function that sets the size type and returns the size value from
 * a MathML attribute string with unit as defined in Section 2.4.4.2
 *
 * @returns the size value
 *
 * @param str the attribute string.
 * @param st size type container. It will be properly assigned to its size
 * type or NoSize if str is invalid
 */
double BasicElement::getSize( const QString& str, SizeType* st )
{
    int index = str.find( "%" );
    if ( index != -1 ) {
        return str2size( str, st, index, RelativeSize ) / 100.0;
    }
    index = str.find( "pt", 0, false );
    if ( index != -1 ) {
        return str2size( str, st, index, AbsoluteSize );
    }
    index = str.find( "mm", 0, false );
    if ( index != -1 ) {
        return str2size( str, st, index, AbsoluteSize ) * 72.0 / 20.54;
    }
    index = str.find( "cm", 0, false );
    if ( index != -1 ) {
        return str2size( str, st, index, AbsoluteSize ) * 72.0 / 2.54;
    }
    index = str.find( "in", 0, false );
    if ( index != -1 ) {
        return str2size( str, st, index, AbsoluteSize ) * 72.0;
    }
    index = str.find( "em", 0, false );
    if ( index != -1 ) {
        return str2size( str, st, index, RelativeSize );
    }
    index = str.find( "ex", 0, false );
    if ( index != -1 ) {
        return str2size( str, st, index, RelativeSize );
    }
    index = str.find( "pc", 0, false );
    if ( index != -1 ) {
        return str2size( str, st, index, AbsoluteSize ) * 12.0;
    }
    index = str.find( "px", 0, false );
    if ( index != -1 ) {
        return str2size( str, st, index, PixelSize );
    }
    // If there's no unit, assume 'pt'
    return str2size( str, st, str.length(),AbsoluteSize );
}

SizeType BasicElement::getSpace( const QString& str )
{
    if ( str == "negativeveryverythinmathspace" ) {
        return NegativeVeryVeryThinMathSpace;
    }
    if ( str == "negativeverythinmathspace" ) {
        return NegativeVeryThinMathSpace;
    }
    if ( str == "negativethinmathspace" ) {
        return NegativeThinMathSpace;
    }
    if ( str == "negativemediummathspace" ) {
        return NegativeMediumMathSpace;
    }
    if ( str == "negativethickmathspace" ) {
        return NegativeThickMathSpace;
    }
    if ( str == "negativeverythickmathspace" ) {
        return NegativeVeryThickMathSpace;
    }
    if ( str == "negativeveryverythickmathspace" ) {
        return NegativeVeryVeryThickMathSpace;
    }
    if ( str == "veryverythinmathspace" ) {
        return VeryVeryThinMathSpace;
    }
    if ( str == "verythinmathspace" ) {
        return VeryThinMathSpace;
    }
    if ( str == "thinmathspace" ) {
        return ThinMathSpace;
    }
    if ( str == "mediummathspace" ) {
        return MediumMathSpace;
    }
    if ( str == "thickmathspace" ) {
        return ThickMathSpace;
    }
    if ( str == "verythickmathspace" ) {
        return VeryThickMathSpace;
    }
    if ( str == "veryverythickmathspace" ) {
        return VeryVeryThickMathSpace;
    }
    return NoSize;
}


/**
 * Used internally by getSize()
 */
double BasicElement::str2size( const QString& str, SizeType *st, uint index, SizeType type )
{
    QString num = str.left( index );
    bool ok;
    double size = num.toDouble( &ok );
    if ( ok ) {
        if ( st ) {
            *st = type;
        }
        return size;
    }
    if ( st ) {
        *st = NoSize;
    }
    return -1;
}

KFORMULA_NAMESPACE_END
