/******************************************************************************
 * program:     wp2latex                                                      *
 * function:    convert WordPerfect files into LaTeX                          *
 * modul:       pass1_1.cc                                                    *
 * description: This is conversion module for parsing WP 1.x binary files.    *
 * licency:     GPL		                                              *
 ******************************************************************************/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#include "sets.h"
#include "stringa.h"
#include "lists.h"
#include "struct.h"

#include "wp2latex.h"
#include "cp_lib/cptran.h"


int set0_10[]={0 ___ 0xFF};		///< Everything
int set1_10[]={0 ___ 0xFF};		///< Header, Footer, Minipage Text
int set2_10[]={0x20 ___ 0x7F,0xE1};	///< Characters Only

//0 - object must be handled specialy, size is not defined
extern BYTE ObjWP1SizesC0[];


class TconvertedPass1_WP1: public TconvertedPass1
     {
public:char	UnderlineType;
     char SubSup;
     SBYTE DefColumns;
     char LogLevel;

     virtual int Convert_first_pass(void);
     
protected:
     set filter[4];

     void InitFilter1(void);

     void ProcessKey1(void);
     void Character1(void);
     bool CheckConzistency1(long NewPos);
     void FootEndNote1(DWORD & NewPos, WORD & w);
     };


#ifndef FINAL
void CrackObject(TconvertedPass1 *cq, DWORD end_of_code);
#endif

/*Register translators here*/
TconvertedPass1 *Factory_WP1(void) {return new TconvertedPass1_WP1;}
FFormatTranslator FormatWP1("WP1.x",Factory_WP1);


void TconvertedPass1_WP1::Character1(void)
{
#ifdef DEBUG
  fprintf(log,"\n#Character1() ");fflush(log);
#endif

  fread(&subby, 1, 1, wpd);

  CharacterStr(this,Ext_chr_str(subby,this,ConvertCpg));

  sprintf(ObjType, ":%u",(unsigned)subby);
}


void TconvertedPass1_WP1::FootEndNote1(DWORD & NewPos, WORD & w)
{
#ifdef DEBUG
  fprintf(log,"\n#FootEndNote1() ");fflush(log);
#endif
  unsigned char OldFlag;
  DWORD unknown;
  attribute OldAttr;
  unsigned char NoteType;

  OldFlag = flag;
  OldAttr = attr;
  flag = HeaderText;
  recursion++;
  attr.InitAttr();		//Turn all attributes in the note off

  NewPos = ftell(wpd);
  RdDWORD_HiEnd(&unknown, wpd);
  fread(&NoteType, 1, 1, wpd);
  if(NoteType & 2){
		  if(!EndNotes) EndNotes=true;		/* set up endnotes */
		  if(EndNotes<0) goto FootNote;		/* endnotes are disabled */
		  fprintf(strip, "\\endnote{");
		  }
	    else {
FootNote:	 fprintf(strip, "\\footnote{");
		 }

  while (!feof(wpd))	// Skip everything to the mark 0x8D
	{
	if(fread(&by, 1, 1, wpd)!=1) goto EmptyNote;
	if(by==0xE2) goto EmptyNote;
	if(by==0x8D) break;	// real start of the note text
	}

  ActualPos = ftell(wpd);
  while (!feof(wpd))
       {
       if(fread(&by, 1, 1, wpd)!=1) break;

       if(by==0xE2) break;
       if(by==0) {ActualPos++;continue;}

       ProcessKey1();
       }

EmptyNote:
  ActualPos = ftell(wpd);
  w = ActualPos-NewPos+1;
  NewPos = ActualPos;

  fputc('}',strip);

  recursion--;
  strcpy(ObjType, NoteType==2?"Endnote":"Footnote");
  attr = OldAttr;
  flag = OldFlag;
  char_on_line = true;
}


