///
///  Copyright (C) 2004-2007 Andrej Vodopivec <andrejv@users.sourceforge.net>
///
///  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.
///
///  This program 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 General Public License for more details.
///
///
///  You should have received a copy of the GNU General Public License
///  along with this program; if not, write to the Free Software
///  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
///

#include "MathParser.h"

#include "FracCell.h"
#include "ExptCell.h"
#include "TextCell.h"
#include "SubCell.h"
#include "SqrtCell.h"
#include "LimitCell.h"
#include "MatrCell.h"
#include "ParenCell.h"
#include "AbsCell.h"
#include "AtCell.h"
#include "DiffCell.h"
#include "SumCell.h"
#include "IntCell.h"
#include "FunCell.h"
#include "EditorCell.h"
#include "ImgCell.h"
#include "SubSupCell.h"

#include <wx/wx.h>
#include <wx/config.h>

#define MAXLENGTH 50000

MathParser::MathParser()
{}

MathParser::~MathParser()
{}

MathCell* MathParser::ParseFracTag(xmlNodePtr node)
{
  FracCell *frac = new FracCell;
  frac->SetFracStyle(m_FracStyle);
  frac->SetHighlight(m_highlight);
  xmlNodePtr child = node->children;
  if (child)
  {
    frac->SetNum(ParseTag(child, false));
    child = child->next;
    if (child)
    {
      frac->SetDenom(ParseTag(child, false));
      if (node->properties != NULL)
      {
        frac->SetFracStyle(FC_CHOOSE);
      }
      frac->SetType(m_ParserStyle);
      frac->SetupBreakUps();
      return frac;
    }
  }
  delete frac;
  return NULL;
}

MathCell* MathParser::ParseDiffTag(xmlNodePtr node)
{
  DiffCell *diff = new DiffCell;
  xmlNodePtr child = node->children;
  if (child)
  {
    int fc = m_FracStyle;
    m_FracStyle = FC_DIFF;
    diff->SetDiff(ParseTag(child, false));
    m_FracStyle = fc;
    child = child->next;
    if (child)
    {
      diff->SetBase(ParseTag(child, true));
      diff->SetType(m_ParserStyle);
      return diff;
    }
  }
  delete diff;
  return NULL;
}

MathCell* MathParser::ParseSupTag(xmlNodePtr node)
{
  ExptCell *expt = new ExptCell;
  if (node->properties != NULL)
    expt->IsMatrix(true);
  xmlNodePtr child = node->children;
  if (child)
  {
    expt->SetBase(ParseTag(child, false));
    child = child->next;
    if (child)
    {
      MathCell* power = ParseTag(child, false);
      power->SetExponentFlag();
      expt->SetPower(power);
      expt->SetType(m_ParserStyle);
      return expt;
    }
  }
  delete expt;
  return NULL;
}

MathCell* MathParser::ParseSubSupTag(xmlNodePtr node)
{
  SubSupCell *subsup = new SubSupCell;
  xmlNodePtr child = node->children;
  if (child)
  {
    subsup->SetBase(ParseTag(child, false));
    child = child->next;
    if (child)
    {
      MathCell* index = ParseTag(child, false);
      index->SetExponentFlag();
      subsup->SetIndex(index);
      child = child->next;
      if (child)
      {
        MathCell* power = ParseTag(child, false);
        power->SetExponentFlag();
        subsup->SetExponent(power);
        subsup->SetType(m_ParserStyle);
        return subsup;
      }
    }
  }
  delete subsup;
  return NULL;
}

MathCell* MathParser::ParseSubTag(xmlNodePtr node)
{
  SubCell *sub = new SubCell;
  xmlNodePtr child = node->children;
  if (child)
  {
    sub->SetBase(ParseTag(child, false));
    child = child->next;
    if (child)
    {
      MathCell* index = ParseTag(child, false);
      index->SetExponentFlag();
      sub->SetIndex(index);
      sub->SetType(m_ParserStyle);
      return sub;
    }
  }
  delete sub;
  return NULL;
}

MathCell* MathParser::ParseAtTag(xmlNodePtr node)
{
  AtCell *at = new AtCell;
  xmlNodePtr child = node->children;
  if (child)
  {
    at->SetBase(ParseTag(child, false));
    at->SetHighlight(m_highlight);
    child = child->next;
    if (child)
    {
      at->SetIndex(ParseTag(child, false));
      at->SetType(m_ParserStyle);
      return at;
    }
  }
  delete at;
  return NULL;
}

