/******************************************************************************
 * 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<strings.h>
#include<lists.h>
#include<dbllist.h>

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


string CutFileName(const char *FullFilename); //from wp2lfuti.cc
int SavePictureEPS(const char *Name,const Image &Img); //froom ras_img.cc


#define AbiwordVersion "0.8"


/*Register translators here*/
class TconvertedPass1_Abi: public TconvertedPass1_XML
     {
public:
     virtual int Convert_first_pass(void);
     };
TconvertedPass1 *Factory_Abi(void) {return new TconvertedPass1_Abi;}
FFormatTranslator FormatAbiword("abiword",Factory_Abi);
static void ProcessKeyABI(TconvertedPass1_XML *cq);


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++;
   }
}



static void ParPropAbi(TconvertedPass1_XML *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ParPropAbi() ");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 "text-align")>=0)
	{			    /*0x80-Left, 0x83-Right, 0x82-Center, 0x81-Full */
	Props=d.Member(i,1);
	if(!StrCmp(Props,"center")) Justification(cq, 0x82);
	if(!StrCmp(Props,"right"))  Justification(cq, 0x83);
	if(!StrCmp(Props,"left"))   Justification(cq, 0x80);
	if(!StrCmp(Props,"justify"))Justification(cq, 0x81);
	}
     }
  if(cq->char_on_line) HardReturn(cq);  //Paragraph on
  SoftReturn(cq);

return;
}


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

  InitAttr(NewAttr);
  if((i=cq->TAG_Args IN "props")>=0)
     {
     Props=cq->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(cq->attr,NewAttr,s);
  if(s!="") fputs(s,cq->strip);

return;
}


static void FieldAbi(TconvertedPass1_XML *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#FieldAbi() ");fflush(cq->log);
#endif
int i;
char *Type;

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


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

 if((i=cq->TAG_Args IN "strux-image-dataid")>=0)
 {
   Box.Width = -1; 		// Undefined, use default 100mm
   Box.Image_type = 0;		// Image on disk, even when embedded. Would be extracted later.
   Box.AnchorType = 0; 		// 0-Paragraph, 1-Page, 2-Character
   Box.HorizontalPos = 2;	// 0-Left, 1-Right, 2-Center, 3-Full
   Box.Image_size = 0;
   Box.RotAngle = 0;

   FileName = cq->TAG_Args.Member(i,1);
   if(FileName==NULL) FileName="dummy";
   if((i=cq->TAG_Args IN "props")>=0)
     {
     Props = cq->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;}
	  else if(strstr(w_props,"cm")) {Box.Width = 10*width;}
	  else if(strstr(w_props,"in")) {Box.Width = 25*width;} //inch
          }
	}
     }

   ImageWP(cq, FileName, Box, NULL);
 }
return;
}


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

  Box.Width = -1;		// Undefined, use default 100mm
  Box.Image_type=0;		// Image on disk
  Box.AnchorType = 0; 		// 0-Paragraph, 1-Page, 2-Character
  Box.HorizontalPos=2;		// 0-Left, 1-Right, 2-Center, 3-Full
  Box.Image_size = 0;
  Box.RotAngle = 0;
  
  if((i=cq->TAG_Args IN "dataid")>=0)
	FileName=cq->TAG_Args.Member(i,1);
  if((i=cq->TAG_Args IN "props")>=0)
     {
     Props=cq->TAG_Args.Member(i,1);
     ParseProps(Props,d);
     }
  if(FileName==NULL) FileName="dummy";

  ImageWP(cq, FileName, Box, NULL);
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;
}


static void ProcessDataAbi(TconvertedPass1_XML *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ProcessDataAbi() ");fflush(cq->log);
#endif
unsigned char OldFlag;
FILE *f;
DWORD Data=0;
char data64;
char cnt;
string filename;

 OldFlag = cq->flag;
 cq->flag = Nothing;
 
 if((cnt=cq->TAG_Args IN "name")>=0)
   {   
   filename=cq->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 = fopen(filename(),"wb");

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

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

      switch(cnt)
        {
        case 0: Data = (DWORD)data64<<18; cnt=1; break;
        case 1: Data |= (DWORD)data64<<12;cnt=2; break;
        case 2: Data |= (DWORD)data64<<6; cnt=3; break;
        case 3: Data |= (DWORD)data64;
	        cnt=0;
                if(f) {
	 	      fputc(Data>>16,f);
		      fputc((Data&0xFF00)>>8,f);
		      fputc(Data&0xFF,f);
		      }
	         break;
         }
     }
    else
      {
      ProcessKeyABI(cq);
      if(cq->by==ABI_DATA && cq->subby==1) break;
      }
    } while(!feof(cq->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.Raster!=NULL)
      {
      filename = MergePaths(OutputDir,RelativeFigDir)+CutFileName(filename)+".eps";
      if(SavePictureEPS(filename(),Img)<0)
        {
        if(cq->err != NULL)
	  {
	  cq->perc.Hide();
	  fprintf(cq->err, _("\nError: Cannot save file:%s !"),filename());
	  }
        }
      }      
    }

  cq->flag = OldFlag;
}


