/* Font.C
 *
 * Anything having to do with changing the style, family, or size of the
 * fonts is handled through this Font class.
 *
 * Copyright 1992 Jonathan Monsarrat. Permission given to freely distribute,
 * edit and use as long as this copyright statement remains intact.
 *
 */

#include "Global.h"
#include "Font.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

FontUsed *Font::_fonts_used[MAXFONTS];
int Font::_num_fonts_used;

// Build a list of fonts actually used, along with the characters
// used for each font.
FontUsed::FontUsed(char *style, char *petname, int font)
{
   strcpy(_style, style);
   strcpy(_petname, petname);
   _font = font;
   for(int x=0; x<256; x++)
      _charused[x] = (x >= '0' && x <= '9') ? 1 : 0;
}

void FontUsed::used(char *textstr)
{
   for(int x=0; textstr[x]; x++)
      _charused[textstr[x]] = 1;
}

int FontUsed::font()
{
   return _font;
}

void FontUsed::dump(ofstream &outfile)
{
   outfile << "{" << _style << endl;

   int num=0;
   for(int x=0; x < 256; x++)
      if(_charused[x]) {
         if(num++ > 25) {
	    outfile << endl << endl;
	    num=0;
	 }
	 switch(x) {
	 case '&':
	 case '#':
	 case '{':
	 case '}':
	 case '_':
	 case '%':
	 case '$':
	    outfile << '\\';
	 default:
	    outfile << (char) x << " ";
	    break;
	 }
      }

   outfile << "`` '' ." << endl;
   outfile << "}" << endl;
}

int FontUsed::match(char *textstr)
{
   return !strcmp(textstr,_petname);
}

char *FontUsed::petname()
{
   return _petname;
}

Font::Font()
{
   _basesize = 10;
   _fontsize = 10;
   strcpy(_fontstyle,"rm");
   strcpy(_fontsizecmd,"\\normalsize");
   _fonts_used[0] = new FontUsed("\\normalsize\\rm", "/cm10rm", 1);
   _num_fonts_used = 1;
   Global::files->outfile << endl;
   _currentused = 0;
   _pending = 1;
   _pending_whitespace = 1;
}

Font::Font(Font *base)
{
   _basesize = base->_basesize;
   _fontsize = base->_fontsize;
   strcpy(_fontstyle, base->_fontstyle);
   strcpy(_fontsizecmd, base->_fontsizecmd);
   _currentused = base->_currentused;
   _pending = base->_pending;
   _pending_whitespace = base->_pending_whitespace;
}

Font::~Font()
{
   for(int x=0; x < _num_fonts_used; x++)
      delete _fonts_used[x];
}

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


void Font::newsize(float val)
{
   if(_fontsize != val) {
      _fontsize = (int) val;

      /* We are updating the size of the font, so we had better update
       * also the amount of space to skip between lines. This is done
       * by doing a set statement on \baselinestretch.
       */
      if(!Global::files->plain_text_output) {
	   Stack::set(Environment::PLength, 0,
		      Stack::get(Environment::PLength, Length::Parameter,
				 "\\baselinestretch"), "\\baselinestretch");
      }
      _pending = 1;    // Print the postscript command for this.
      _pending_whitespace = Global::files->whitespace_next();
   }
}

void Font::set_fontstyle(char *style)
{
   if(strcmp(_fontstyle,style)!=0) {
      strcpy(_fontstyle,style);
      _pending = 1;    // Print the postscript command for this.
      _pending_whitespace = Global::files->whitespace_next();
   }
}

