/* Counter.C
 *
 * LameTeX needs to keep track of a number of counters, just like LaTeX does,
 * for things like tables, footnotes, chapters and sections, and enumerations.
 * Since all counters need to be handled in much the same way, the Counter
 * derived class under the Parameters class has been defined.
 *
 * 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 "Counter.h"
#include "Document.h"
#include "Font.h"
#include <string.h>
#include <stdio.h>

Counter::Counter()
{
   for(int x=0; x < LastType; x++)
      counters[x] = 0;
}

Counter::Counter(Counter *base)
{
   for(int x=0; x < LastType; x++)
      counters[x] = base->counters[x];
}

Param *Counter::copy()
{
   return new Counter(this);
}

int Counter::set(int subtype, float print_number, char *)
{
   char number[10];
   char message[20];
   char output[20];
   if(subtype >= Part && subtype <= Subparagraph) {
      Token openbrace;
      if(!openbrace.match("{"))
	 Global::files->fatal_error("Expecting '{' after section statement");
      if(print_number) {
	   counters[subtype]++;
	   switch(subtype) {
	     case Part:
		counters[Chapter]=0;
	     case Chapter:
		counters[Section]=0;
	     case Section:
		counters[Subsection]=0;
	     case Subsection:
		counters[Subsubsection]=0;
	     case Subsubsection:
		counters[Paragraph]=0;
	     case Paragraph:
		counters[Subparagraph]=0;
	     case Subparagraph:
	     default:
		break;
	   }
      } else
	   counters[subtype] = -1;
   }

   float fontsize = Global::stack->get(Environment::PFont, Font::Size, "");

   switch(subtype) {
   case Part:
      Global::files->outfile << endl;
      if(print_number) {
	   Operator::plaintext("Part");
	   upcase_roman(counters[Part], number);
	   Operator::plaintext(number);
      }
      Operator::do_vspace(0, 0, 200.0, "");
      break;
   case Chapter:
      Global::files->outfile << endl;
      if(Global::files->plain_text_output)
	   Operator::do_vspace(0, 0, 13.0, "");
      else
	   Operator::do_vspace(0, 0, 112.5 + (fontsize-18.0) * 0.85, "");
      if(print_number) {
	   Operator::plaintext("Chapter");
	   arabic(counters[Chapter], number);
	   Operator::plaintext(number);

	   if(Global::files->plain_text_output)
		Operator::do_vspace(0, 0, 13.0, "");
	   else
		Operator::do_vspace(0, 0, 50.0 + (fontsize-22.0) * 0.85, "");
      }
      Stack::set(Environment::PFont, Font::Huge, 0.0, "");
      break;
   case Section:
      Global::files->outfile << endl;
      if(Global::files->plain_text_output)
	   Operator::do_vspace(0, 0, 8.0, "");
      else
	   Operator::do_vspace(0, 0, 20.7 + (fontsize-13.0) * 3.4, "");
      if(print_number) {
         sprintf(number,"%d.%d",counters[Chapter],counters[Section]);
         Operator::plaintext(number);
         Global::files->outfile << endl;
         Global::files->outfile << 15.0 + (fontsize-13.0) * 1.1
	      << " HSpace" << endl;
      }
      break;
   case Subsection:
      Global::files->outfile << endl;
      Operator::do_vspace(0, 0, 4.5 + (fontsize-11.0) * 3.4, "");
      if(print_number) {
	   sprintf(number,"%d.%d.%d",counters[Chapter],counters[Section],
		   counters[Subsection]);
	   Operator::plaintext(number);
	   Global::files->outfile << endl;
	   Global::files->outfile << 15.0 + (fontsize-11.0) * 1.1
		<< " HSpace" << endl;
      }
      break;
   case Subsubsection:
   case Paragraph:
   case Subparagraph:
      break;
   case Page:
      break;
   case Equation:
      break;
   case Figure:
      break;
   case Table:
      break;
   case Footnote:
      break;
   case Mpfootnote:
      break;
   case Description:
      counters[Item] = Description;   // Mark Description most recent
      counters[Description]=0;
      break;
   case Itemize:
      counters[Item] = Itemize;   // Mark Itemize most recent
      break;
   case Enum:
      if(++counters[Enum] > 4)
	 Global::files->fatal_error(
            "Can't have more than four nested enumerates");
      counters[Item] = Enum;      // Mark Enumerate most recent
      switch(counters[Enum]) {
      case 1:
	 counters[Enumi]=0;
	 break;
      case 2:
 	 counters[Enumii]=0;
	 break;
      case 3:
	 counters[Enumiii]=0;
	 break;
      case 4:
	 counters[Enumiv]=0;
	 break;
      }
      break;
   case Enumi:
      break;
   case Enumii:
      break;
   case Enumiii:
      break;
   case Enumiv:
      break;
   case Item:
      switch(counters[Item]) {
      case Description:
	 description();
	 break;
      case Itemize:
	 Stack::set(Environment::PDocument, Document::NewLine, 0.0, "");
	 Global::files->force_start_page();
	 Global::files->outfile << endl;
	 Global::files->outfile << "BULLET" << endl;
	 break;
      case Enum:
	 Stack::set(Environment::PDocument, Document::NewLine, 0.0, "");
	 Global::files->force_start_page();
	 Global::files->outfile << endl;
	 switch(counters[Enum]) {
	 case 1:
	    arabic(++counters[Enumi], message);
	    strcat(message, ".");
	    break;
	 case 2:
	    message[0]='(';
	    downcase_alpha(++counters[Enumii], &message[1]);
	    strcat(message, ")");
	    break;
	 case 3:
	    downcase_roman(++counters[Enumiii], message);
	    strcat(message, ".");
	    break;
	 case 4:
	    upcase_alpha(++counters[Enumiv], message);
	    strcat(message, ".");
	    break;
	 }
	 Global::files->force_start_page();
	 Operator::registrar(message, output);
	 Global::files->outfile << " " << output << " ENUMERATE" << endl;
         break;
      default:
         break;
      }
      break;
   default:
      break;
   }
   return TRUE;
}

/* Handles the \item command in a description. If the item is the 0th
 * item, then make an indentation.
 */
