#include <ctype.h>
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"tag_reader.h"
#include"tag_parser.h"
#include"tag_writer.h"
#include"routines.h"
#include"translator.h"

int main(int argc,char ** argv)
{
  int i;
  int nproc;
  int nevents;
  int theformatver;
  double fullcs;
  int * nout;
  char buff[STRSIZE];
  char dump[STRSIZE];
  char genversion[STRSIZE];
  FILE * inFile;
  FILE * outFile;
  elementary_tag * tag;
  string_comnd * c;
  process * proc1;
  beams * thebeams;
  tags * header;

  if (2 > argc || 3 < argc) {
    ShowHelp (0);
    return 1;
  }
  if (0 == strcmp (argv[1], "-help") || 0 == strcmp (argv[1], "--help")) {
      ShowHelp (1);
      return 1;
  };
  if (3 != argc) {
    ShowHelp (0);
    return 1;
  }
  if (0 == strcmp (argv[1], argv[2])) {
    fprintf (stderr, "***Error! input and output files should be different!\n");
    return 3;
  }

  inFile = fopen (argv[1], "r");
  if (!inFile) {
    fprintf (stderr, "***Error! input file not found: %s\n", argv[1]);
    return 2;
  }
  outFile = fopen (argv[2], "w");
  if (!inFile) {
    fprintf (stderr, "***Error! output file can not be opened: %s\n", argv[1]);
    return 2;
  }

/****************************************************************/
/*     initialize global variables                              */

   theformatver = check_format (inFile);
   switch (theformatver) {
     case 1: fprintf (stdout, "The file is in the 1st format. I'll translate it to the 2nd format!\n");
       break;
     case 2: fprintf (stdout, "The file is in the 2st format. Nothing to be done!\n");
       return 0;
       break;
     default: fprintf (stdout, "Unknown format... Stop\n");
       return -1;
   }

  while(NULL != fgets(buff,1000,inFile)) {
    if (NULL != strstr(buff, "#CompHEP") ) {
      sscanf(buff,"#CompHEP version %s%[^a]", genversion, dump);
    }
    if(NULL != strstr(buff, "#Total_cross_section_(pb)")) {
      sscanf(buff,"#Total_cross_section_(pb) = %lf", &fullcs);
      nout = malloc (nproc * sizeof (int));
    }
    if(NULL != strstr(buff, "#Number_of_subprocesses")) {
      sscanf(buff,"#Number_of_subprocesses = %i", &nproc);
      nout = malloc (nproc * sizeof (int));
    }
    if(NULL != strstr(buff, "#Events_mixed_and_randomized")) {
       sscanf(buff,"#Events_mixed_and_randomized = %i", &nevents);
    }
    if(NULL != strstr(buff,"#Nproc")) {
       break;
    }
  }
  rewind (inFile);

  header = initcap1( "header");

  proc1 = read_subproc_info (proc1, inFile);
  thebeams = fillbeams (thebeams, proc1);
/****************************************************************/
/*         rewriting of header                                  */
  for (i = 0; i < 2; ++i) {
    tag = inittag ("beam");
    sprintf (buff, "ID=%i", i + 1);                      tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "energy=%.5E", thebeams->benergy[i]); tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "KF=%i", thebeams->bkfcode[i]);       tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "name='%s'", thebeams->bname[i]);     tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "mass=%.5E", thebeams->bmass[i]);     tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    header = add_tag (header, tag);
    freetag(tag);

    tag = inittag ("strfun");
    sprintf (buff, "IDbeam=%i", i + 1);                      tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "name='%s'", thebeams->pdfname[i]);       tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "version='%s'", thebeams->pdfversion[i]); tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "PDFgr=%i", thebeams->pdfgr[i]);          tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "PDFid=%i", thebeams->pdfid[i]);          tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    header = add_tag (header, tag);
    freetag(tag);
  }
  freebeams (thebeams);
  freeproc (proc1);
  rewind (inFile);

  for (i = 0; i < nproc; ++i) {
    int j;
    char id[STRSIZE];
    process * theproc;
    theproc = read_subproc_info (theproc, inFile);
    sprintf (id, "IDprocess=%i", i + 1);

    tag = inittag ("process");
    sprintf (buff, "ID=%i", i + 1);                         tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "name='%s'", theproc->name);             tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "CrosSec=%.5E", theproc->crossec);       tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "CrosSecErr=%.5E", theproc->crossecerr); tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "Nparton=%i", theproc->nparton);         tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "master=%i", theproc->master);           tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    header = add_tag (header, tag);
    freetag(tag);
    nout[i] = theproc->nparton;

    tag = inittag ("generator");
    tag->tagsize = addcom (&(tag->commands), tag->tagsize, id);
    sprintf (buff, "name='%s'", "CompHEP");      tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "version='%s'", genversion);  tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    header = add_tag (header, tag);
    freetag(tag);

    tag = inittag ("n_event");
    tag->tagsize = addcom (&(tag->commands), tag->tagsize, id);
    sprintf (buff, "N=%i", theproc->nevents);    tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "mult=%.5E", theproc->mult);  tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "maxW=%.5E", theproc->maxW);  tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "CutN=%i", theproc->ncuts);   tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    header = add_tag (header, tag);
    freetag(tag);

    for (j = 0; j < theproc->nparton; ++j) {
      tag = inittag ("parton");
      tag->tagsize = addcom (&(tag->commands), tag->tagsize, id);
      if (j < 2) {
        sprintf (buff, "in=%i", j+1);  tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
        tag->tagsize = addcom (&(tag->commands), tag->tagsize, "out=0");
      }
      else {
        tag->tagsize = addcom (&(tag->commands), tag->tagsize, "in=0");
        sprintf (buff, "out=%i", j+1);  tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
      }
      sprintf (buff, "KF=%i", theproc->prt[j].kfcode);   tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
      sprintf (buff, "name='%s'", theproc->prt[j].name); tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
      sprintf (buff, "mass=%.5E", theproc->prt[j].mass); tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
      header = add_tag (header, tag);
      freetag(tag);
    }

    for (j = 0; j < theproc->ncuts; ++j) {
      tag = inittag ("cut");
      tag->tagsize = addcom (&(tag->commands), tag->tagsize, id);
      sprintf (buff, "name='%s'", theproc->cut[j].name); tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
      switch (theproc->cut[j].stat) {
        case 1: 
          sprintf (buff, "min=%.5E", theproc->cut[j].min); tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
          tag->tagsize = addcom (&(tag->commands), tag->tagsize, "reg=1");
          break;
        case 2: 
          sprintf (buff, "max=%.5E", theproc->cut[j].max); tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
          tag->tagsize = addcom (&(tag->commands), tag->tagsize, "reg=1");
          break;
        case 3: 
          sprintf (buff, "min=%.5E", theproc->cut[j].min); tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
          sprintf (buff, "max=%.5E", theproc->cut[j].max); tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
          tag->tagsize = addcom (&(tag->commands), tag->tagsize, "reg=1");
          break;
        case 4: 
          sprintf (buff, "filename='%s'", theproc->cut[j].filename); tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
          tag->tagsize = addcom (&(tag->commands), tag->tagsize, "reg=0");
          break;
      }
      header = add_tag (header, tag);
      freetag(tag);
    }

    tag = inittag ("QCDinfo");
    tag->tagsize = addcom (&(tag->commands), tag->tagsize, id);
    sprintf (buff, "NL=%i", theproc->qcdnl);              tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "Nflavour=%i", theproc->qcdnflv);      tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    sprintf (buff, "QCDLambda=%.5E", theproc->qcdlambda); tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    header = add_tag (header, tag);
    freetag(tag);

    tag = inittag("format");
    tag->tagsize = addcom (&(tag->commands), tag->tagsize, id);
    tag->tagsize = addcom (&(tag->commands), tag->tagsize, "ProcNumber='i'");
    tag->tagsize = addcom (&(tag->commands), tag->tagsize, "p1.3='17.10E'");
    tag->tagsize = addcom (&(tag->commands), tag->tagsize, "p2.3='17.10E'");
    for (j = 2; j < theproc->nparton; j++) {
      sprintf (buff, "p%i.1='17.10E'", j + 1); tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
      sprintf (buff, "p%i.2='17.10E'", j + 1); tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
      sprintf (buff, "p%i.3='17.10E'", j + 1); tag->tagsize = addcom (&(tag->commands), tag->tagsize, buff);
    }
    tag->tagsize = addcom (&(tag->commands), tag->tagsize, "Qsquared='10.3E'");
    tag->tagsize = addcom (&(tag->commands), tag->tagsize, "color_chain='string'");
    tag->tagsize = addcom (&(tag->commands), tag->tagsize, "weight='10.3E'");
    header = add_tag (header, tag);
    freetag(tag);
  }

  tag = inittag("total");
  sprintf (buff, "%i", nproc);  tag = add_com (tag, initcom(c, "Nproc", buff, 1)); free(c);
  sprintf (buff, "%i", nevents);    tag = add_com (tag, initcom(c, "Nevent", buff, 1)); free(c);
  sprintf (buff, "%.5E", fullcs); tag = add_com (tag, initcom(c, "CrosSec", buff, 1)); free(c);
  tag = add_com (tag, initcom(c, "CrosSecErr", "0.00000E+00", 1)); free (c);
  header = add_tag (header, tag);
  freetag (tag);

  writetagbase (outFile, header);