int Font::set(int subtype, float value, char *textstr)
{
   /* The Font Family is hard-coded to "cm" for Computer Modern */
   switch(subtype) {
      /* Base sizes */
   case Base10pt:    // Do nothing; this is the default.
      break;
   case Base11pt:
      delete _fonts_used[0];
      _fonts_used[0] = new FontUsed("\\normalsize\\rm", "/cm11rm", 1);
      Global::files->outfile << endl;
      Global::files->outfile << "/basefont /cm11rm def" << endl;
      _basesize = 11;
      _fontsize = 11;
      _pending = 1;
      _pending_whitespace = Global::files->whitespace_next();
      break;
   case Base12pt:
      delete _fonts_used[0];
      _fonts_used[0] = new FontUsed("\\normalsize\\rm", "/cm12rm", 1);
      Global::files->outfile << endl;
      Global::files->outfile << "/basefont /cm12rm def" << endl;
      _basesize = 12;
      _fontsize = 12;
      _pending = 1;
      _pending_whitespace = Global::files->whitespace_next();
      break;

      /* _Font styles */
   case Italic:
      if(strcmp(_fontstyle,"it")==0)
	 strcpy(_fontstyle,"rm");        // Double Italic is Roman!
      else
	 strcpy(_fontstyle,"it");
      _pending = 1;    // Print the postscript command for this.
      _pending_whitespace = Global::files->whitespace_next();
      break;
   case Bold:
      set_fontstyle("bf");
      break;
   case Roman:
      set_fontstyle("rm");
      break;
   case SansSerif:
      set_fontstyle("sf");
      break;
   case Slant:
      set_fontstyle("sl");
      break;
   case SmallCaps:
      set_fontstyle("sc");
      break;
   case Typewriter:
      set_fontstyle("tt");
      break;

      /* Sizes relative to the base size */
   case Tiny:
      strcpy(_fontsizecmd,"\\tiny");
      switch(_basesize) {
      case 10:
      default:
         newsize(4);
         break;
      case 11:
      case 12:
         newsize(6);
         break;
      }
      break;
   case Scriptsize:
      strcpy(_fontsizecmd,"\\scriptsize");
      switch(_basesize) {
      case 10:
      default:
         newsize(6);
         break;
      case 11:
      case 12:
         newsize(7);
         break;
      }
      break;
   case Footnotesize:
      strcpy(_fontsizecmd,"\\footnotesize");
      switch(_basesize) {
      case 10:
      default:
         newsize(7);
         break;
      case 11:
         newsize(8);
         break;
      case 12:
         newsize(10);
         break;
      }
      break;
   case Small:
      strcpy(_fontsizecmd,"\\small");
      switch(_basesize) {
      case 10:
      default:
	 newsize(8);
	 break;
      case 11:
	 newsize(10);
	 break;
      case 12:
	 newsize(11);
	 break;
      }
      break;
   case Normalsize:
      strcpy(_fontsizecmd,"\\normalsize");
      newsize(_basesize);
      break;
   case large:
      strcpy(_fontsizecmd,"\\large");
      switch(_basesize) {
      case 10:
      default:
	 newsize(11);
	 break;
      case 11:
	 newsize(12);
	 break;
      case 12:
	 newsize(13);
	 break;
      }
      break;
   case Large:
      strcpy(_fontsizecmd,"\\Large");
      switch(_basesize) {
      case 10: 
      case 11:
      default:
	 newsize(13);
	 break;
      case 12:
	 newsize(15);
	 break;
      }
      break;
   case LARGE:
      strcpy(_fontsizecmd,"\\LARGE");
      switch(_basesize) {
      case 10: 
      case 11:
      default:
	 newsize(15);
	 break;
      case 12:
	 newsize(18);
	 break;
      }
      break;
   case huge:
      strcpy(_fontsizecmd,"\\huge");
      switch(_basesize) {
      case 10:
      case 11:
      default:
	 newsize(18);
	 break;
      case 12:
	 newsize(22);
	 break;
      }
      break;
   case Huge:
      strcpy(_fontsizecmd,"\\Huge");
      newsize(22);
      break;
   case Currentused:
      _currentused = do_command(textstr, textstr,
				Global::files->whitespace_next(), 0);
      break;
   case Pending:
      if(_pending) {
	 postscript_set(0);
	 _pending = 0;
      }
      break;
   case Size:
      set_fontstyle("");
      newsize(value);
      break;
      /* These font styles aren't handled right now */
   case Used:
      _fonts_used[_currentused]->used(textstr);
      break; 
   case FunnyPrint:
      Global::files->outfile << endl << "[ 0";
      if(Global::files->whitespace_next())
	 Global::files->outfile << " false";
      else
	 Global::files->outfile << " true";
      Global::files->outfile << " ] NewFont " << textstr << " NW " << endl;
      _pending = 1;    // Print the postscript command for this.
      _pending_whitespace = Global::files->whitespace_next();
//      postscript_set(0);
      break;
   default:
      break;
   }
   return TRUE;
}

