#include <stdio.h>
#include <string.h>
#include <math.h>
#include <gd.h>
#include <gdfontt.h>
#include <gdfonts.h>

#define MAX 1000

#define HOURPIX 120
#define DEGREEPIX 6
#define KMPIX 300
#define LEFT 40
#define RIGHT 10
#define TOP 10
#define BOTTOM 40

/*
  Per compilare:
  
  gcc montagna.c -lpng -lgd -o montagna

 */

int n=0;
int min[MAX];
int quota[MAX];
float temp[MAX];
char *note[MAX];
char flag[MAX];
char *titolo;
char *data;

int quotaMin=20000,quotaMax=-1000;
int salita=0,discesa=0;
int tempoSalita=0,tempoDiscesa=0;
int salitaMax=0,discesaMax=0;
int errore=0;

char *ora(int min) {
  static char s[10][10];
  static int n=-1;
  n++;
  if (n>=10) n=0;
  sprintf(s[n],"%.2d:%2.2d",min/60,min%60);
  return s[n];
}

void outTxt(FILE *out) {
  int i;
  for (i=0;i<n;i++) 
    fprintf(out,"%s %4d %c %s\n",
	    ora(min[i]),quota[i],flag[i],note[i]);
}

void outHtml(FILE *out) {
  int i;
  
  fprintf(out,"<h1>%s</h1>\n",titolo);
  fprintf(out,"<h3>%s</h3>\n",data);
  fprintf(out,"<!--begin-->\n");
  fprintf(out,"<table bgcolor=yellow><tr><th>Ore<th>quota<th>temp.<th>note</th></tr>\n");
  for (i=0;i<n;i++) {
    if ((note[i] && strlen(note[i]))||(strchr("AP",flag[i]))) {
      fprintf(out,"<tr><td bgcolor=pink>%s"
	      "<td bgcolor=pink>%.4d"
	      "<td bgcolor=pink>%2.2f"
	      "<td bgcolor=pink>%s%s</tr>\n",
	      ora(min[i]),quota[i],temp[i],note[i]?note[i]:"",flag[i]=='A'?" (arrivo)":flag[i]=='P'?" (partenza)":"");
    }
  }
  fprintf(out,"</table>\n");
  fprintf(out,"<!--middle-->\n");
  fprintf(out,"<p><table border=1>");
  fprintf(out,"<tr><td>Quota massima/minima: <td>%d m"
	  "<td>%d m</tr>\n",quotaMax,quotaMin);
  fprintf(out,"<tr><td>Dislivello:<td colspan=2>%d m</tr>",quotaMax-quotaMin);
  fprintf(out,"<tr><td>Tempo totale (con soste):<td colspan=2>%s h</tr>\n",
	 ora(min[n-1]-min[0]));
  fprintf(out,"<tr><td>Tempo salita/discesa:<td>%s h"
	  "<td>%s h</tr>\n",ora(tempoSalita),ora(tempoDiscesa));

  fprintf(out,"<tr><td>Dislivello salita/discesa:<td>%d m"
	  "<td>%d m</tr>\n",salita,discesa);

  fprintf(out,"<tr><td>Velocit&agrave; media salita/discesa:<td>%d m/h"
	  "<td>%d m/h</tr>\n",
	  60*salita/tempoSalita,60*discesa/tempoDiscesa);
  fprintf(out,"<tr><td>Velocit&agrave; massima salita/discesa: <td>%d m/h"
	  "<td>%d m/h</tr>\n",
	 salitaMax,discesaMax);
  fprintf(out,"</table>\n");
}

