/******************************************************************************
 * program:     wp2latex                                                      *
 * function:    module for conversion AbiWord files into LaTeX 		      *
 * modul:       pass1abi.cc                                                   *
 * description: This module contains parser for AbiWord documents. It could   *
 *		be optionally compiled with WP2LaTeX package.		      *
 * licency:     GPL		                                              *
 ******************************************************************************/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#include<stringa.h>
#include<lists.h>
#include<dbllist.h>

#include"wp2latex.h"
#include"pass1xml.h"
#include "cp_lib/cptran.h"
#include"images/image.h"


int SavePictureEPS(const char *Name,const Image &Img); //froom ras_img.cc


#define AbiwordVersion "0.11"


/*Register translators here*/
class TconvertedPass1_Abi: public TconvertedPass1_XML
{
public:
     virtual int Convert_first_pass(void);

     void CharPropAbi(void);
     void FieldAbi(void);
     void FrameAbi(void);
     void ImageAbi(void);
     void ParPropAbi(void);
     void ProcessKeyABI(void);
     void ProcessDataAbi(void);
     void SkipMetaAbi(void);
     void TableABI(void);
};

TconvertedPass1 *Factory_Abi(void) {return new TconvertedPass1_Abi;}
FFormatTranslator FormatAbiword("abiword",Factory_Abi);


enum AbiKeywords
{
  ABI_HRT =   131,
  ABI_PAR =   132,
  ABI_FIELD = 133,
  ABI_CHAR =  134,
  ABI_IMAGE = 135,
  ABI_SECTION = 136,
  ABI_PBR =   137,
  ABI_DATA =  138,	//"D"
  ABI_TABLE = 139,	// CELL, TABLE
  ABI_FRAME,
  ABI_META,
  ABI_SECTMETA
};



/** Break property string into double list property, value. */
static void ParseProps(char *PropStr, doublelist & d)
{
char *BegProp,*EndProp,*BgPropVal,*EndPropVal;
char c1,c2;

 erase(d);
 if(PropStr==NULL) return;
 while(isspace(*PropStr) || *PropStr=='\"') PropStr++;
 while(*PropStr!=0)
   {
   EndProp=BegProp=PropStr;
   while(*EndProp!=0 && *EndProp!=':') EndProp++;
   EndPropVal=BgPropVal=EndProp;
   if(*BgPropVal==':')
	{
	do
	  {BgPropVal++;}
	while(isspace(*BgPropVal));
	EndPropVal=BgPropVal;
	while(*EndPropVal!=0 && *EndPropVal!=';') EndPropVal++;
	}
   while(EndPropVal>BgPropVal && isspace(*(EndPropVal-1)) ) EndPropVal--;

   c1=*EndProp;		c2=*EndPropVal;
   *EndProp=0;		*EndPropVal=0;
   d.Add(BegProp,BgPropVal);
   *EndProp=c1;		*EndPropVal=c2;

   PropStr=EndPropVal;
   while(isspace(*PropStr) || *PropStr==';') PropStr++;
   }
}


void TconvertedPass1_Abi::ParPropAbi(void)
{
#ifdef DEBUG
  fprintf(log,"\n#ParPropAbi() ");fflush(log);
#endif
int i;
char *Props=NULL;
doublelist d;

  if((i=TAG_Args IN "props")>=0)
     {
     Props = TAG_Args.Member(i,1);
     ParseProps(Props,d);
     if((i=d IN "text-align")>=0)
	{			    /*0x80-Left, 0x83-Right, 0x82-Center, 0x81-Full */
	Props=d.Member(i,1);
	if(!StrCmp(Props,"center")) Justification(this, 0x82);
	if(!StrCmp(Props,"right"))  Justification(this, 0x83);
	if(!StrCmp(Props,"left"))   Justification(this, 0x80);
	if(!StrCmp(Props,"justify"))Justification(this, 0x81);
	}
     }
  if(char_on_line) HardReturn(this);  //Paragraph on
  SoftReturn(this);

return;
}