/****************************************************************/
/*         rewriting of events                                  */
  while(NULL != fgets(buff,1000,inFile)) {
    if(NULL != strstr(buff,"#Nproc")) {
       break;
    }
  }
  for (i = 0; i < nevents; ++i) {
    event * ev;
    if (1 == read_event_old_format(ev, inFile, nout, nproc)) {
      write_event_new_format (ev, outFile);
    } else {
      fprintf (stdout, "Warning!...");
      break;
    }
  }
  fclose(inFile);
  fclose(outFile);
  return 0;
}

int read_event_new_format (event * ev, FILE * f, tags * header) {
  char ** eventword = malloc (10 * sizeof (char *));
  int stat = 1;
  int npr;
  char buf[STRSIZE];

/*
  if (fgets (buff, 1000, f)) {
    sscanf (buff, "%i:%[^\n]", &npr, buff);
    npr--;
    pos = 
    stat = event_parser (buff, eventword);
    if (!stat) {
      fprintf (stderr, "Error in event %i\n. Exit!", debug);
      exit (1);
    }
    ev->pvect[3] = atof (eventword[pos[npr][3]]);
    ev->pvect[7] = atof (eventword[pos[npr][7]]);
    for (i = 2; i < Npartons[npr]; i++)
      for (j = 0; j < 3; j++)
        ev->pvect[4 * i + j + 1] = atof (eventword[pos[npr][4 * i + j + 1]]);
  ev->scale = atof (eventword[pos[npr][scalepos]]);
  strcpy (ev->colorchain, eventword[pos[npr][colorpos]]);
  }

*/
  return stat;
}