bool TconvertedPass1_WP1::CheckConzistency1(long NewPos)
{
#ifdef DEBUG
  fprintf(log,"\n#CheckConzistency1(%ld) ",NewPos);fflush(log);
#endif

  bool Result = true;
  unsigned char TestedBy;
  long Pos;

  Pos = ftell(wpd);

  fseek(wpd, NewPos-1 , 0);
  fread(&TestedBy, 1, 1, wpd);
  if (TestedBy != by)
  	{
        if (err != NULL)
	  {
	  perc.Hide();
          fprintf(err,
	      _("\nError: Object %lX:%X consistency check failed. Trying to ignore."),Pos,(int)by);
          }
        CorruptedObjects++;
        Result = false;
	/* asm int 3; end;*/
	}

  fseek(wpd, Pos, 0);
  return Result;
}


/** This is main procedure for processing one key. It is recursivelly called. */
void TconvertedPass1_WP1::ProcessKey1(void)
{
#ifdef DEBUG
  fprintf(log,"\n#ProcessKey1() ");fflush(log);
#endif
  WORD w;
  DWORD NewPos = 0;
  unsigned char by, subby;

  if (this->by == 0)
    fread(&this->by, 1, 1, wpd);

  *ObjType = '\0';
  w = 1;
  this->subby=0;

  /*Guessing end position of the object*/

  if(this->by >= 0xC0)
      {
      w=ObjWP1SizesC0[this->by-0xC0];
      if(w>0) NewPos = ActualPos + w;
      }
      
  by = this->by;
  subby = this->subby;

  if(ExtendedCheck && NewPos != 0)
    if(!CheckConzistency1(NewPos))
      {
      NewPos = 0;
      strcpy(ObjType, "Corrupted!!");
      goto _LObjectError;
      }

  if(filter[flag][this->by])
     {
     switch(by)
	{
	case 0x02:PageNumber(this);	break;          /* Page number */

	case 0x09:strcpy(ObjType, "!Tab");
		  putc(' ', strip);   break;	/*tabulator (ignored)*/
	case 0x0a:HardReturn(this);break;			// Hard return
	case 0x0b:Terminate_Line(this,'p');strcpy(ObjType, "SRt SoftPg");break;/* Soft page break after a SRt */
	case 0x0c:strcpy(ObjType, "HPg");Terminate_Line(this,'P');break;	// Hard page
	case 0x0d:SoftReturn(this);break;			/* Soft return */

	case 0x20:putc(' ', strip);   break;	/* Soft space ' '*/


	case 0x80:strcpy(ObjType, "NOP");		break;	/* NOP */
	case 0x81:Justification(this, 1 | 0x80);		break;	/* Full justification */
	case 0x82:Justification(this, 0 | 0x80);		break;	/* Left justification - Ragged right */
	case 0x83:if(envir=='c')				/* End centering */
			 Justification(this, 1 + 0x80);
		  strcpy(ObjType, "Center End");	break;
//	case 0x84: leave_environment(0); break;         	/* End aligned text */
//	case 0x85: process0(MathCalc); break;            	/* Begin math calc */
	case 0x86:CenterPage(this);			break;	/* Center page vertically */
	case 0x87:Column(this,DefColumns);  		break;	/* Begin column mode */
	case 0x88:Column(this,1);				break;	/* End column mode */
//	case 0x89: process0(Tab);	 break;			/* Tab after right margin */
	case 0x8A:WidowOrphan(this,3);			break;	/* Widow/orphan protection */
	case 0x8B:WidowOrphan(this,0);			break;	/* Allow widows/orphans */
	case 0x8C:if(char_on_line>=0) Terminate_Line(this,'h'); /* Soft page break after a HRt */
				     else Terminate_Line(this,'s');     // hard return mustn't occur here, fix it
		  strcpy(ObjType, "HRt-SPg");	break;
//      case 0x8D:process0(note_status == eFn ? FNoteNum : ENoteNum);/* Footnote/Endnote number */
	case 0x90:Attr_ON(this,10);			break;	/* Begin redline */
	case 0x91:Attr_OFF(this,10);			break;	/* End redline */
	case 0x92:Attr_ON(this,13);			break;	/* Begin strikeout */
	case 0x93:Attr_OFF(this,13);			break;	/* End strikeout */
	case 0x94:if(UnderlineType & 1) Attr_ON(this,11);/*Dbl   Begin underlining */
				       else Attr_ON(this,14);/*Normal*/
		  strcpy(ObjType, "Underline");	break;
	case 0x95:AttrOff(this,14);				/* End underlining */
		  AttrOff(this,11);
		  strcpy(ObjType, "underline");	break;
	case 0x96:Attr_ON(this,17);			break;	/* Begin reverse video */
	case 0x97:Attr_OFF(this,17);			break;	/* End reverse video */
//	case 0x98: 	 break;					/* Table of contents placeholder */
//	case 0x99:Overstrike4(this,NewPos,w);		break;	/* Overstrike */
	case 0x9A:CancelHyph(this);			break;
	case 0x9B:strcpy(ObjType,"!End Gener Text");break;	/* End of generated text */
	case 0x9C:Attr_OFF(this,12);			break;	/* End boldface */
	case 0x9D:Attr_ON(this,12);			break;	/* Begin boldface */
	case 0x9E:Hyphenation(this, false);		break;	/* Forbid hyphenation */
	case 0x9F:Hyphenation(this, true);		break;	/* Allow hyphenation */
	case 0xA0:fputc('~', strip);strcpy(ObjType, " ");
		  break;		    			/* Hard space */
//	case 0xA1: process0(SubTtl); break;                   /* Do subtotal */
//	case 0xA2: process0(IsSubTtl); break;              /* Subtotal entry */
//	case 0xA3: process0(Ttl); break;                         /* Do total */
//	case 0xA4: process0(IsTtl); break;                    /* Total entry */
//	case 0xA5: process0(GrandTtl); break;              /* Do grand total */
//	case 0xA6: process0(MathCalcColumn); break;      /* Math calc column */
//	case 0xA7: process0(Math); break;                 /* Begin math mode */
//	case 0xA8: process0(eMath); break;                  /* End math mode */
	case 0xA9:                     				/* Normal breakable hyphen */
	case 0xAA:                              		/* Hyphen at end of line */
	case 0xAB:HardHyphen(this);break;			 	/* Hyphen at end of page */
	case 0xAC:SoftHyphen(this);			break;	/* Discretionary hyphen */
	case 0xAD:SoftHyphen(this);			break;	/* Discretionary hyphen at EOLn */
	case 0xAE:SoftHyphen(this);			break;  /* Discretionary hyphen at EOPg */
	case 0xAF:Column(this,false);			break;	/* EOT columns and EOLn */
	case 0xB0:Column(this,false);			break;	/* EOT columns and EOPg */
//	case 0xB1: process0(NegateTotal); break;     /* Negate current total */
	case 0xB2:Attr_ON(this,8);			break;	/* Italic On */
	case 0xB3:Attr_OFF(this,8);			break;	/* Italic Off */
	case 0xB4:Attr_ON(this,9);			break;	/* Shadow On */
	case 0xB5:Attr_OFF(this,9);			break;	/* Shadow Off */
	case 0xB6:Attr_ON(this,7);			break;	/* Outline On */
	case 0xB7:Attr_OFF(this,7);			break;  /* Outline Off */

	case 0xBC:AttrOn(attr,5);SubSup|=0x82;		/* Superscript */
		  strcpy(ObjType, "SupScript");
		  break;
	case 0xBD:AttrOn(attr,6);SubSup|=0x81;		/* Subscript */
		  strcpy(ObjType, "SubScript");
		  break;
//	case 0xBE:LineUpDown(this, 0.5);			break;	/* Advance 1/2 line up */
//	case 0xBF:LineUpDown(this, -0.5);			break;	/* Advance 1/2 line down */

	case 0xC0:strcpy(ObjType, "!WP1_MARGIN_RESET_GROUP"); goto GROUP_Cx;
	case 0xC1:strcpy(ObjType, "!WP1_SPACING_RESET_GROUP"); goto GROUP_Cx;
	case 0xC2:strcpy(ObjType, "!WP1_MARGIN_RELEASE_GROUP"); goto GROUP_Cx;
	case 0xC3:strcpy(ObjType, "!WP1_CENTER_TEXT_GROUP"); goto GROUP_Cx;
	case 0xC4:strcpy(ObjType, "!WP1_FLUSH_RIGHT_GROUP"); goto GROUP_Cx;

	case 0xC9:strcpy(ObjType, "!WP1_SET_TABS_GROUP"); goto GROUP_Cx;

	case 0xCC:strcpy(ObjType, "!WP1_LEFT_INDENT_GROUP"); goto GROUP_Cx;

	case 0xCE:strcpy(ObjType, "!WP1_TOP_MARGIN_SET_GROUP"); goto GROUP_Cx;
	case 0xCF:strcpy(ObjType, "!WP1_SUPPRESS_PAGE_CHARACTERISTICS_GROUP"); goto GROUP_Cx;
	case 0xD0:strcpy(ObjType, "!WP1_BOTTOM_MARGIN_SET_GROUP"); goto GROUP_Cx;
	case 0xD1:strcpy(ObjType, "!WP1_HEADER_FOOTER_GROUP"); goto GROUP_Cx;

	case 0xE0:strcpy(ObjType, "!WP1_LEFT_RIGHT_INDENT_GROUP"); goto GROUP_Cx;
	case 0xE1:Character1();	break;		/* IBM character WP1_EXTENDED_CHARACTER_GROUP */
	case 0xE2:FootEndNote1(NewPos, w);		break;	/* footnote or endnote */

	case 0xF4:strcpy(ObjType, "!WP1_POINT_SIZE_GROUP"); goto GROUP_Cx;

	case 0xFB:Justification(this, 1); 	break;

	default:if(by>=0x01 && by<=0x7f)
		     {
		     RequiredFont = FONT_NORMAL;
		     CharacterStr(this,Ext_chr_str(this->by,this,NULL));
                     if(attr.Closed_Depth>0)
			{
			if(SubSup)
			  {
			  AttrOff(this,5);
			  AttrOff(this,6);
			  }
			SubSup=0;
			}
		     }
		else if(by>=0xC0)
                  {
GROUP_Cx:         if(NewPos==0)
		    {	//move to the second delimiter, variable size items
	       	    while(!feof(wpd))
		      {
		      by=fgetc(wpd);
		      if(by==this->by) break;
		      }
		    }
	          }
		break;
	}
  }

_LObjectError:
  if(log != NULL && LogLevel>=1)
    {   /**/
    if((by >= 0x80)||(*ObjType != '\0'))
        {
	fprintf(log, _("\n%*sObject type:%3Xh subtype:%3d length:%4u"),
	                    recursion * 2, "", by, subby, w);
	if (*ObjType != '\0')
	          fprintf(log, " [%s] ", ObjType);
             else fprintf(log, "    ");
        if(*ObjType==0) UnknownObjects++;

        fflush(log);
        }
    else if (by >= ' ' && by <= 'z')
	         putc(by, log);
    }


  if (NewPos == 0)
	{
	if(by<0xC0) ActualPos++;	//Only one byte read - simple guess of new position
	       else ActualPos = ftell(wpd);
	return;
	}
  ActualPos = ftell(wpd);
  if (NewPos == ActualPos) return;
  fseek(wpd, NewPos, SEEK_SET);
  ActualPos = NewPos;
  NewPos = 0;  
}


void TconvertedPass1_WP1::InitFilter1(void)
{
 filter[0] = set(set0_10,sizeof(set0_10)/sizeof(int));
 filter[1] = set(set1_10,sizeof(set1_10)/sizeof(int));
 filter[2] = set(set2_10,sizeof(set2_10)/sizeof(int));
 filter[3] = set();
}


/***************************************/
/* This procedure provides all needed processing for the first pass*/
int TconvertedPass1_WP1::Convert_first_pass(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_WP1::Convert_first_pass() ");fflush(log);
#endif
DWORD fsize;

  LogLevel=1;
  InitFilter1();

  ConvertCpg = GetTranslator("wp4aTOinternal");
  DefColumns=2;

  UnderlineType = 0;
  SubSup = 0;

  DocumentStart=ftell(wpd);

  fsize=filesize(wpd);
  perc.Init(ftell(wpd), fsize,_("First pass WP 1.x:"));

  ActualPos = ftell(wpd);
  while (ActualPos < fsize)
      {
      if(Verbosing >= 1)		//actualise a procentage counter
	      perc.Actualise(ActualPos);

      by = 0;
      ProcessKey1();
      }

  Finalise_Conversion(this);
  return(1);
}

/*--------------------End of PASS1_WP1------------------*/