void TconvertedPass1_Abi::CharPropAbi(void)
{
#ifdef DEBUG
  fprintf(log,"\n#CharPropAbi() ");fflush(log);
#endif
int i;
char *Props=NULL;
string s;
doublelist d;
attribute NewAttr;

  //InitAttr(NewAttr);
  if((i=TAG_Args IN "props")>=0)
     {
     Props=TAG_Args.Member(i,1);
     ParseProps(Props,d);

     if((i=d IN "color")>=0)
	{
/*	RGBQuad RGB;
	Props=d.Member(i,1);
	if(Props)
	   {
	   sscanf(Props,"%2X%2X%2X",&RGB.R, &RGB.G, &RGB.B);
	   Color(cq,0,&RGB);
	   }*/
	}
     if((i=d IN "font-style")>=0)
	{
	Props=d.Member(i,1);
	if(!StrCmp(Props,"italic")) AttrOn(NewAttr,8);
	}
     if((i=d IN "font-weight")>=0)
	{
	Props=d.Member(i,1);
	if(!StrCmp(Props,"bold")) AttrOn(NewAttr,12);
	}
     if((i=d IN "text-decoration")>=0)
	{
	Props=d.Member(i,1);
	if(!StrCmp(Props,"underline")) AttrOn(NewAttr,14);
        if(!StrCmp(Props,"line-through")) AttrOn(NewAttr,13);
	}
     }

  AttrFit(attr,NewAttr,s);
  if(!s.isEmpty()) fputs(s(),strip);

return;
}


void TconvertedPass1_Abi::FieldAbi(void)
{
#ifdef DEBUG
  fprintf(log,"\n#FieldAbi() ");fflush(log);
#endif
int i;
const char *Type;

  if((i=TAG_Args IN "type")>=0)
     {
     Type=TAG_Args.Member(i,1);
     if(Type)
	if(!strcmp(Type,"page_number")) PageNumber(this);
     }
}


void TconvertedPass1_Abi::FrameAbi(void)
{
#ifdef DEBUG
  fprintf(log,"\n#FrameAbi() ");fflush(log);
#endif
TBox Box;
int i;
const char *FileName = NULL;
char *Props;
doublelist d;

 if((i=TAG_Args IN "strux-image-dataid")>=0)
 {
   initBox(Box);   

   FileName = TAG_Args.Member(i,1);
   if(FileName==NULL) FileName="dummy";
   if((i=TAG_Args IN "props")>=0)
     {
     Props = TAG_Args.Member(i,1);
     ParseProps(Props,d);

     if((i=d IN "frame-width")>=0)
	{
	const char *w_props = d.Member(i,1);
	double width = atof(w_props);
	// printf(" \n width = %f %s \n", width, w_props);

	if(width>0)
	  {
	  if(strstr(w_props,"mm"))	{Box.Width = width/10;}
	  else if(strstr(w_props,"cm")) {Box.Width = width;}
	  else if(strstr(w_props,"in")) {Box.Width = 2.54*width;} //inch
          }
	}
     }

   ImageWP(this, FileName, Box);
 }
return;
}


void TconvertedPass1_Abi::ImageAbi(void)
{
#ifdef DEBUG
  fprintf(log,"\n#ImageAbi() ");fflush(log);
#endif
TBox Box;
int i;
const char *FileName=NULL;
char *Props;
doublelist d;

  initBox(Box);
  
  if((i=TAG_Args IN "dataid")>=0)
	FileName = TAG_Args.Member(i,1);
  if((i=TAG_Args IN "props")>=0)
     {
     Props = TAG_Args.Member(i,1);
     ParseProps(Props,d);
     if((i=d IN "width")>=0)
	{
	const char *w_props = d.Member(i,1);
	double width = atof(w_props);

        if(width>0)
	  {
	  if(strstr(w_props,"mm"))	{Box.Width = width/10;}
	  else if(strstr(w_props,"cm")) {Box.Width = width;}
	  else if(strstr(w_props,"in")) {Box.Width = 2.54*width;} //inch
          }	
        }
     }
  if(FileName==NULL) FileName="dummy";

  ImageWP(this, FileName, Box);
return;
}