int read_event_old_format (event * ev, FILE * f, int * nnout, int Nproc) {
  int i;
  int npr;
  int stat = 1;
  char buff[STRSIZE];
  char buf[MAXINOUT + 1][STRSIZE];

  if (fgets (buff, 1000, f)) {
    sscanf (buff, "%i%[^\n]", &npr, buf[0]);
    ev->procnumber = npr;
    npr--;
    if (0 > npr || npr > Nproc-1) {
      fprintf (stderr, "Error! subprocess number is of the limits: Nproc = %i, Nproc_MAX =%i \n", npr, Nproc);
      exit (3);
    }
    for (i = 0; i < 4 * nnout[npr]; i++) ev->pvect[i] = 0;

    sscanf (buf[0], "    %lf %lf%[^a]", ev->pvect + 3, ev->pvect + 7, buf[2]);

    for (i = 2; i < nnout[npr]; ++i) {
      int n = 4 * i;
      sscanf (buf[i], " %lf %lf %lf%[^a]", ev->pvect + n + 1, ev->pvect + n + 2, ev->pvect + n + 3, buf[i + 1]);
    }
    sscanf (buf[i], " %lf%[^a]", &(ev->scale), buff);
    ev->colorchain = strchr(buff,'(');
    ev->nout = nnout[npr];
  }
  else {
    stat = 0;
  }
  return stat;
}