float Font::get(int subtype, char *comparestr)
{ 
   switch(subtype) {
   case Base:
      return(_basesize);
   case Currentused:
      return(_currentused);
   case Pending:
      return(_pending);
   case Style:
      return(!strcmp(comparestr,_fontstyle));
   case ShutDown:
      if(!Global::files->plain_text_output)
         shutdown();
      break;
   case Size:
   default:
      break;
   }
   return(_fontsize);
}

void Font::revert(Param *from)
{
   int size = (int)from->get(Size,"");

   if(size != _fontsize || !from->get(Style,_fontstyle)
      || from->get(Pending,"") || _currentused != from->get(Currentused,""))
      _pending = 1;    // Print the postscript command for this.
      _pending_whitespace = Global::files->whitespace_next();
}

void Font::use_command(char *style, char *petname, int font)
{
   _fonts_used[_num_fonts_used++] = new FontUsed(style,petname, font);
}

int Font::do_command(char *style, char *petname, int whitespace, int font)
{
   for(int used=0; used < _num_fonts_used; used++)
      if(_fonts_used[used]->match(petname))
	 break;
   if(_num_fonts_used >= MAXFONTS)
      Global::files->fatal_error("Too many fonts");
   if(used >= _num_fonts_used)
      use_command(style, petname, font);

   Global::files->outfile << endl << "[ " << used+1;

   if(whitespace)
      Global::files->outfile << " false";
   else
      Global::files->outfile << " true";

   Global::files->outfile << " ] NewFont    % " << petname << endl;
   return(used);
}

void Font::postscript_set(int)
{
   /* The Font Family is hard-coded to "cm" for Computer Modern */
   char petname[MAXSTRING];

   sprintf(petname,"/cm%d%s",_fontsize,_fontstyle);
   char style[MAXSTRING];
   sprintf(style,"%s\\%s",_fontsizecmd,_fontstyle);
   _currentused = do_command(style, petname, _pending_whitespace, 1);
}

// This is an important routine which builds a dummy latex files
// Just to trick LaTeX into giving us the proper fonts.
void Font::shutdown()
{
   cerr << "Making dummy file for snarfing LaTeX fonts..." << endl;
   ofstream dummy("lametex.tex");
   dummy << "\\documentstyle[" << _basesize << "pt]{report}" << endl;
   dummy << "\\pagestyle{empty}" << endl;
   dummy << "\\begin{document}" << endl;
   for(int x=0; x < _num_fonts_used; x++)
      _fonts_used[x]->dump(dummy);
   dummy << "\\end{document}" << endl;
   dummy.close();
   cerr << "Snarfing LaTeX fonts..." << endl;
   system( LATEX );  
   system( DVIPS ); 

   ifstream latex_postscript("lametex.ps");
   if(!latex_postscript) {           // Open file failed?
      cerr << "Unable to open postscript temp file lametex.ps for reading"
	   << endl;
      exit (-1);
   }

   ofstream lametex_postscript("lametex.PS");
   if(!lametex_postscript) {           // Open file failed?
      cerr << "Unable to open postscript temp file lametex.PS for writing"
	   << endl;
      exit (-1);
   }

   char line[MAXSTRING];
   while(!latex_postscript.eof()) {
      latex_postscript.getline(line, MAXSTRING, '\n');
      if(strstr(line,"TeXDict begin")==line)
	 break;
   }

   lametex_postscript << line << endl;
   while(!latex_postscript.eof()) {
      latex_postscript.getline(line, MAXSTRING, '\n');
      lametex_postscript << line << endl;
      if(strstr(line,"%%EndSetup"))
	 break;
   }

   int y;
   for(x=_num_fonts_used-1, y=0; x >= 0 ; x--)
      if(_fonts_used[x]->font())
	 lametex_postscript << _fonts_used[x]->petname()
			    << "{ pop F" << (char) ('a' + y++)
			    << " } bind def" << endl;

   lametex_postscript << "/fontnames [" << endl;
   lametex_postscript << "/TIMESROMAN" << endl;

   for(x=0; x < _num_fonts_used; x++)
      lametex_postscript << _fonts_used[x]->petname() << endl;
   lametex_postscript << "] def" << endl;
      
   latex_postscript.close();
   lametex_postscript.close();
   system("rm lametex.tex lametex.dvi lametex.ps");
   cerr << "  ** SNARF! **" << endl;
}