void outGraph(FILE *out) {
  int quotaMax=-1000,quotaMin=10000;
  int minMax=-1000,minMin=10000;
  int maxx,maxy;
  int i,pass;
  gdImagePtr im;
  int back,fore,fill,line,write,grid,pausa;
  gdPoint points[4];

  for (i=0;i<n;i++) {
    if (quota[i]>quotaMax) quotaMax=quota[i];
    if (quota[i]<quotaMin) quotaMin=quota[i];
    if (min[i]>minMax) minMax=min[i];
    if (min[i]<minMin) minMin=min[i];
  }
  quotaMin-=50;
  quotaMax+=50;
  im = gdImageCreate(maxx=LEFT+HOURPIX*(minMax-minMin)/60+RIGHT,
		     maxy=BOTTOM+KMPIX*(quotaMax-quotaMin)/1000+TOP);
  
  back=gdImageColorAllocate(im,96,192,255);
  gdImageColorTransparent(im,back);
  fore=gdImageColorAllocate(im,0,0,0);
  fill=gdImageColorAllocate(im,0,255,0);
  line=gdImageColorAllocate(im,0,215,0);
  pausa=line;
  write=gdImageColorAllocate(im,255,0,0);
  grid=gdImageColorAllocate(im,200,255,200);

  /*grid*/

  for (i=minMin;i<minMax;i++) {
    int x=LEFT+(i-minMin)*HOURPIX/60;
    char s[10];
    if (i%60==0)
      gdImageFilledRectangle(im,x-1,TOP,x+1,maxy-BOTTOM,grid);
    else if (i%15==0)
      gdImageLine(im,x,TOP,x,maxy-BOTTOM,grid);
  }
  
  for (i=0;i<n;i++) {
    if (i && min[i-1]>min[i]-4) continue;
    gdImageStringUp(im,gdFontSmall,
		    LEFT+(min[i]-minMin)*HOURPIX/60-gdFontSmall->h/2,
		    maxy-(BOTTOM-6*gdFontSmall->w),ora(min[i]),fore);
  }


  for (i=quotaMin;i<quotaMax;i++) {
    int y=maxy-(BOTTOM+(i-quotaMin)*KMPIX/1000);
    char s[10];
    if (i%1000==0)
      gdImageFilledRectangle(im,LEFT,y-1,maxx-RIGHT,y+1,grid);
    else if (i%100==0)
      gdImageLine(im,LEFT,y,maxx-RIGHT,y,grid);
    if (i%100==0) {
      sprintf(s,"%d",i);
      gdImageString(im,gdFontSmall,LEFT-5*gdFontSmall->w,y-gdFontSmall->h/2,s,fore);
    }
  }

  /*graph*/
  for (pass=0;pass<3;pass++) {
    for (i=0;i<n;i++) {
      points[2].x=points[1].x;
      points[2].y=points[1].y;
      points[3].x=points[0].x;
      points[3].y=points[0].y;
      points[0].x=LEFT+(min[i]-minMin)*HOURPIX/60;
      points[0].y=maxy-BOTTOM;
      points[1].x=points[0].x;
      points[1].y=maxy-(BOTTOM+(quota[i]-quotaMin)*KMPIX/1000);
      if (i>0) {
	if (pass==0)
	  gdImageFilledPolygon(im,points,4,flag[i-1]=='A'?pausa:fill);
	if (pass==1)
	  gdImageLine(im,points[1].x,points[1].y,points[2].x,points[2].y,fore);
      }
      if (note[i] && strlen(note[i])) {
	if (pass==2) {
	  gdImageLine(im,points[0].x,points[0].y,points[1].x,points[1].y,line);
	  gdImageStringUp(im,gdFontSmall,
			  points[0].x-((i==0)?0:(i==n-1)?2:1)*gdFontSmall->h/2,
			  points[0].y-gdFontSmall->h,note[i],write);
	}
      }
    }
  }
  
  /*frame*/
  gdImageRectangle(im,LEFT,maxy-BOTTOM,maxx-RIGHT,TOP,fore);
  
  gdImagePng(im,out);
  gdImageDestroy(im);
}

