// format_LaTeX.C --- LaTeX text formatting.
// 
// Copyright 2005 Per Abrahamsen and KVL.
//
// This file is part of Daisy.
// 
// Daisy is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
// 
// Daisy 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 Lesser Public License for more details.
// 
// You should have received a copy of the GNU Lesser Public License
// along with Daisy; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#define BUILD_DLL

#include "format_LaTeX.h"
#include "version.h"
#include "assertion.h"
#include "librarian.h"
#include "format.h"
#include "frame.h"
#include <ostream>
#include <map>
#include <time.h>

symbol
FormatLaTeX::format_type () const
{
  static const symbol type ("LaTeX");
  return type; 
}

void
FormatLaTeX::list_open ()
{ 
  list_level++;
  if (list_level > 3)
    out () << "\\begin{enumerate}\n";
  else
    out () << "\\begin{itemize}\n";
}

void
FormatLaTeX::list_close ()
{ 
  if (list_level > 3)
    out () << "\\end{enumerate}\n";
  else
    out () << "\\end{itemize}\n";
  list_level--;
}

void 
FormatLaTeX::item_open (const symbol name)
{
  out () << "\\item ";
  italic (name);
  text (": ");
}

void 
FormatLaTeX::item_close ()
{ }

void 
FormatLaTeX::table_open (const symbol format)
{
  out () << "\\begin{tabular}{" << format << "}\n";
  table_first_row.push (true);
}

void 
FormatLaTeX::table_close ()
{
  out () << "\n\\end{tabular}\n";
  daisy_assert (!table_first_row.empty ());
  table_first_row.pop ();
}

void 
FormatLaTeX::table_row_open ()
{ 
  if (table_first_row.top ())
    table_first_row.top () = false;
  else
    out () << "\\\\\n";

  table_first_column.push (true);
}

void 
FormatLaTeX::table_row_close ()
{
  daisy_assert (!table_first_column.empty ());
  table_first_column.pop ();
}

void 
FormatLaTeX::table_cell_open ()
{
  if (table_first_column.top ())
    table_first_column.top () = false;
  else
    out () << "&";
}

void 
FormatLaTeX::table_cell_close ()
{ }

void 
FormatLaTeX::table_multi_cell_open (const int cells, const symbol format)
{ 
  table_cell_open ();
  out () << "\\multicolumn{" << cells << "}{" << format <<"}{"; 
}

void
FormatLaTeX::table_multi_cell_close ()
{ out () << "}"; }

void
FormatLaTeX::typewriter_open ()
{ 
  out () << "\\begin{tt}\n";
}

void
FormatLaTeX::typewriter_close ()
{ 
  out () << "\\end{tt}\n";
}

void 
FormatLaTeX::section_open (const symbol type, const symbol title,
			   const symbol scope, const symbol label)
{
  out () << "\\" << type;
  out () << "{";
  text (title);
  out () << "}\n";
  this->label (scope, label);
  out () << "\n";
}

void 
FormatLaTeX::section_close ()
{ }

void
FormatLaTeX::document_open (const symbol where, const symbol desc)
{ 
  out () << "\
%%% " << where << " --- " << desc << ".\n\
%%%\n\
%%% This file is automatically generated, do not edit.\n"; 
}

void
FormatLaTeX::document_close (const symbol where)
{ 
  out () << "\
\n\
%%% " << where << " ends here\n";
}

void
FormatLaTeX::version ()
{ 
  const time_t now = time (NULL);
  out () << "\
\n\
\\chapter*{Version}\n\
\\label{version}\n\
\\addcontentsline{toc}{chapter}{\\numberline{}Version}\n\
\n\
Daisy version " << ::version << ".\n\
LaTeX manual generated: " << ctime (&now);
}

void
FormatLaTeX::text (const symbol text_s)
{
  const std::string text = text_s.name ();
  for (unsigned int i = 0; i < text.length (); i++)
    switch (text[i])
      {
      case '^':
	if (i+1 < text.length () && (isalnum (text[i+1]) || text[i+1] == '-'))
	  {
	    out () << "$" << text[i] << "{";
	    do
	      {
		out () << text[i+1];
		i++;
	      }
	    while (i+1 < text.length () && isalnum (text[i+1]));
	    out () << "}$";
	  }
	else
	  out () << "\\" << text[i] << "{ }";
	break;
      case '~':
	out () << "\\" << text[i] << "{ }";
	break;
      case '_':
      case '#':
      case '$':
      case '%':
      case '&':
      case '{':
      case '}':
	out () << "\\" << text[i];
	break;
      case '\\':
	out () << "$\\backslash$";
	break;
      case '[':
      case ']':
        // case '+':
      case '=':
      case '<':
      case '>':
	out () << "$" << text[i] << "$";
	break;
      default:
	out () << text[i];
      }
}