MathCell* MathParser::ParseFunTag(xmlNodePtr node)
{
  FunCell *fun = new FunCell;
  xmlNodePtr child = node->children;
  if (child)
  {
    fun->SetName(ParseTag(child, false));
    child = child->next;
    if (child)
    {
      fun->SetType(m_ParserStyle);
      fun->SetArg(ParseTag(child, false));
      return fun;
    }
  }
  delete fun;
  return NULL;
}

MathCell* MathParser::ParseText(xmlNodePtr node, int style)
{
  TextCell* cell = new TextCell;
  if (node != NULL && node->content != NULL)
  {
    wxString str((const char*)(node->content), wxConvUTF8);
    str = ToLocal(str);
    if (style == TS_NUMBER)
    {
      if (str.Length() > 100) // This could be made configurable.
        str = str.Left(30) + wxString::Format(wxT("[%d digits]"), str.Length() - 60) + str.Right(30);
    }
    cell->SetValue(str);
    cell->SetType(m_ParserStyle);
    cell->SetStyle(style);
    cell->SetHighlight(m_highlight);
  }
  return cell;
}

MathCell* MathParser::ParseSqrtTag(xmlNodePtr node)
{
  xmlNodePtr child = node->children;
  SqrtCell* cell = new SqrtCell;
  cell->SetInner(ParseTag(child, true));
  cell->SetType(m_ParserStyle);
  cell->SetHighlight(m_highlight);
  return cell;
}

MathCell* MathParser::ParseAbsTag(xmlNodePtr node)
{
  xmlNodePtr child = node->children;
  AbsCell* cell = new AbsCell;
  cell->SetInner(ParseTag(child, true));
  cell->SetType(m_ParserStyle);
  cell->SetHighlight(m_highlight);
  return cell;
}

MathCell* MathParser::ParseParenTag(xmlNodePtr node)
{
  xmlNodePtr child = node->children;
  ParenCell* cell = new ParenCell;
  cell->SetInner(ParseTag(child, true), m_ParserStyle);
  cell->SetHighlight(m_highlight);
  if (node->properties != NULL)
    cell->SetPrint(false);
  return cell;
}

MathCell* MathParser::ParseLimitTag(xmlNodePtr node)
{
  LimitCell *limit = new LimitCell;
  xmlNodePtr child = node->children;
  if (child)
  {
    limit->SetName(ParseTag(child, false));
    child = child->next;
    if (child)
    {
      limit->SetUnder(ParseTag(child, false));
      child = child->next;
      if (child)
      {
        limit->SetBase(ParseTag(child, false));
        limit->SetType(m_ParserStyle);
        return limit;
      }
    }
  }
  delete limit;
  return NULL;
}

MathCell* MathParser::ParseSumTag(xmlNodePtr node)
{
  SumCell *sum = new SumCell;
  xmlNodePtr child = node->children;
  if (node->properties != NULL)
    sum->SetSumStyle(SM_PROD);
  sum->SetHighlight(m_highlight);
  if (child)
  {
    sum->SetUnder(ParseTag(child, false));
    child = child->next;
    if (child)
    {
      sum->SetOver(ParseTag(child, false));
      child = child->next;
      if (child)
      {
        sum->SetBase(ParseTag(child, false));
        sum->SetType(m_ParserStyle);
        return sum;
      }
    }
  }
  delete sum;
  return NULL;
}

MathCell* MathParser::ParseIntTag(xmlNodePtr node)
{
  IntCell *in = new IntCell;
  xmlNodePtr child = node->children;
  in->SetHighlight(m_highlight);
  if (node->properties == NULL)
  {
    in->SetIntStyle(INT_DEF);
    if (child)
    {
      in->SetUnder(ParseTag(child, false));
      child = child->next;
      if (child)
      {
        in->SetOver(ParseTag(child, false));
        child = child->next;
        if (child)
        {
          in->SetBase(ParseTag(child, false));
          child = child->next;
          if (child)
          {
            in->SetVar(ParseTag(child, true));
            in->SetType(m_ParserStyle);
            return in;
          }
        }
      }
    }
  }
  else
  {
    if (child)
    {
      in->SetBase(ParseTag(child, false));
      child = child->next;
      if (child)
      {
        in->SetVar(ParseTag(child, true));
        in->SetType(m_ParserStyle);
        return in;
      }
    }
  }
  delete in;
  return NULL;
}

MathCell* MathParser::ParseTableTag(xmlNodePtr node)
{
  MatrCell *matrix = new MatrCell;
  matrix->SetHighlight(m_highlight);

  if (node->properties != NULL)
    matrix->SetSpecialFlag(true);

  xmlNodePtr rows = node->children;
  while (rows)
  {
    matrix->NewRow();
    xmlNodePtr cells = rows->children;
    while (cells)
    {
      matrix->NewColumn();
      matrix->AddNewCell(ParseTag(cells, false));
      cells = cells->next;
    }
    rows = rows->next;
  }
  matrix->SetType(m_ParserStyle);
  matrix->SetDimension();
  return matrix;
}