void Counter::description()
{
   Stack::relative_set(Environment::PLength, Length::Parameter,
		       -22.5, "\\oddsidemargin");
   Stack::relative_set(Environment::PLength, Length::Parameter,
		       22.5, "\\textwidth");
   Stack::set(Environment::PDocument, Document::NewLine, 0.0, "");
   Global::files->force_start_page();
   Global::files->outfile << endl;

   Token openbrace;
   if(!openbrace.match("["))
      Global::files->fatal_error("Expecting '[' after \\item");
   Stack::set(Environment::PFont, Font::Bold, 0.0,  "");
   
   for(Token description; !description.match("]"); description=Token())
      description.handle();
   
   Global::files->outfile << endl << "13.0 HSpace" << endl;
   Stack::set(Environment::PFont, Font::Roman, 0.0,  "");
   
   Stack::relative_set(Environment::PLength, Length::Parameter,
		       22.5, "\\oddsidemargin");
   Stack::relative_set(Environment::PLength, Length::Parameter,
		       -22.5, "\\textwidth");
}

float Counter::get(int subtype, char *)
{
   return counters[subtype];
}

void Counter::postscript_set(int subtype)
{
   switch(subtype) {
   default:
      break;
   }
}

void Counter::revert(Param *from)
{
   int count;
   for(int subtype=0; subtype < LastType; subtype++)
      if((count = (int)from->get(subtype,"revert")) != counters[subtype]) {
	 if(count > 0)
	    counters[subtype] = count;
	 switch(subtype) {
	 case Part:
	    Stack::set(Environment::PDocument, Document::NewLine, 0.0, "");
	 case Chapter:
	    if(Global::files->plain_text_output)
		 Operator::do_vspace(0, 0, 13.0, "");
	    else
		 Operator::do_vspace(0, 0, 52, "");
	    break;
	 case Section:
	    if(Global::files->plain_text_output)
		 Operator::do_vspace(0, 0, 8.0, "");
	    else
		 Operator::do_vspace(0, 0, 22.4, "");
	    break;
	 case Subsection:
	    if(Global::files->plain_text_output)
		 Operator::do_vspace(0, 0, 8.0, "");
	    else
		 Operator::do_vspace(0, 0, 18.0, "");
	    break;
	 case Subsubsection:
	 case Paragraph:
	 case Subparagraph:
	    Stack::set(Environment::PDocument, Document::NewLine, 0.0, "");
	    break;
	 case Page:
	    break;
	 case Equation:
	    break;
	 case Figure:
	    break;
	 case Table:
	    break;
	 case Footnote:
	    break;
	 case Mpfootnote:
	    break;
	 case Enum:
	    counters[Enum]--;
	    break;
	 case Enumi:
	    break;
	 case Enumii:
	    break;
	 case Enumiii:
	    break;
	 case Enumiv:
	    break;
	 case Itemize:
	    break;
	 }
      }
}