void
FormatLaTeX::bold (const symbol text)
{
  out () << "\\textbf{";
  this->text (text);
  out () << "}";
}

void
FormatLaTeX::italic (const symbol text)
{
  out () << "\\textit{";
  this->text (text);
  out () << "}";
}

void
FormatLaTeX::verbatim (const symbol text)
{
  out () << "\\begin{verbatim}\n";
  out () << text;
  out () << "\\end{verbatim}\n";
}

bool
FormatLaTeX::formatp (const symbol format)
{ return format == format_type (); }

void
FormatLaTeX::raw (const symbol format, const symbol text)
{
  daisy_assert (format == format_type ());
  out () << text;
}

void
FormatLaTeX::special (const symbol name)
{
  static struct SymTable : public std::map<symbol, symbol>
  {
    SymTable ()
    {
      typedef std::pair<symbol,symbol> p;
      insert (p ("...", "\\ldots{}"));
      insert (p ("->", "$\\rightarrow $"));
      insert (p ("nbsp", "~"));
      insert (p ("daisy", "\\daisy{}"));
    }
  } sym_table;
  daisy_assert (sym_table.find (name) != sym_table.end ());
  out () << sym_table[name];
}

void
FormatLaTeX::soft_linebreak ()
{ out () << "\n"; }

void
FormatLaTeX::hard_linebreak ()
{ out () << "\\\\\n"; }

void
FormatLaTeX::new_paragraph ()
{ out () << "\n\n"; }

void
FormatLaTeX::index (const symbol name)
{
  out () << "\\index{";
  text (name);
  out () << "}";
}

void
FormatLaTeX::quote_id (const symbol text_s)
{
  const std::string text = text_s.name ();

  for (size_t i = 0; i < text.length (); i++)
    switch (text[i])
      {
      case 'Q':
	  out () << "QQ";
	break;
      case '%':
	out () << "QPERCENT";
	break;
      default:
	out () << text[i];
      }
}

void
FormatLaTeX::label (const symbol scope, const symbol id)
{ 
  out () << "\\label{";
  quote_id (scope);
  out () << ":";
  quote_id (id);
  out () << "}"; 
}

void
FormatLaTeX::pageref (const symbol scope, const symbol id)
{ 
  out () << "\\pageref{";
  quote_id (scope);
  out () << ":";
  quote_id (id);
  out () << "}"; 
}

void
FormatLaTeX::ref (const symbol scope, const symbol id)
{ 
  out () << "\\ref{";
  quote_id (scope);
  out () << ":";
  quote_id (id);
  out () << "}"; 
}

void
FormatLaTeX::see (const symbol type,
		  const symbol scope, const symbol id)
{
  out () << "(see " << type << "~";
  ref (scope, id);
  out () << ")"; 
}

void
FormatLaTeX::see_page (const symbol scope, const symbol id)
{
  out () << "(see ";
  ref (scope, id);
  out () << ", page ";
  pageref (scope, id);
  out () << ")\n";
}

void
FormatLaTeX::cite (const std::vector<symbol>& cite)
{
  if (cite.size () < 1)
    return;
  out () << "See also \\cite{" << cite[0];
  for (size_t i = 1; i < cite.size (); i++)
    out () << "," << cite[i];
  out () << "}";
  soft_linebreak ();
}

void
FormatLaTeX::frame_description (const Frame& frame)
{
  Format::frame_description (frame);
  if (!frame.check ("cite"))
    return;
  cite (frame.name_sequence ("cite"));
}

FormatLaTeX::FormatLaTeX (const BlockModel& al)
  : Format (al),
    list_level (0)
{ }

static struct FormatLaTeXSyntax : public DeclareModel
{
  Model* make (const BlockModel& al) const
  { return new FormatLaTeX (al); }
  FormatLaTeXSyntax ()
    : DeclareModel (Format::component, "LaTeX", "Format text as LaTeX.")
  { }
  void load_frame (Frame&) const
  { }
} FormatLaTeX_syntax;