MathCell* MathParser::ParseTag(xmlNodePtr node, bool all)
{
  //  wxYield();
  MathCell* tmp = NULL;
  MathCell* cell = NULL;
  while (node)
  {
    // Parse tags
    if (node->type == XML_ELEMENT_NODE)
    {
      wxString tagName((const char*)(node->name), wxConvUTF8);
      tagName = ToLocal(tagName);

      if (tagName == wxT("v"))
      {               // Variables (atoms)
        if (cell == NULL)
          cell = ParseText(node->children, TS_VARIABLES);
        else
          cell->AppendCell(ParseText(node->children, TS_VARIABLES));
      }
      else if (tagName == wxT("t"))
      {          // Other text
        if (cell == NULL)
          cell = ParseText(node->children, TS_NORMAL_TEXT);
        else
          cell->AppendCell(ParseText(node->children, TS_NORMAL_TEXT));
      }
      else if (tagName == wxT("n"))
      {          // Numbers
        if (cell == NULL)
          cell = ParseText(node->children, TS_NUMBER);
        else
          cell->AppendCell(ParseText(node->children, TS_NUMBER));
      }
      else if (tagName == wxT("h"))
      {          // Hidden cells (*)
        MathCell* tmp = ParseText(node->children);
        tmp->m_isHidden = true;
        if (cell == NULL)
          cell = tmp;
        else
          cell->AppendCell(tmp);
      }
      else if (tagName == wxT("p"))
      {          // Parenthesis
        if (cell == NULL)
          cell = ParseParenTag(node);
        else
          cell->AppendCell(ParseParenTag(node));
      }
      else if (tagName == wxT("f"))
      {               // Fractions
        if (cell == NULL)
          cell = ParseFracTag(node);
        else
          cell->AppendCell(ParseFracTag(node));
      }
      else if (tagName == wxT("e"))
      {          // Exponentials
        if (cell == NULL)
          cell = ParseSupTag(node);
        else
          cell->AppendCell(ParseSupTag(node));
      }
      else if (tagName == wxT("i"))
      {          // Subscripts
        if (cell == NULL)
          cell = ParseSubTag(node);
        else
          cell->AppendCell(ParseSubTag(node));
      }
      else if (tagName == wxT("fn"))
      {         // Functions
        if (cell == NULL)
          cell = ParseFunTag(node);
        else
          cell->AppendCell(ParseFunTag(node));
      }
      else if (tagName == wxT("g"))
      {          // Greek constants
        MathCell* tmp = ParseText(node->children, TS_GREEK_CONSTANT);
        if (cell == NULL)
          cell = tmp;
        else
          cell->AppendCell(tmp);
      }
      else if (tagName == wxT("s"))
      {          // Special constants %e,...
        MathCell* tmp = ParseText(node->children, TS_SPECIAL_CONSTANT);
        if (cell == NULL)
          cell = tmp;
        else
          cell->AppendCell(tmp);
      }
      else if (tagName == wxT("q"))
      {          // Square roots
        if (cell == NULL)
          cell = ParseSqrtTag(node);
        else
          cell->AppendCell(ParseSqrtTag(node));
      }
      else if (tagName == wxT("d"))
      {          // Differentials
        if (cell == NULL)
          cell = ParseDiffTag(node);
        else
          cell->AppendCell(ParseDiffTag(node));
      }
      else if (tagName == wxT("sm"))
      {         // Sums
        if (cell == NULL)
          cell = ParseSumTag(node);
        else
          cell->AppendCell(ParseSumTag(node));
      }
      else if (tagName == wxT("in"))
      {         // integrals
        if (cell == NULL)
          cell = ParseIntTag(node);
        else
          cell->AppendCell(ParseIntTag(node));
      }
      else if (tagName == wxT("mspace"))
      {
        if (cell == NULL)
          cell = new TextCell(wxT(" "));
        else
          cell->AppendCell(new TextCell(wxT(" ")));
      }
      else if (tagName == wxT("at"))
      {
        if (cell == NULL)
          cell = ParseAtTag(node);
        else
          cell->AppendCell(ParseAtTag(node));
      }
      else if (tagName == wxT("a"))
      {
        if (cell == NULL)
          cell = ParseAbsTag(node);
        else
          cell->AppendCell(ParseAbsTag(node));
      }
      else if (tagName == wxT("ie"))
      {
        if (cell == NULL)
          cell = ParseSubSupTag(node);
        else
          cell->AppendCell(ParseSubSupTag(node));
      }
      else if (tagName == wxT("lm"))
      {
        if (cell == NULL)
          cell = ParseLimitTag(node);
        else
          cell->AppendCell(ParseLimitTag(node));
      }
      else if (tagName == wxT("r"))
      {
        if (cell == NULL)
          cell = ParseTag(node->children);
        else
          cell->AppendCell(ParseTag(node->children));
      }
      else if (tagName == wxT("tb"))
      {
        if (cell == NULL)
          cell = ParseTableTag(node);
        else
          cell->AppendCell(ParseTableTag(node));
      }
      else if (tagName == wxT("mth"))
      {
        MathCell *tmp = ParseTag(node->children);
        if (tmp != NULL)
          tmp->ForceBreakLine(true);
        else
          tmp = new TextCell(wxT(" "));
        if (cell == NULL)
          cell = tmp;
        else
          cell->AppendCell(tmp);
      }
      else if (tagName == wxT("lbl"))
      {
        int oldPS = m_ParserStyle;
        m_ParserStyle = MC_TYPE_LABEL;
        MathCell* tmp = ParseTag(node->children);
        m_ParserStyle = oldPS;
        tmp->ForceBreakLine(true);
        if (cell == NULL)
          cell = tmp;
        else
          cell->AppendCell(tmp);
      }
      else if (tagName == wxT("st"))
      {
        MathCell* tmp = ParseText(node->children, TS_STRING);
        if (cell == NULL)
          cell = tmp;
        else
          cell->AppendCell(tmp);
      }
      else if (tagName == wxT("hl"))
      {
        bool highlight = m_highlight;
        m_highlight = true;
        MathCell* tmp = ParseTag(node->children);
        m_highlight = highlight;
        if (cell == NULL)
          cell = tmp;
        else
          cell->AppendCell(tmp);
      }
      else if (tagName == wxT("img"))
      {
        wxString filename((const char*)(node->children->content), wxConvUTF8);
        filename = ToLocal(filename);

        ImgCell *tmp = new ImgCell;
        tmp->LoadImage(filename);
        wxRemoveFile(filename);
        if (cell == NULL)
          cell = tmp;
        else
          cell->AppendCell(tmp);
      }
      else if (node->children)
      {
        if (cell == NULL)
          cell = ParseTag(node->children);
        else
          cell->AppendCell(ParseTag(node->children));
      }
    }
    // Parse text
    else
    {
      if (cell == NULL)
        cell = ParseText(node);
      else
        cell->AppendCell(ParseText(node));
    }
    if (!all)
      break;

    if (tmp == NULL)
      tmp = cell;
    else
      cell = cell->m_next;

    node = node->next;
  }

  if (tmp != NULL)
    return tmp;
  return cell;
}