int write_event_new_format (event * ev, FILE * f) {
  int i;

  fprintf (f, "%2d:", ev->procnumber);
  fprintf (f, "%17.10E:%17.10E", ev->pvect[3], ev->pvect[7]);
  for (i = 2; i < ev->nout; i++)
    fprintf (f, ":%17.10E:%17.10E:%17.10E", ev->pvect[4 * i + 1], ev->pvect[4 * i + 2], ev->pvect[4 * i + 3]);
  fprintf (f, ":%10.3E:", ev->scale);
  fprintf (f, "%s:\n",ev->colorchain);
  return 1;
}

process * read_subproc_info (process * pr, FILE * f) {
  int i, j;
  int len;
  int num;
  int err = 0;
  char buff[STRSIZE];
  char * buf;
  char * prname;
  char mass[MAXINOUT][STRSIZE];
  char name[MAXINOUT][PARTNAMLEN];

/*  pr = clean(pr);*/

  if (NULL != pr) pr = NULL;
  pr = malloc (sizeof(process));

  pr->ncuts = 0;
  pr->cut = NULL;
  pr->nevents = 0;
  pr->mult = 0.0;
  pr->maxW = 1.0;
  pr->master = 3;
  pr->genname = malloc (10 * sizeof(char));
  strcpy(pr->genname, "CompHEP");
  pr->genversion = malloc (10 * sizeof(char));
  strcpy(pr->genversion, "4.4.4");
  pr->crossecerr = 0.0;

  while ( NULL != fgets (buff, 1000, f) )
    if ( NULL != strstr(buff, "#PROCESS") ) break;

  /* particles */
  sscanf(buff,"#PROCESS  %s%s%[^a]", name[0], name[1], mass[0]);
  buf = strstr(buff, "->");
  len = strlen(buf);
  i = 2; /* exclude -> */
  num = 2;
  while (i < len-1) {
    if (32 == buf[i]) {i++;continue;}
    j = 0;
    while (1) {
      if (32 == buf[i]) {++num;break;}
      name[num][j++] = buf[i++];
    }
  }
  pr->nparton = num;
  prname = malloc (5 * MAXINOUT * sizeof(char));
  prname[0] = '\0';
  for (i = 0; i < num; ++i) {
    if (2 > i) pr->prt[i].stat = -1;
    else pr->prt[i].stat = 1;
    if (2 == i) strcat(prname, "-> ");
    strcat(prname, name[i]);
    strcat(prname, " ");
    strcpy(pr->prt[i].name, name[i]);
    pr->prt[i].kfcode = comphep_particle_name_base (name[i]);
  }
  pr->name = malloc (strlen(prname) * sizeof(char));
  strcpy(pr->name, prname);
  free (prname);

  fgets (buff, 1000, f);
  if (fgets (buff, 1000, f)) sscanf(buff,"  SQRT(S) %lf",&(pr->sqrtS));
  else err = 1;
  if (fgets (buff, 1000, f)) sscanf(buff,"  Rapidity(c.m.s) %lf",&(pr->rapidity));
  else err = 1;
  if (fgets (buff, 1000, f)) sscanf(buff,"  StrFun1: %s",pr->pdf[0]);
  else err = 1;
  if (fgets (buff, 1000, f)) sscanf(buff,"  StrFun2: %s",pr->pdf[1]);
  else err = 1;
  if (fgets (buff, 1000, f) ) {
    sscanf(buff,"#MASSES  %[^a]",mass[0]);
    for (i = 0; i < pr->nparton; ++i) {
      double themass;
      sscanf (mass[i], " %lf%[^a]",&themass, mass[i + 1]);
      pr->prt[i].mass = themass;
    }
  }
  else err = 1;
  if (fgets (buff, 1000, f) ) sscanf(buff,"#Cross_section(Width) %lf",&pr->crossec);
  else err = 1;

  if (err) {
    fprintf(stdout, "Error in the file format!");
    exit(3);
  }

  return pr;
}

