/*************************************************************\
 * 06TAB.CPP - Program by A.Kovalew (Ukraine). Box number 6. *
 *                                                           *
 * Tasks solved: 1,2,4                                       *
 *                                                           *
\*************************************************************/

#include <stdio.h>
#include <conio.h>
#include <alloc.h>
#include <stdlib.h>
#include <ctype.h>
#include <process.h>
#include <string.h>

#define NORMAL 0
#define LEFT   1
#define RIGHT  2
#define CENTER 3

char *iofile[2];		// file names
char *temp;			// temp string
int N,nx,ny;			// tablelength

char *mmm(long n);
void error(char *mes);
void ex(void);
void memerror(void);

void readfile(void);
void writefile(void);

void read_data(char *str,FILE *fp);	// in readfile()
void read_lex (char *str,FILE *fp);

void frmtstr(char *str2,char *str,int n,int n2,int t);// formats string to another

void wrn(FILE *fp,int c,int num);	// for writefile()
void wr (FILE *fp,int c);

int mycmp(char *str1,char *str2,int start);

void findwh(void);		// finds width,height

struct cellstr			// main cell structure
{ unsigned int x,y;		// coordinates on virtual screen
  char *data;
  unsigned int *p;			// pointers to offsets of sub-strings of data
  int alx,aly;
  int wx,wy;
};
int *width;
int *height;

cellstr **cell;			// table definition

void main(int argc,char *argv[])
{ int i,I,fl=0,fl2=0,ix,iy;
  char *str;
  temp=mmm(9000);
  printf("\n\n****************** 06TAB *******************\n");
  printf("*  Program by Andrei Kovalew, Ukraine.     *\n");
  printf("*  Made specially for ICP\'96!             *\n");
  printf("********************************************\n");
  printf("Starting work...\n");
  if(argc<3 || argc>4)error("Wrong parameters. Use 06TAB [-nXXX] file1 file2.\n");
  for(I=1;I<argc;I++)    		// reading parameters
  { if(argv[I][0]=='-')
    { str=mmm(1000);
      for(i=1;i<=strlen(argv[I]);i++)str[i-1]=argv[I][i];
      N=atoi((const char *)str);
      if(N<=2)error("Table width must be > 2.\n");
      fl=1;
    }
    else
    { iofile[fl2]=mmm(strlen(argv[I]));
      strcpy(iofile[fl2],argv[I]);fl2++;
    }
  }
  if(fl==0)N=80;
  readfile();
  findwh();
  writefile();
  printf("Wait please...\n");
  ex();
}
char *mmm(long n)
{ char *r;
  r=(char *)malloc(n+1);
  if(r==NULL)memerror();
  return r;
}
void error(char *mes)
{ printf("\nERROR: %s\n",mes);
  //farfree(temp);
  exit(2);
}
void ex(void)
{ //farfree(temp);
  printf("The work is done. Bye!\n");
  exit(0);
}
void readfile(void)
{ FILE *fp;
  char *lex;
  int i,tnx,x,y,ffl,GGG;
  fp=fopen(iofile[0],"rt");
  if(fp==NULL)error("Error while opening the file.");
  lex=mmm(1000);x=y=0;nx=0;ny=0;tnx=0;
  cell=(struct cellstr **)malloc(2*sizeof(struct cellstr *)+1);
  if(cell==NULL)memerror();
  cell[0]=(struct cellstr *)malloc(2*sizeof(struct cellstr)+1);
  if(cell[0]==NULL)memerror();
  read_lex(lex,fp);
  if(strcmp(lex,"TR")!=0)error("Data must start with <TR> tag !");
  ny=1;
  while(1)
  { read_lex(lex,fp);lex=strupr(lex);
    if(strncmp(lex,"TR",2)==0)
    { y++;x=0;ny++;tnx=0;GGG=-1;
      if(mycmp(lex,"ALIGN=LEFT",3)==0)GGG=LEFT;
      if(mycmp(lex,"ALIGN=RIGHT",3)==0)GGG=RIGHT;
      if(mycmp(lex,"ALIGN=CENTER",3)==0)GGG=CENTER;
      cell=(struct cellstr **)
		realloc((void *)cell,(ny+1)*sizeof(struct cellstr *));
      if(cell==NULL)memerror();
      cell[y]=(struct cellstr *)malloc(sizeof(struct cellstr)*2);
      if(cell[y]==NULL)memerror();
    }
    else
    if(strncmp(lex,"TD",2)==0 || strncmp(lex,"TH",2)==0)
    { tnx++;
      if(nx<tnx)
      { nx=tnx;
	for(i=0;i<ny;i++)
	{ cell[i]=(struct cellstr *)realloc((void *)cell[i],sizeof(struct cellstr)*nx);
	  if(cell[i]==NULL)memerror();
	}
      }
      read_data(temp,fp);
      cell[y][x].data=mmm(strlen(temp));
      strcpy(cell[y][x].data,temp);
      cell[y][x].wx=strlen(temp);
      cell[y][x].wy=1;ffl=0;
      if(GGG!=-1)			// special case for whole row
      { cell[y][x].alx=GGG;
	cell[y][x].aly=NORMAL;
	goto NXT;      // clumzy ...
      }
      if(mycmp(lex,"ALIGN=RIGHT",3)==0)
      { cell[y][x].alx=RIGHT;
	cell[y][x].aly=NORMAL;ffl=1;
      }

      { cell[y][x].alx=LEFT;
	cell[y][x].aly=NORMAL;ffl=1;
      }
      if(mycmp(lex,"ALIGN=CENTER",3)==0)
      { cell[y][x].alx=CENTER;
	cell[y][x].aly=NORMAL;ffl=1;
      }
      if(ffl==0)			// align by default
      { if(strncmp(lex,"TD",2)==0)
	{ cell[y][x].alx=NORMAL;
	  cell[y][x].aly=NORMAL;
	} else
	{ cell[y][x].alx=CENTER;
	  cell[y][x].aly=CENTER;
	}
      }
      NXT:x++;
    }  else error("Unknown tag.");
    if(feof(fp))break;
  }
  printf("Table %dx%d found.\n",nx,ny);
  if(nx==0 || ny==0)error("Wrong table.");
}