/***
 * Parse the string s, which is (correct) xml fragment.
 * Put the result in line.
 */
MathCell* MathParser::ParseLine(wxString s, int style)
{
  m_ParserStyle = style;
  m_FracStyle = FC_NORMAL;
  m_highlight = false;
  MathCell* cell = NULL;

  wxConfigBase* config = wxConfig::Get();
  bool showLong = false;
  config->Read(wxT("showLong"), &showLong);

  if (s.Length() < MAXLENGTH || showLong)
  {
    s = ToUnicode(s);

#if wxUSE_UNICODE
    char *buf;
    wxWX2MBbuf tmp = wxConvertWX2MB(s.wx_str());
    buf = strdup(tmp);
    xmlDocPtr doc = xmlParseMemory(buf, strlen(buf));
    free(buf);
#else
    xmlDocPtr doc = xmlParseMemory(s.c_str(), s.Length());
#endif

    if (doc != NULL)
    {
      cell = ParseTag(xmlDocGetRootElement(doc));
      xmlFreeDoc(doc);
    }
  }
  else
  {
    cell = new TextCell(_(" << Expression too long to display! >>"));
    cell->ForceBreakLine(true);
  }
  return cell;
}

wxString MathParser::ToUnicode(wxString s)
{
#if wxUSE_UNICODE
  return s;
#else
  return wxString(s.wc_str(wxConvLocal), wxConvUTF8);
#endif
}

wxString MathParser::ToLocal(wxString s)
{
#if wxUSE_UNICODE
  return s;
#else
  return wxString(s.wc_str(wxConvUTF8), wxConvLocal);
#endif
}


syntax highlighted by Code2HTML, v. 0.9.1