void Counter::downcase_roman(int count, char *number)
{
   switch(count/100) {
   case 1:
      strcpy(number,"c");
      break;
   case 2:
      strcpy(number,"cc");
      break;
   case 3:
      strcpy(number,"ccc");
      break;
   case 4:
      strcpy(number,"cd");
      break;
   case 5:
      strcpy(number,"d");
      break;
   case 0:
   default:
      strcpy(number,"");
      break;
   }

   switch(count%100/10) {
   case 0:
      break;
   case 1:
      strcat(number,"x");
      break;
   case 2:
      strcat(number,"xx");
      break;
   case 3:
      strcat(number,"xxx");
      break;
   case 4:
      strcat(number,"xl");
      break;
   case 5:
      strcat(number,"l");
      break;
   case 6:
      strcat(number,"lx");
      break;
   case 7:
      strcat(number,"lxx");
      break;
   case 8:
      strcat(number,"lxxx");
      break;
   case 9:
      strcat(number,"xc");
      break;
   }

   switch(count%10) {
   case 0:
      break;
   case 1:
      strcat(number,"i");
      break;
   case 2:
      strcat(number,"ii");
      break;
   case 3:
      strcat(number,"iii");
      break;
   case 4:
      strcat(number,"iv");
      break;
   case 5:
      strcat(number,"v");
      break;
   case 6:
      strcat(number,"vi");
      break;
   case 7:
      strcat(number,"vii");
      break;
   case 8:
      strcat(number,"viii");
      break;
   case 9:
      strcat(number,"ix");
      break;
   }
};

void Counter::upcase_roman(int count, char *number)
{
   switch(count/100) {
   case 1:
      strcpy(number,"C");
      break;
   case 2:
      strcpy(number,"CC");
      break;
   case 3:
      strcpy(number,"CCC");
      break;
   case 4:
      strcpy(number,"CD");
      break;
   case 5:
      strcpy(number,"D");
      break;
   case 0:
   default:
      strcpy(number,"");
      break;
   }

   switch(count%100/10) {
   case 0:
      break;
   case 1:
      strcat(number,"X");
      break;
   case 2:
      strcat(number,"XX");
      break;
   case 3:
      strcat(number,"XXX");
      break;
   case 4:
      strcat(number,"XL");
      break;
   case 5:
      strcat(number,"L");
      break;
   case 6:
      strcat(number,"LX");
      break;
   case 7:
      strcat(number,"LXX");
      break;
   case 8:
      strcat(number,"LXXX");
      break;
   case 9:
      strcat(number,"XC");
      break;
   }

   switch(count%10) {
   case 0:
      break;
   case 1:
      strcat(number,"I");
      break;
   case 2:
      strcat(number,"II");
      break;
   case 3:
      strcat(number,"III");
      break;
   case 4:
      strcat(number,"IV");
      break;
   case 5:
      strcat(number,"V");
      break;
   case 6:
      strcat(number,"VI");
      break;
   case 7:
      strcat(number,"VII");
      break;
   case 8:
      strcat(number,"VIII");
      break;
   case 9:
      strcat(number,"IX");
      break;
   }
};

void Counter::arabic(int count, char *number)
{
   sprintf(number,"%d",count);
};

void Counter::downcase_alpha(int count, char *number)
{
   if(count >= 26) {
      sprintf(number,"%c%c",('a'+count/26-1),('a'+count%26-1));
   }
   sprintf(number,"%c",('a'+count%26-1));
};

void Counter::upcase_alpha(int count, char *number)
{
   if(count >= 26) {
      sprintf(number,"%c%c",('A'+count/26-1),('A'+count%26-1));
   }
   sprintf(number,"%c",('A'+count%26-1));
};