void memerror(void)
{ error("Memory allocation error.");
}
void read_data(char *str,FILE *fp)
{ int c,cc,c1,c2,c3,i,pos,fl;
  i=0;fl=0;
  while(!feof(fp))
  { c=fgetc(fp);
    if(c=='<'){ ungetc(c,fp);break; }
    if(c=='&')				// scanning >,<,&
    { c1=fgetc(fp);c2=fgetc(fp);c1=toupper(c1);c2=toupper(c2);
      fl=0;
      if(c1=='L' && c2=='T'){ fgetc(fp);str[i++]='<';fl=1; }
      if(c1=='G' && c2=='T'){ fgetc(fp);str[i++]='>';fl=1; }
      if(c1=='A' && c2=='M')if(toupper(fgetc(fp))=='P'){ fgetc(fp);str[i++]='&';fl=1; }
      if(fl==0)error("Wrong &-operand use. Should be &lt, &gt or &amp.");
      continue;
    }
    if(isgraph(c))str[i++]=c;
     else
     { while(!feof(fp))
       { cc=fgetc(fp);
	 if(cc!=32 && cc!='\n' && cc!='\t')break;
       }
       if(feof(fp))break;
       ungetc(cc,fp);if(c!='\n')str[i++]=32;
     }
  }
  str[i]='\0';
}
void read_lex (char *str,FILE *fp)		// sorry, without buffering
{ int c,i,pos,fl;
  i=0;fl=0;
  while(1)
  { if(feof(fp))break;
    c=fgetc(fp);
    if(fl==1 && c=='>')break;
    if(fl==1)str[i++]=toupper(c);
    if(fl==0 && c!='<')error("Lexem must have \'<\' first. EXAMPLE: <TH>, <TD>...");
      else fl=1;
  }
  str[i]='\0';
//  printf("<%s> ",str);
}
void findwh(void)			// finds general height,width
{ int i,iy,mwidth,mw;			// and wraps data of cells
  width=(int *)malloc(nx*2);
  if(width==NULL)memerror();
  height=(int *)malloc(ny*2);
  if(height==NULL)memerror();
  for(i=0;i<nx;i++)width[i]=1;
  for(i=0;i<ny;i++)height[i]=1;
  for(i=0;i<nx;i++)
    for(iy=0;iy<ny;iy++)
    { if(width[i]<cell[iy][i].wx)width[i]=cell[iy][i].wx;	// found widths
      cell[iy][i].p=(unsigned int *)malloc(3);
      if(cell[iy][i].p==NULL)memerror();
      cell[iy][i].p[0]=cell[iy][i].wx;
      cell[iy][i].aly=1;
    }
  mwidth=0;
  for(i=0;i<nx;i++)
  { width[i]+=2;		// spaces from both sides
    mwidth+=width[i];		// summ
  }
  if(mwidth>N)
  { mw=(N-1)/ny-1;
    for(i=0;i<nx;i++)
     for(iy=0;iy<ny;iy++)
      if(cell[iy][i].wx>mw)
      { if(mw!=0)cell[iy][i].wy=cell[iy][i].wx/mw+1; else error("Internal error.");
	if(height[iy]<cell[iy][i].wy)height[iy]=cell[iy][i].wy;	// found heights
	cell[iy][i].p=(unsigned int *)realloc((void *)cell[iy][i].p,2*cell[iy][i].wy+1);
	if(cell[iy][i].p==NULL)memerror();
	for(i=0;i<cell[iy][i].wy-1;i++)cell[iy][i].p[i]=mw;
	cell[iy][i].p[cell[iy][i].wy-1]=cell[iy][i].wx%mw;
      }
  }
}
void writefile(void)
{ FILE *fp;
  int ix,iy,i,ii;
  fp=fopen(iofile[1],"w");
  for(iy=0;iy<ny;iy++)
  { if(iy==0)wr(fp,'+'); else wr(fp,'|');
    for(ix=0;ix<nx;ix++)
    { wrn(fp,'-',width[ix]);
      if(ix==nx-1 && iy!=0)wr(fp,'|');
	else if(iy==0 && ix!=nx-1)wr(fp,'-'); else wr(fp,'+');
    }
    wr(fp,'\n');
    for(ix=0;ix<nx;ix++)
     for(ii=0;ii<height[iy];ii++)
     { if(ii<cell[iy][ix].wy)
       { frmtstr(temp,(char *)(cell[iy][ix].data+cell[iy][ix].wx*ii),	// align !!!
		cell[iy][ix].wx,width[ix]-2,cell[iy][ix].alx);
	 wr(fp,'|');wr(fp,' ');
	 for(i=0;i<width[ix]-2;i++)wr(fp,temp[i]);
	 wr(fp,' ');
       } else { wr(fp,'|');wrn(fp,' ',width[ix]); }
     }
    wr(fp,'|');wr(fp,'\n');
  }
  wr(fp,'+');
  for(ix=0;ix<nx;ix++)
  { wrn(fp,'-',width[ix]);
    if(ix==nx-1 && iy!=ny-1)wr(fp,'+'); else wr(fp,'-');
  }
  wr(fp,'\n');
  fclose(fp);
}
void wrn(FILE *fp,int c,int num)	// for writefile()
{ for(int i=0;i<num;i++)fputc(c,fp);// printf("%c",c);
}
void wr (FILE *fp,int c)
{ fputc(c,fp);
  //printf("%c",c);
}
void frmtstr(char *str2,char *str,int n,int n2,int t)
{ int i;
  if(t==NORMAL || t==LEFT)
  { for(i=0;i<n;i++)str2[i]=str[i];
    for(i=n;i<n2;i++)str2[i]=32;
  }
  if(t==RIGHT)
  { for(i=0;i<n2-n;i++)str2[i]=32;
    for(i=0;i<n;i++)str2[n2-n+i]=str[i];
  }
  if(t==CENTER)
  { for(i=0;i<(n2-n)/2;i++)str2[i]=32;
    for(i=0;i<n;i++)str2[(n2-n)/2+i]=str[i];
    for(i=(n2-n)/2+n;i<n2;i++)str2[i]=32;
  }
  str2[n2]='\0';
}
int mycmp(char *str1,char *str2,int start)
{ for(int i=0;i<strlen(str2);i++)
   if(str1[i+start]!=str2[i])return -1;
  return 0;
}