static void SectionAbi(TconvertedPass1_XML *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#SectionAbi() ");fflush(cq->log);
#endif
int i;
char *Props=NULL;
doublelist d;

  if((i=cq->TAG_Args IN "props")>=0)
     {
     Props=cq->TAG_Args.Member(i,1);
     ParseProps(Props,d);
     if((i=d IN "columns")>=0)
	{
	i=atoi(d.Member(i,1));
	if(i<=0) i=1;
	Column(cq,i);
	}
     }
  if(cq->char_on_line) HardReturn(cq);  //Paragraph on
  SoftReturn(cq);

return;
}


void TconvertedPass1_Abi::ProcessDataAbi(void)
{
#ifdef DEBUG
  fprintf(log,"\n#ProcessDataAbi() ");fflush(log);
#endif
unsigned char OldFlag;
FILE *f;
uint32_t Data=0;
char data64;
char cnt;
string filename;

 OldFlag = flag;
 flag = Nothing;
 
 if((cnt=TAG_Args IN "name")>=0)
   {   
   filename = TAG_Args.Member(cnt,1);
   }
 if(length(filename)==0)
   filename=GetSomeImgName(".png"); //invent some filename if empty

 filename = MergePaths(OutputDir,RelativeFigDir) + CutFileName(filename) + ".png";

 f = OpenWrChk(filename(),"wb",err);

 cnt=0;
 do {
    ReadXMLTag();
    if(this->by==0)
      {
      subby = TAG[0];
      if(subby==0 || subby=='\n' || subby=='\r' || subby=='\t')
 	 continue;

      if(subby>='A' && subby<='Z')
        data64 = subby - 'A';
      else if(subby>='a' && subby<='z')
        data64 = subby - 'a' + 26;
      else if(subby>='0' && subby<='9')
        data64 = subby - '0' + 2*26;
      else if(subby=='+') data64 = 62;
      else if(subby=='/') data64 = 63;
      else
        {
        if(err != NULL)
 	  {
 	  perc.Hide();
	  fprintf(err, _("\nWarning: Invalid character '%c' (%d) inside base64 data will be ignored!"),this->subby,this->subby);
	  }
        data64 = 0;
        continue;
        }

      switch(cnt)
        {
        case 0: Data = (uint32_t)data64<<18; cnt=1; break;
        case 1: Data |= (uint32_t)data64<<12;cnt=2; break;
        case 2: Data |= (uint32_t)data64<<6; cnt=3; break;
        case 3: Data |= (uint32_t)data64;
	        cnt=0;
                if(f) {
	 	      fputc(Data>>16,f);
		      fputc((Data&0xFF00)>>8,f);
		      fputc(Data&0xFF,f);
		      }
	         break;
         }
     }
    else
      {
      ProcessKeyABI();
      if(this->by==ABI_DATA && this->subby==1) break;
      }
    } while(!feof(wpd));

 if(f)
    {
    switch(cnt)
      {
      case 0: fputc(Data>>16,f); break;
      case 1: fputc(Data>>16,f); fputc((Data&0xFF00)>>8,f); break;
      case 2: fputc(Data>>16,f); fputc((Data&0xFF00)>>8,f); fputc(Data&0xFF,f); break;
//    case 3: break; //all done
      }

    fclose(f); f=NULL;

    Image Img;
    Img = LoadPicture(filename());
    if(!Img.isEmpty())
      {
      filename = MergePaths(OutputDir,RelativeFigDir)+CutFileName(filename)+".eps";
      if(SavePictureEPS(filename(),Img)<0)
        {
        if(err != NULL)
	  {
	  perc.Hide();
	  fprintf(err, _("\nError: Cannot save file: \"%s\"!"),filename());
	  }
        }
      }      
    }

  flag = OldFlag;
}


