/* Document.C
 *
 * Of all the parameters, those in the derived class Document are probably
 * the most basic. Calls to class Document routines can usually be resolved
 * directly into some simple PostScript output involving no nesting.
 *
 * Copyright 1992 Jonathan Monsarrat. Permission given to freely distribute,
 * edit and use as long as this copyright statement remains intact.
 *
 */

#include "Operator.h"
#include "Global.h"
#include "Document.h"
#include "Font.h"
#include <string.h>
#include <stdio.h>

float Document::start_page;

Document::Document()
{
   for(int x=0; x < LastType; x++)
      values[x] = 0.0;
   begin_command[0] = '\0';
   start_page = 0;
}

Document::Document(Document *base)
{
   for(int x=0; x < LastType; x++)
      values[x] = base->values[x];
   strcpy(begin_command, base->begin_command);
}

Param *Document::copy()
{
   Document *doc = new Document(this);
   doc->set(End, 0.0, begin_command);
   return doc;
}

int Document::set(int subtype, float value, char *replacestr)
{
   switch(subtype) {
   case Begin:
      strcpy(begin_command,replacestr);
      break;
   case DocumentStart:
      values[DocumentStart] = value;
      break;
   case End:
      if(strcmp(begin_command,replacestr) != 0) {
	 char message[MAXSTRING];
	 sprintf(message, "\\end{%s} does not match a \\begin{%s}",
		 replacestr, begin_command);
	 Global::files->fatal_error(message);
      }
      begin_command[0] = '\0';
      break;
   case NewLine:
      Operator::do_vspace(0, 0,
          Global::stack->get(Environment::PLength, Length::Parameter,
			     "\\baselineskip"), "");
      break;
   case StartPage:
      start_page = value;
      break;
   case Stealth:
      if(!Stack::get(Environment::PDocument, Document::Comment, ""))
	 Global::files->fatal_error(
         "\\stealth must go on a comment line to be compatible with LaTeX");
      values[Stealth] = value;
      break;
   default:
      values[subtype] = value;
      break;
   }
   return TRUE;
}

float Document::get(int subtype, char *comparestr)
{
   switch(subtype) {
   case CloseBrace:
      if(begin_command[0] != '\0') {
	 char message[MAXSTRING];
	 sprintf(message, "\\begin{%s} closed with '}' instead of \\end{%s}",
	      begin_command, begin_command);
	 Global::files->fatal_error(message);
      }
      break;
   case Begin:
   case End:
      return !strcmp(comparestr, begin_command);
   case ShutDown:         // only happens in Stack::shutdown()
      if(begin_command[0] != '\0') {
	 char message[MAXSTRING];
	 sprintf(message, "\\begin{%s} not closed at end of input",
	      begin_command);
	 Global::files->fatal_error(message);
      }
      break;
   case StartPage:
      return start_page;
   default:
      break;
   }
   return values[subtype];
}

void Document::revert(Param *from)
{
   for(int subtype=0; subtype < LastType; subtype++) {
      switch(subtype) {
      case End:
      case Begin:
      case CloseBrace:
      case ShutDown:
	 break;
      default:
	 if(values[subtype] != from->get(subtype,"revert"))
	    postscript_set(subtype);
	 break;
      }
   }
}

void Document::postscript_set(int subtype)
{
   switch(subtype) {
   case DocumentStart:          // No postscript output needed
   default:
      break;
   }
}

/* Begins an environment */
void Document::begin(int paramtype, int subtype, float value,
		  char *replacestr)
{
   Token openbrace;
   if(!openbrace.match("{"))
      Global::files->fatal_error("Expecting '{' after \\begin statement");
   
   Token command;
   if(command.match("}"))
      Global::files->
	 fatal_error("Expecting command before closing '}' in \\begin");
   
   if(!command.match("stealth"))
      Stack::push(paramtype, subtype, value, replacestr);
   
   Token closebrace;
   if(!closebrace.match("}"))
      Global::files->
	 fatal_error("More than one word before closing '}' in \\begin");
   
   char begin_command[MAXSTRING];
   strcpy(begin_command,"\\");  // Make the token "foo" into command "\foo"
   char *tokentext = command.get_text();

   // One of the values in parameter "Document" keeps track of the
   // command executed by the last \begin command, to that when the \end
   // command is called, we can check it against the \begin command.
   if(!command.match("stealth"))
      Stack::set(Environment::PDocument, Document::Begin, 0.0, tokentext);

   strcat(begin_command,tokentext);
   command.make_text(begin_command);
   command.handle();          // Handle the command.

}

