/* This file is part of the KDE project
   Copyright (C) 2002 Ariya Hidayat <ariyahidayat@yahoo.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 <tqstring.h>
#include <tqxml.h>
#include <tqfile.h>
#include <tqvaluestack.h>

#include <wmlparser.h>

class WMLParseState
{
  public:
    unsigned tableRow, tableCol;
    WMLFormat currentFormat;
    WMLFormatList formatList;
    WMLLayout currentLayout;
    WMLParseState();
    WMLParseState( const WMLParseState& );
    WMLParseState& operator=( const WMLParseState& );
    void assign( const WMLParseState& );
};

WMLParseState::WMLParseState()
{
  tableRow = tableCol = 0;
}

WMLParseState::WMLParseState( const WMLParseState& state )
{
  assign( state );
}

WMLParseState& WMLParseState::operator=( const WMLParseState& state )
{
  assign( state );
  return *this ;
}

void WMLParseState::assign( const WMLParseState& state )
{
  tableRow = state.tableRow;
  tableCol = state.tableCol;
  currentFormat = state.currentFormat;
  formatList = state.formatList;
  currentLayout = state.currentLayout;
}

// ContentHandler for use with the reader
class WMLHandler: public TQXmlDefaultHandler
{
  public:

    WMLHandler( WMLParser *parser ){ m_parser = parser; }

    bool startDocument();
    bool startElement( const TQString&, const TQString&, const TQString& ,
                       const TQXmlAttributes& );
    bool endElement( const TQString&, const TQString&, const TQString& );
    bool characters( const TQString& ch );

  private:

    WMLParser *m_parser;

    bool m_inBlock;
    TQString m_text;

    bool m_inLink;
    TQString m_link;
    TQString m_href;

    WMLParseState m_state;
    TQValueStack<WMLParseState> m_stateStack;
    
    bool flushParagraph();
    void pushState();
    void popState();
};

bool WMLHandler::startDocument()
{
  m_text = "";
  m_inBlock = false;

  m_link = "";
  m_href = "";
  
  return true;
}

bool WMLHandler::startElement( const TQString&, const TQString&,
                                        const TQString& qName,
                                        const TQXmlAttributes& attr )
{
  TQString tag = qName.lower();

  if( tag == "wml" )
    return m_parser->doOpenDocument();

  if( tag == "card" ) 
  {
    m_state = WMLParseState();
    TQString card_id = attr.value("id");
    TQString card_title = attr.value("title");
    return m_parser->doOpenCard( card_id, card_title );
  }

  if( tag == "p" )
  {
    m_state.currentLayout = WMLLayout();
    m_inBlock = TRUE;
    if( m_state.currentFormat.bold || 
        m_state.currentFormat.italic ||
        m_state.currentFormat.underline ||
        (m_state.currentFormat.fontsize != WMLFormat::Normal) )
      m_state.formatList.append( m_state.currentFormat );

    TQString align = attr.value("align").lower();
    if( align == "right" )
      m_state.currentLayout.align =  WMLLayout::Right;
    if( align == "center" )
      m_state.currentLayout.align =  WMLLayout::Center;

    return TRUE;
  }

  if(( tag == "b" ) || (tag == "strong") )
  {
    m_state.currentFormat.bold = TRUE;
    m_state.currentFormat.pos = m_text.length();
    m_state.formatList.append( m_state.currentFormat );
    return TRUE;
  }
 
  if(( tag == "i" ) || (tag == "em") )
  {  
    m_state.currentFormat.italic = TRUE;
    m_state.currentFormat.pos = m_text.length();  
    m_state.formatList.append( m_state.currentFormat );  
    return TRUE;  
  }  

  if( tag == "u" )
  {
    m_state.currentFormat.underline = TRUE;
    m_state.currentFormat.pos = m_text.length();
    m_state.formatList.append( m_state.currentFormat );
    return TRUE;
  }

  if( tag == "big" )
  {
    m_state.currentFormat.fontsize = WMLFormat::Big;
    m_state.currentFormat.pos = m_text.length();
    m_state.formatList.append( m_state.currentFormat );
    return TRUE;
  }

  if( tag == "small" ) 
  {
    m_state.currentFormat.fontsize = WMLFormat::Small;
    m_state.currentFormat.pos = m_text.length();
    m_state.formatList.append( m_state.currentFormat );
    return TRUE;
  }

  if( tag == "a" )
  {
    TQString href = attr.value("href");
    if( !href.isEmpty() )
    {
      m_inBlock = false;
      m_inLink = true;
      m_state.currentFormat.link = "";
      m_state.currentFormat.href = href;
      m_state.currentFormat.pos = m_text.length();
      m_state.currentFormat.len = 1;
      m_text.append( "#" ); // inline char
      return true;
    }
  }

  // open new table
  if( tag == "table" )
  {
    pushState();
    return m_parser->doBeginTable();
  }

  // open table row
  if( tag == "tr" )
  {
    m_state.tableRow++;
    return TRUE;
  }

  // open table cell, keep in sync with <p> above
  if( tag == "td" )
  {
    m_state.tableCol++;
    m_state.currentLayout = WMLLayout();
    m_inBlock = TRUE;
    m_state.formatList.append( m_state.currentFormat );
    return m_parser->doTableCell( m_state.tableRow, m_state.tableCol );
  }

  // unhandled element
  return TRUE;
}

bool WMLHandler::endElement( const TQString&, const TQString&, 
  const TQString& qName )
{
  TQString tag = qName.lower();

  if( tag == "wml" )
    return m_parser->doCloseDocument();

  if( tag == "card" )
  {
    // forget </p> before </card> ?
    m_inBlock = FALSE;
    if( !m_text.isEmpty() )
      flushParagraph();
    return m_parser->doCloseCard();
  }

  if( tag == "p" )
  {
    m_inBlock = FALSE;
    return flushParagraph();
  }

  if(( tag == "b" ) || (tag == "strong") )
  {
    m_state.currentFormat.bold = FALSE; 
    m_state.currentFormat.pos = m_text.length();
    m_state.formatList.append( m_state.currentFormat );
    return TRUE;
  }

  if(( tag == "i" ) || (tag == "em") )
  {
    m_state.currentFormat.italic = FALSE;
    m_state.currentFormat.pos = m_text.length();
    m_state.formatList.append( m_state.currentFormat );
    return TRUE;
  }

  if( tag == "u" )
  {
    m_state.currentFormat.underline = FALSE;  
    m_state.currentFormat.pos = m_text.length();
    m_state.formatList.append( m_state.currentFormat );
    return TRUE;
  }

  if( tag == "big" )
  {
    m_state.currentFormat.fontsize = WMLFormat::Normal;
    m_state.currentFormat.pos = m_text.length();
    m_state.formatList.append( m_state.currentFormat );
    return TRUE;
  }

  if( tag == "small" )
  {
    m_state.currentFormat.fontsize = WMLFormat::Normal;
    m_state.currentFormat.pos = m_text.length();
    m_state.formatList.append( m_state.currentFormat );
    return TRUE;
  }

  if( tag == "a" )
  {
    m_inBlock = true;
    m_inLink = false;
    m_state.formatList.append( m_state.currentFormat );
    return true;
  }

  // close table
  if( tag == "table" )
  {
    popState();
    return m_parser->doEndTable();
  }

  // close table row
  if( tag == "tr" )
    return TRUE; //skip

  // close table cell, like </p>
  if( tag == "td" )
  {
    m_inBlock = FALSE;
    return flushParagraph();
  }

  // unhandled
  return TRUE;
}

bool WMLHandler::characters( const TQString& ch )
{
  if( m_inBlock )
    m_text.append( ch );

  if( m_inLink )
    m_state.currentFormat.link.append( ch );

  return TRUE;
}

bool WMLHandler::flushParagraph()
{
  // calc length of each format tag
  for( unsigned i=0; i<m_state.formatList.count(); i++ )
  {
    int nextpos;
    WMLFormat& format = m_state.formatList[i];
    if( i < m_state.formatList.count()-1 )
    {
      WMLFormat& nextformat = m_state.formatList[i+1];
      nextpos = nextformat.pos;
    }
    else nextpos = m_text.length();
    if( format.len <= 0 )
      format.len = nextpos - format.pos;
  }

  bool result = m_parser->doParagraph( m_text, m_state.formatList, m_state.currentLayout );

  // ready for next paragraph
  m_text = "";
  m_state.formatList.clear();
  m_state.currentLayout = WMLLayout();

  // m_state.currentFormat = WMLFormat();
  // FIXME should we reset formatting ?

  return result;
}

void WMLHandler::pushState()
{
  m_stateStack.push( m_state );
}

void WMLHandler::popState()
{
  if( !m_stateStack.isEmpty() )
    m_state = m_stateStack.pop();
}

// formatting for the text
WMLFormat::WMLFormat()
{
  pos = len = 0;
  fontsize = Normal;
  bold = italic = underline = FALSE;
  link = "";
  href = "";
}

void WMLFormat::assign( const WMLFormat& f )
{
  pos = f.pos;
  len = f.len;
  bold = f.bold;
  italic = f.italic;
  underline = f.underline;
  fontsize = f.fontsize;
  link = f.link;
  href= f.href;
}

WMLFormat::WMLFormat( const WMLFormat& f )
{
  assign( f );
}

WMLFormat& WMLFormat::operator=( const WMLFormat& f )
{
  assign( f );
  return *this;
}

// paragraph layout info
WMLLayout::WMLLayout()
{
  align = Left;
}

void WMLLayout::assign( const WMLLayout& l )
{
  align = l.align;
}

WMLLayout::WMLLayout( const WMLLayout& l )
{
  assign( l );
}

WMLLayout& WMLLayout::operator=( const WMLLayout& l )
{
  assign( l );
  return *this;
}
 
// The basic WML parser
void WMLParser::parse( const char* filename )
{
   TQFile f( filename );
   TQXmlInputSource source( &f );
   TQXmlSimpleReader reader;
   WMLHandler handler( this );
   reader.setContentHandler( &handler );
   reader.parse( source );
}

bool WMLParser::doOpenDocument()
{
  return TRUE;
}

bool WMLParser::doCloseDocument()
{
  return TRUE;
}

bool WMLParser::doOpenCard( TQString, TQString )
{
  return TRUE;
}

bool WMLParser::doCloseCard()
{
  return TRUE;
}

bool WMLParser::doParagraph( TQString, WMLFormatList, WMLLayout )
{
  return TRUE;
}

bool WMLParser::doBeginTable()
{
  return TRUE;
}

bool WMLParser::doEndTable()
{
  return TRUE;
}

bool WMLParser::doTableCell( unsigned, unsigned )
{
  return TRUE;
}