void TconvertedPass1_Abi::SkipMetaAbi(void)
{
#ifdef DEBUG
  fprintf(log,"\n#SkipMetaAbi() ");fflush(log);
#endif
unsigned char OldFlag;

 OldFlag = flag;
 flag = Nothing;

 do {
    TAG.erase();
    ProcessKeyABI();
    if(by==ABI_META && subby==1) break;
    if(by==ABI_SECTMETA && subby==1) break;
    } while(!feof(wpd));

 flag = OldFlag;
}


void TconvertedPass1_Abi::TableABI(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TableABI() ");fflush(log);
#endif
unsigned char OldFlag;
char OldEnvir;
long FilePos;
int i, FieldCount=0;
char *Prop;
char *FieldPos=NULL;	///< @TODO FieldPos is not allocated!
char Alignment;
doublelist PrpL;

FilePos = ftell(wpd);
OldFlag = flag;
OldEnvir = envir;
flag = Nothing;
recursion++;

do {
   TAG.erase();
   ProcessKeyABI();
   if(by==ABI_TABLE)
     {
     if(subby == 0) break;			// <TABLE>
     if(subby == 1) break;			// </TABLE>
     if(subby == 2)				// <CELL>
       {
       if((i=TAG_Args IN "props")>=0)
          {
          ParseProps(TAG_Args.Member(i,1),PrpL);
          if((i=PrpL IN "top-attach")>=0)
	    {
	    Prop=PrpL.Member(i,1);
	    if(Prop)
	      if(strcmp(Prop,"0")) break;
	    }
          }
       }
     if(subby == 3) 			// </CELL>
	{
	FieldCount++;
	}
     }
   }while(!feof(wpd));


  if(FieldCount>0)
    {
    line_term = 's';   /* Soft return */
    if(char_on_line == LEAVE_ONE_EMPTY_LINE)    /* Left one enpty line for new enviroment */
	{
	fputc('%', table); fputc('%', strip);
	NewLine(this);
	}
    if(char_on_line>=CHAR_PRESENT) //  if(cq->char_on_line>=-1)
	{
	NewLine(this);
	}
    envir = '!';
    fputc('%', table); fputc('%', strip);
    NewLine(this);

    envir = 'B';

    fprintf(strip, "{|");
    for(i=0; i<FieldCount; i++)
	{
	Alignment='l';
	if(FieldCount<100 && FieldPos!=NULL) Alignment=FieldPos[i];
	fprintf(strip, "%c|",Alignment);
	}
    putc('}', strip);

    char_on_line = false;
    nomore_valid_tabs = false;
    rownum++;
    Make_tableentry_attr(this);
    latex_tabpos = 0;
    }

	/*Process all content of the table */
  flag = OldFlag;
  fseek(wpd,FilePos,SEEK_SET);
  ReadXMLTag();
  while(!feof(wpd))
	{
	if((by==0 && (subby==10 || subby==13)))
	  {
	  by = 0;
	  subby = ' ';	/* remove \n from cell text */
	  }
	ProcessKeyABI();
	if(by==ABI_TABLE)
           switch(subby)
		   {
		   case 0:TablePos=1; goto FinishTable; //nested tables are not converted
		   case 1:EndTable(this);
			  TablePos=0;
			  goto FinishTable;
		   case 2:if((i=TAG_Args IN "props")>=0)
                            {
                            ParseProps(TAG_Args.Member(i,1),PrpL);
                            if((i=PrpL IN "left-attach")>=0)
	                      {
	                      Prop=PrpL.Member(i,1);
			      if(Prop)
			       if(!strcmp(Prop,"0"))
				 {
				 RowTable(this); TablePos|=2;
				 break;
				 }
			      else
				 {CellTable(this);TablePos|=4;break;}
			      }
		            }
			  break;
		   }

	ReadXMLTag();
	}

FinishTable:
  if(char_on_line <= FIRST_CHAR_MINIPAGE)  /* Left one enpty line for ending enviroment */
	{
	fputc('%', table);fputc('%', strip);
	NewLine(this);
	}
  envir = '^';		//Ignore enviroments after table
  fputc('%', table);
  NewLine(this);
  char_on_line = FIRST_CHAR_MINIPAGE;	// stronger false;

  recursion--;

  flag=OldFlag;
  envir=OldEnvir;
  TAG="TABLE";
strcpy(ObjType, "Table Start");
}