/* Ends an environment */
void Document::end(int, int, float, char *)
{
   Token openbrace;
   if(!openbrace.match("{"))
      Global::files->fatal_error("Expecting '{' after \\end");

   Token command;     // Assume it's the same as the last begin command
   if(command.match("}"))
      Global::files->fatal_error(
          "Expecting command before '}' in \\end statement");
   if(!command.match("stealth")) {
      char *begin_command = command.get_text();
      Stack::set(Environment::PDocument, Document::End, 0.0, begin_command);
   }

   Token closebrace;
   if(!closebrace.match("}"))
      Global::files->
	 fatal_error("More than one word before closing '}' in \\end");

   if(command.match("stealth"))
      Stack::set(Environment::PDocument, Document::Stealth, 0.0, "");
   else
      Stack::pop(0, Document::End, 0.0, "");
}

void Document::documentstyle(int, int, float, char *)
{
   Token openbrace;
   if(openbrace.match("[")) {
      Global::files->comma_delimiter(TRUE); // Commas are valid delimiters
      
      for(Token option = Token(); !option.match("]"); option = Token()) {
	 if(option.match(""))
	    continue;

	 if(option.match("10pt")) {
	    // Do nothing. This is the default.
	 } else if(option.match("11pt")) {
	    // Makes 11 point type the default size
	    Stack::set(Environment::PFont, Font::Base11pt, 0.0, "");
	    Stack::set(Environment::PLength, Length::Parameter, 23.5,
		       "\\bigskipamount");
	    Stack::set(Environment::PLength, Length::Parameter, 18.6,
		       "\\medskipamount");
	    Stack::set(Environment::PLength, Length::Parameter, 16.0,
		       "\\smallskipamount");
	 } else if(option.match("12pt")) {
	    // Makes 12 point type the default size
	    Stack::set(Environment::PFont, Font::Base12pt, 0.0, "");
	    Stack::set(Environment::PLength, Length::Parameter, 25.7,
		       "\\bigskipamount");
	    Stack::set(Environment::PLength, Length::Parameter, 20.3,
		       "\\medskipamount");
	    Stack::set(Environment::PLength, Length::Parameter, 17.4,
		       "\\smallskipamount");
	 } else if(option.match("twoside")) {
	    // Formats the output for printing on both sides of a page
            Global::files->warning("twoside option not supported in LameTeX");
	 } else if(option.match("twocolumn")) {
	    // Makes two-column pages
            Global::files->warning(
                     "twocolumn option not supported in LameTeX");
	 } else if(option.match("titlepage")) {
	    // article option only!
            Global::files->warning("twoside option not supported in LameTeX");
	 } else if(option.match("openbib")) {
            Global::files->warning("openbib option not supported in LameTeX");
	 } else if(option.match("leqno")) {
            Global::files->warning("leqno option not supported in LameTeX");
	 } else if(option.match("fleqn")) {
            Global::files->warning("fleqn option not supported in LameTeX");
	 } else
            Global::files->warning("Unknown option in \\documentstyle[]");
      }
      Global::files->comma_delimiter(FALSE);  // Commas not valid delimiters
      openbrace = Token();               // An openbrace should come now
   }

   if(openbrace.match("{")) {
   } else
      Global::files->fatal_error("Expecting '{' or '[' in \\documentstyle");

   Token style;
   if(style.match("}"))
      Global::files->fatal_error(
           "Expecting style before '}' in \\documentstyle");
   
   if(style.match("article")) {
      // the article style does not have a \chapter command
      // the default pagestyle is plain
      // \flushbottom if [twoside], else \raggedbottom
      // numbers figures and tables consecutively throughout
      // if [titlepage] is used \abstract looks like normal article paragraph
      // \parskip is zero
   } else if(style.match("book")) {
      // \flushbottom
      // numbers figures and tables by chapter
      // there is no \abstract environment
      // \parskip is zero
   } else if(style.match("letter")) {
      // same as the \letter command. Very complex.
      // special commands \opening, \address, \signature, \cc, \encl, etc.
      // \raggedbottom
      // \parskip is non-zero
      Global::files->warning("letter style not supported in LameTeX");
   } else if(style.match("report")) {
      // the default pagestyle is plain
      // \flushbottom if [twoside], else \raggedbottom
      // numbers figures and tables by chapter
      // \abstract is placed on a separate page
      // \parskip is zero
   } else if(style.match("slides")) {
      // same as the \slide command. Very complex.
      Global::files->warning("slides style not supported in LameTeX");
   } else
      Global::files->warning(
      "Unknown style in \\documentstyle. User-defined styles not supported.");

   Token closebrace;
   if(!closebrace.match("}"))
      Global::files->fatal_error(
         "Only one word allowed between braces in \\documentstyle");
}