static void SkipMetaAbi(TconvertedPass1_XML *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#SkipMetaAbi() ");fflush(cq->log);
#endif
unsigned char OldFlag;

OldFlag = cq->flag;
cq->flag = Nothing;

do {
   cq->TAG = "";
   ProcessKeyABI(cq);
   if(cq->by==ABI_META && cq->subby==1) break;
   if(cq->by==ABI_SECTMETA && cq->subby==1) break;
   } while(!feof(cq->wpd));

cq->flag = OldFlag;
}


static void TableABI(TconvertedPass1_XML *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#TableABI() ");fflush(cq->log);
#endif
unsigned char OldFlag;
char OldEnvir;
long FilePos;
int i,FieldCount=0;
char *Prop;
char *FieldPos=NULL;
char Alignment;
doublelist PrpL;

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

do {
   cq->TAG = "";
   ProcessKeyABI(cq);
   if(cq->by==ABI_TABLE)
     {
     if(cq->subby == 0) break;			// <TABLE>
     if(cq->subby == 1) break;			// </TABLE>
     if(cq->subby == 2)				// <CELL>
       {
       if((i=cq->TAG_Args IN "props")>=0)
          {
          ParseProps(cq->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(cq->subby == 3) 			// </CELL>
	{
	FieldCount++;
	}
     }
   }while(!feof(cq->wpd));


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

    cq->envir = 'B';

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

    cq->char_on_line = false;
    cq->nomore_valid_tabs = false;
    cq->rownum++;
    Make_tableentry_attr(cq);
    cq->latex_tabpos = 0;
    }

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

	cq->ReadXMLTag();
	}

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

  cq->recursion--;

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


static void ProcessKeyABI(TconvertedPass1_XML *cq)
{
#ifdef DEBUG
  fprintf(cq->log,"\n#ProcessKeyABI() ");fflush(cq->log);
#endif
string TAG;
const char *tag;
BYTE by,subby;

 *cq->ObjType=0;
 if(cq->TAG=="") cq->ReadXMLTag();
 by=cq->by;
 subby=cq->subby;

 switch(by)
	{
	case 0:switch(cq->subby)                        //Normal character
		  {
		  case 10:
		  case 13:by=128;break; //CR
		  case  9:strcpy(cq->ObjType, "!Tab");
		  case 32:by=32;break;  //Space
		  }
	       break;
	case 1:if(cq->TAG=="") break;			//Extended chatacter &xxx;
	       TAG = Ext_chr_str(cq->TAG[0],cq)+copy(cq->TAG,1,length(cq->TAG)-1);
	       tag=TAG();
	       by=201;
	       break;
	case 2:TAG=copy(cq->TAG,1,length(cq->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 3:TAG=copy(cq->TAG,2,length(cq->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 4:				//comment
	       break;
	case 5:if(cq->TAG=="") break;		//Extended chatacter &xxx
	       cq->TAG[length(cq->TAG)-1]=' ';
	       TAG=Ext_chr_str(cq->TAG[0],cq)+copy(cq->TAG,1,length(cq->TAG)-1);
	       tag=TAG();
	       by=201;
	       break;
	case 6:				//expanded unicode character
	       break;
	}

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

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

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


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

 cq->ActualPos = ftell(cq->wpd);
}


int TconvertedPass1_Abi::Convert_first_pass(void)
{
#ifdef DEBUG
  fprintf(log,"\n#TconvertedPass1_Abi::Convert_first_pass() ");fflush(log);
#endif
DWORD 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 = "";
      ProcessKeyABI(this);
      }

  Finalise_Conversion(this);
  return(1);
}