void TconvertedPass1_Abi::ProcessKeyABI(void)
{
#ifdef DEBUG
  fprintf(log,"\n#ProcessKeyABI() ");fflush(log);
#endif
string TAG;
const char *tag = NULL;
uint8_t by, subby;

 *ObjType = 0;
 if(TAG.isEmpty()) ReadXMLTag();
 by = this->by;
 subby = this->subby;

 switch(by)
     {
     case XML_char:
             switch(this->subby)                        //Normal character
	       {
	       case 10:
	       case 13:by=128;break; //CR
	       case  9:strcpy(ObjType, "!Tab");
	       case 32:by=32;break;  //Space
	       }
             break;
	case XML_extchar:
             if(TAG.isEmpty()) break;			//Extended chatacter &xxx;
	       TAG = Ext_chr_str(TAG[0],this) + copy(TAG,1,length(TAG)-1);
	       tag=TAG();
	       by=201;
	       break;
	case XML_tag:
               TAG = copy(TAG,1,length(TAG)-2);	//Normal tag <xxx>
	       if( (tag=TAG())==NULL) break;
	       if(TAG=="CELL") {by=ABI_TABLE;subby=2;break;}
	       if(TAG=="D")    {by=ABI_DATA;subby=0;break;}	//data
	       if(TAG=="FIELD"){by=ABI_FIELD;subby=0;break;}
	       if(TAG=="FRAME"){by=ABI_FRAME;subby=0;break;}
	       if(TAG=="P")    {by=ABI_PAR;subby=0;break;}	//new paragraph
	       if(TAG=="PBR/") {by=ABI_PBR;break;}		//new page
	       if(TAG=="C")    {by=ABI_CHAR;break;}	//character properties
	       if(TAG=="IMAGE"){by=ABI_IMAGE;break;}		//image
	       if(TAG=="SECTION"){by=ABI_SECTION;break;}
	       if(TAG=="TABLE"){by=ABI_TABLE;subby=0;break;}
	       if(TAG=="M")    {by=ABI_META;subby=0;break;}
	       if(TAG=="METADATA"){by=ABI_SECTMETA;subby=0;break;}
	       break;
	case XML_closetag:
               TAG = copy(TAG,2,length(TAG)-3);	//Closing tag </xxx>
	       if( (tag=TAG())==NULL) break;
	       if(TAG=="CELL") {by=ABI_TABLE;subby=3;break;}
	       if(TAG=="D")    {by=ABI_DATA;subby=1;break;}	//data
	       if(TAG=="FRAME"){by=ABI_FRAME;subby=1;break;}
	       if(TAG=="P")    {by=ABI_PAR;subby=1;break;}	//end paragraph
	       if(TAG=="TABLE"){by=ABI_TABLE;subby=1;break;}
	       if(TAG=="M")    {by=ABI_META;subby=1;break;}
	       if(TAG=="METADATA"){by=ABI_SECTMETA;subby=1;break;}
	       break;
        case XML_CDATA:
	case XML_comment:			//comment
	       break;
	case XML_badextchar:
               if(TAG.isEmpty()) break;		//Extended chatacter &xxx
	       TAG[length(TAG)-1] = ' ';
	       TAG=Ext_chr_str(TAG[0],this) + copy(TAG,1,length(TAG)-1);
	       tag = TAG();
	       by = 201;
	       break;
	case 6:				//expanded unicode character
	       break;
	}

  this->by=by;
  this->subby=subby;
  if(flag<Nothing)
    switch(by)
	{
	case XML_char:
               tag = Ext_chr_str(subby,this,ConvertCpg);
	       CharacterStr(this,tag);
	       break;		//Normal character
	case XML_comment:
               if(rownum<10)
	         {		// Strip leading warning, because it no longer makes sense.
		 if(StrStr(TAG(),"<!-- This file is an AbiWord document.")) break;
		 if(StrStr(TAG(),"<!-- AbiWord is a free, Open Source word processor.")) break;
		 if(StrStr(TAG(),"<!-- More information about AbiWord is available at http://www.abisource.com/")) break;
		 if(StrStr(TAG(),"<!-- You should not edit this file by hand.")) break;
		 }     // no break here...
        case XML_CDATA:
	       CommentXML();
	       break;
	case XML_unicode: CharacterStr(this,TAG);
	       break;		//Already expanded unicode character
	case 32:putc(' ', strip);   /*soft space*/
		break;

	case 128:if(TablePos!=1 && TablePos!=3)
		   if(char_on_line)
			SoftReturn(this);
		 break;
	case 129:AttrOn(attr,subby);break;
	case 130:AttrOff(this,subby);     break;
	case ABI_HRT:HardReturn(this);    break;
	case ABI_PAR:if(envir=='B') break;
		 if(subby==0) ParPropAbi();
		 if(subby==1)
		    if(char_on_line) HardReturn(this);  //Paragraph off
		 break;
	case ABI_FIELD:FieldAbi();		break;
	case ABI_CHAR:CharPropAbi();		break;
	case ABI_IMAGE:ImageAbi();		break;
	case ABI_FRAME:if(!subby) FrameAbi();	break;
	case ABI_SECTION:SectionAbi(this);	break;
	case ABI_PBR:HardPage(this);		break;
	case ABI_DATA:if(subby==0) ProcessDataAbi();
		 break;
	case ABI_TABLE:if(subby==0) TableABI();
		 break;
	case ABI_META:if(subby==0) SkipMetaAbi();
    		 break;

	case 200:fputc('~', strip);strcpy(ObjType, " ");
		 break;
	case 201:CharacterStr(this,tag);break; //
	}


 this->by = by;
 this->subby = subby;
 if(log != NULL)
    {   /**/
    if(by==128) fputc('\n',log);
    else if(by==' ' || by==200) fputc(' ',log);
    else if(by==0 || by==201)
	{
	if(tag) fputs(tag,log);
	}
    else
	{
	fprintf(log, _("\n%*s [%s %s]   "),
		  recursion * 2, "", TAG(), ObjType);
//	if(*cq->ObjType==0) UnknownObjects++;
	}
    }

 ActualPos = ftell(wpd);
}


int TconvertedPass1_Abi::Convert_first_pass(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_Abi::Convert_first_pass() ");fflush(log);
#endif
uint32_t fsize;

  if(Verbosing >= 1)
     printf(_("\n>>>Abiword2LaTeX<<< Conversion module: From AbiWord to LaTeX Version %s\n"
	      "      Made by J.Fojtik  (Hosted on WP2LaTeX :))))\n\n"),
	    AbiwordVersion);

  ConvertHTML = GetTranslator("htmlTOinternal");
  SelectTranslator("UTF8");

  TablePos=0;

  DocumentStart=ftell(wpd);
  fsize = FileSize(wpd);
  perc.Init(ftell(wpd), fsize,_("First pass Abiword:") );

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

      TAG.erase();
      ProcessKeyABI();
      }

  Finalise_Conversion(this);
  return(1);
}