beams * fillbeams (beams * b, process * pr) {
  int i, j;
  char * strname;
  char * bbname;
  char * temp;
  char bname[STRSIZE];
  char pdfname[STRSIZE];
  double bmom[2];
  double sqrtS, Rapidity;
  double bmass[2] = {0.0,0.0};

  if (NULL != b) b = NULL;
  b = malloc (sizeof(beams));
  if (NULL == pr) return b;

  sqrtS = pr->sqrtS;
  Rapidity = pr->rapidity;

  for (i = 0; i < 2; ++i) {
    double e = (sqrtS * sqrtS + bmass[i] * bmass[i] - bmass[1 - i] * bmass[1 - i]) / (2 * sqrtS);
    double p = sqrt (e * e - bmass[i] * bmass[i]);
    bmom[i] = p * cosh (Rapidity) + e * sinh (Rapidity) * (1 - 2 * i);
    if (bmom[i] * bmom[i] < (10.E-10) * sqrtS) bmom[i] = 0.0;
  }

  for (i = 0; i < 2; ++i) {
    b->benergy[i] = bmom[i];
    if (NULL != strstr (pr->pdf[i],"PDF:")) {    /* hadrons */
      strname = strchr (pr->pdf[i], pr->pdf[i][4]);
      bbname = strchr (pr->pdf[i], '(');
      str_cpy_both(bname, bbname);
      strncpy(pdfname, strname, strlen(strname) - strlen(bbname));


      b->bkfcode[i] = comphep_particle_name_base (bname);
      b->bname[i] = malloc ((strlen(bname) + 1) * sizeof(char));
      strcpy(b->bname[i], bname);

      for (j = 0; j < strlen(pdfname); ++j) 
        if (isupper(pdfname[j]) ) pdfname[j] = tolower (pdfname[j]);

      if (NULL != strstr(pdfname, "cteq")) {
        b->pdfid[i] = get_pdfsup (pdfname);
        b->pdfgr[i] = 4;
        b->pdfname[i] = malloc (5 * sizeof(char));
        strcpy(b->pdfname[i], "cteq");
        temp = strchr(pdfname, pdfname[4]);
        b->pdfversion[i] = malloc ((strlen(temp) + 1) * sizeof(char));
        strcpy(b->pdfversion[i], temp);
      }

      if (NULL != strstr(pdfname, "mrst")) {
        b->pdfid[i] = get_pdfsup (pdfname);
        b->pdfgr[i] = 3;
        b->pdfname[i] = malloc (5 * sizeof(char));
        strcpy(b->pdfname[i], "mrst");
        temp = strchr(pdfname, pdfname[4]);
        b->pdfversion[i] = malloc ((strlen(temp) + 1) * sizeof(char));
        strcpy(b->pdfversion[i], temp);
      }
    }
  }

  return b;
}

void freebeams(beams * b) {
  int i;
  if (NULL != b) {
    for (i = 0; i < 2; ++i) {
      if (NULL != b->bname[i]) free(b->bname[i]);
      if (NULL != b->pdfname[i]) free(b->pdfname[i]);
      if (NULL != b->pdfversion[i]) free(b->pdfversion[i]);
      if (NULL != b->pdffile[i]) free(b->pdffile[i]);
    }
    free(b);
  }
}

void freeproc(process * p) {
  int i;
  if (NULL != p) {
    if (NULL != p->genname) free(p->genname);
    if (NULL != p->genversion) free(p->genversion);
    if (NULL != p->name) free(p->name);
    for (i = 0; i < p->ncuts; ++i) {
      freecut(&(p->cut[i]));
    }
    free(p);
  }
}

void freecut(cut * c) {
  if (NULL != c) {
    if (NULL != c->name) free(c->name);
    if (NULL != c->filename) free(c->filename);
    free(c);
  }
}

void
ShowHelp (int N)
{
  if (0 == N) {
    fprintf (stdout, "Short help:\n"
"  Using: translator.csh {old_format_file} {new_format_file}\n\n"
"    The program translates information from a file in the old CompHEP\n"
"    (41.10 and earler or 4.2p1) format to a file in the new CompHEP\n"
"     format (4.2.0 and later)\n");
  }

  if (1 == N) {
    fprintf (stdout, "Short help:\n"
"  Using: translator.csh {old_format_file} {new_format_file}\n\n"
"    The program translates information from a file in the old CompHEP\n"
"    (41.10 and earler or 4.2p1) format to a file in the new CompHEP\n"
"     format (4.2.0 and later)\n\n"
"    Files in the old format can be processed by cpyth-1.2.x \n"
"    (CompHEP-PYTHIA interface program)\n" 
"    Files in the new format can be processed by cpyth-2.x \n"
"    (CompHEP-PYTHIA interface program) or by the CompHEP-interfaces package.\n");
  }
}