void outTempGraph(FILE *out) {
  float tempMax=-1000,tempMin=10000;
  int minMax=-1000,minMin=10000;
  int maxx,maxy;
  int i,pass;
  

  gdImagePtr im;
  int back,fore,fill,line,write,grid,pausa;
  gdPoint points[2];

  for (i=0;i<n;i++) {
    if (temp[i]>tempMax) tempMax=temp[i];
    if (temp[i]<tempMin) tempMin=temp[i];
    if (min[i]>minMax) minMax=min[i];
    if (min[i]<minMin) minMin=min[i];
  }
  tempMin-=2;
  tempMax+=2;
  im = gdImageCreate(maxx=LEFT+HOURPIX*(minMax-minMin)/60+RIGHT,
		     maxy=BOTTOM+DEGREEPIX*(tempMax-tempMin)+TOP);
  
  back=gdImageColorAllocate(im,96,192,255);
  gdImageColorTransparent(im,back);
  fore=gdImageColorAllocate(im,0,0,0);
  fill=gdImageColorAllocate(im,0,255,0);
  line=gdImageColorAllocate(im,0,215,0);
  pausa=line;
  write=gdImageColorAllocate(im,255,0,0);
  grid=gdImageColorAllocate(im,200,255,200);


  /*grid*/

  for (i=minMin;i<minMax;i++) {
    int x=LEFT+(i-minMin)*HOURPIX/60;
    char s[10];
    if (i%60==0)
      gdImageFilledRectangle(im,x-1,TOP,x+1,maxy-BOTTOM,grid);
    else if (i%15==0)
      gdImageLine(im,x,TOP,x,maxy-BOTTOM,grid);
  }
  
  for (i=0;i<n;i++) {
    if (i && min[i-1]>min[i]-4) continue;
    gdImageStringUp(im,gdFontSmall,
		    LEFT+(min[i]-minMin)*HOURPIX/60-gdFontSmall->h/2,
		    maxy-(BOTTOM-6*gdFontSmall->w),ora(min[i]),fore);
  }


  for (i=tempMin+1;i<=tempMax;i++) {
    int y=maxy-(BOTTOM+(i-tempMin)*DEGREEPIX);
    char s[10];
    if (i%5==0) {
      gdImageLine(im,LEFT,y,maxx-RIGHT,y,grid);
      sprintf(s,"%d",i);
      gdImageString(im,gdFontSmall,LEFT-5*gdFontSmall->w,y-gdFontSmall->h/2,s,fore);
    }
  }
  

  /*graph*/
  for (pass=0;pass<1;pass++) {
    for (i=0;i<n;i++) {
      points[0].x=points[1].x;
      points[0].y=points[1].y;
      points[1].x=LEFT+(min[i]-minMin)*HOURPIX/60;
      points[1].y=maxy-(BOTTOM+(temp[i]-tempMin)*DEGREEPIX);
      if (i>0) {
	gdImageLine(im,points[1].x,points[1].y,points[0].x,points[0].y,fore);
      }
      if (note[i] && strlen(note[i])) {
	gdImageStringUp(im,gdFontSmall,
			points[1].x-((i==0)?0:(i==n-1)?2:1)*gdFontSmall->h/2,
			maxy-(BOTTOM+gdFontSmall->h),note[i],write);
      }
    }
  }

  
  /*frame*/
  gdImageRectangle(im,LEFT,maxy-BOTTOM,maxx-RIGHT,TOP,fore);
  
  gdImagePng(im,out);
  gdImageDestroy(im);
}


void skipSpaces(char **s) {
  while(isspace(**s)) (*s)++;
}

float getFloat(char **s) {
  float r=0;
  float dec=0.0;
  while (1){
    if (isdigit(**s)) {
      if (dec==0.0) 
	r=10.0*r+(**s-'0');
      else {
	r+=dec*(**s-'0');
	dec/=10.0;
      }
    } else if (**s=='.') dec=0.1;
    else break;
    (*s)++;
  }
  return r;
}


int getInt(char **s) {
  int n=0;
  for (;isdigit(**s);(*s)++) {
    n=n*10+(**s-'0');
  }
  return n;
}
 
int getMin(char **s) {
   int min;
   min=getInt(s)*60;
   if (**s=='.') {
     (*s)++;
     min+=getInt(s);
   }
   return min;
 }

char *getString(char **s) {
  char *r;
  r=*s;
  (*s)=strchr(r,0);
  while (isspace(*(*s-1))) (*s)--;
  **s=0;
  return strdup(r);
}

void calcola(void) {
  int i;
  for (i=0;i<n;i++) {
    if (quota[i]<quotaMin) quotaMin=quota[i];
    if (quota[i]>quotaMax) quotaMax=quota[i];
    if (i && flag[i]=='P' && flag[i-1]!='A')
      fprintf(stderr,"Urka: c'e' una partenza senza arrivo!\n");
    if (i && flag[i-1]!='A') {
      int v;
      if (quota[i]>=quota[i-1]) {
	salita+=quota[i]-quota[i-1];
	tempoSalita+=min[i]-min[i-1];
	v=60*(quota[i]-quota[i-1])/(min[i]-min[i-1]);
	if (v>salitaMax) salitaMax=v;
      } else {
	discesa+=quota[i-1]-quota[i];
	tempoDiscesa+=min[i]-min[i-1];
	v=60*(-quota[i]+quota[i-1])/(min[i]-min[i-1]);
	if (v>discesaMax) discesaMax=v;
      }
    }
  }
}

void interpola(int n1,int quota1,int n2,int quota2) {
  int i;

  if (n1<0 && n2>n) return;
			     
  if (n1<0) { /* solo shift */
    for (i=0;i<n2;i++) 
      quota[i] += quota2-quota[n2];
  } else if (n2>=n) {
    int s=quota1-quota[n1];
    for (i=n1; i<n; i++) 
      quota[i] += s;
  } else {
    /*  if (n2<=n1) return; */
    int r1=quota1-quota[n1];
    int r2=quota2-quota[n2];
    for (i=n1;i<n2;i++) {
      quota[i]+=((double)((n2-i)*r1+(i-n1)*r2))/(n2-n1);
    }
  }
}

void load(FILE *in) {
  int lastgiven=-1;
  int lastgivenquota=0;

  char line[256];
  char *p;
  for (n=0;;) {
    if (!fgets(line,sizeof(line),in)) break;
    p=line;
    if (*p=='#') continue;
    if (*p=='@') {
      p++;
      if (*p=='T') {
	p++;
	skipSpaces(&p);
	titolo=getString(&p);
      } else if (*p=='D') {
	p++;
	skipSpaces(&p);
	data=getString(&p);
      } else continue;
    }

    skipSpaces(&p);
    if (*p==0) continue;

    if (isalpha(*p)) {
      flag[n]=toupper(*p);
      p++;
      skipSpaces(&p);
    } else flag[n]=' ';
    
    
    min[n]=getMin(&p);
    
    skipSpaces(&p);
    temp[n]=getFloat(&p); /*temperatura*/
    
    skipSpaces(&p);
    quota[n]=getInt(&p);
    if (n==0) {lastgiven=0;lastgivenquota=quota[0];}

    skipSpaces(&p);
    if (*p=='(') { /*real quota*/
      int given;
      p++;
      skipSpaces(&p);
      given=getFloat(&p);
      skipSpaces(&p);
      if (*p!=')') fprintf(stderr,"')' expected!\n");
      else p++;
      
      interpola(lastgiven,lastgivenquota,n,given);
      lastgiven=n;
      lastgivenquota=given;
    }
      
    skipSpaces(&p);
    note[n]=getString(&p);    
    n++;
  } 
  interpola(lastgiven,lastgivenquota,n,0);

  calcola();
}
 
int usage(char *name) {
  printf("%s <inputfile> [-[txt|apng|tpng|html][=<filename>]...]n\n",name);
  exit(0);
}

int main(int argc,char *argv[]) {
  char *s;
  char *name;
  FILE *fp;
  FILE *out;
  int i;
  
  for (i=1;i<argc;i++) {
    if (argv[i][0]=='-') {
      char *file;
      file=strchr(argv[i],'=');
      if (file) {
	*file=0;
	file++;
	out=fopen(file,"wb");
	if (!out) {
	  perror("fopen");
	  fprintf(stderr,"filename: %s\n",file);
	  exit(1);
	}
      } else
	out=stdout;
      if (!strcmp(argv[i],"-txt")) outTxt(out);
      else if (!strcmp(argv[i],"-apng")) outGraph(out);
      else if (!strcmp(argv[i],"-tpng")) outTempGraph(out);
      else if (!strcmp(argv[i],"-html")) outHtml(out);
      else usage(argv[0]);
      if (out!=stdout) fclose(out);
    }
    else { 
      name=argv[i];
      
      fp=fopen(name,"rt");
      if (!fp) {
	perror("montagna:");
	fprintf(stderr,"file %s\n",name);
	exit(1);
      }
      load(fp);
      fclose(fp);
    }
  }
  return 0;
}
