/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                            R E A D M O P A C . C                             *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: readmopac.c,v 1.5 2004/08/29 15:01:27 jrh Exp $
* $Log: readmopac.c,v $
* Revision 1.5  2004/08/29 15:01:27  jrh
* Release 2.4.1
*
* Revision 1.4  2003/11/07 11:14:38  jrh
* Release 2.4
*
* Revision 1.3  2000/12/10 15:15:38  jrh
* Release 2.3
*
* Revision 1.2  1999/05/24 01:27:21  jrh
* Release 2.2.1
*
* Revision 1.1  1999/02/07 21:56:01  jrh
* Initial revision
*
*/
#include<ctype.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/stat.h>
#include "isotopes.h"

#define MAXLENLINE 132
#define TRUE  1
#define FALSE 0

int main(int, char **);
int fileLength(char *);
int swapInt(int *);
double swapDouble(double *);
int passInt(int *);
double passDouble(double *);
void makeLower(char *line);

extern void *getmem(size_t, size_t);
extern void *expmem(void *, size_t, size_t);
extern void fremem(void **);
extern void eof(char *, char *, int);

int main(int argc, char **argv)
{
  FILE *file;
  double (*getDouble)(double *);
  double x, y, z, *dp, *coord=NULL, *wn=NULL, *cnm=NULL;
  double *s, *p, *d, *mos=NULL, *e=NULL;
  int (*getInt)(int *);
  int *ip, dof=0, last, count=0, hasSymLabels=FALSE;
  int numat=0, norbs=0, nelecs=0, *nfirst=NULL, *nlast, *nat;
  int isVamp=FALSE;
  char line[MAXLENLINE], f[MAXLENLINE], *word, *buffer, *sym=NULL;
  register int i=0, j, k;

  if (argc != 2) eof("noFile", "", 1);
  strcpy(f, argv[1]);
  if ((word=strrchr(f, '.')) != NULL)
  {
    strcpy(word, ".gpt");
    if ((k=fileLength(f)) > 0)
    {
      file=fopen(f, "r");
      buffer=(char *)getmem((size_t)k, sizeof(char));
      read(fileno(file), (void *)buffer, (size_t)k);
      fclose(file);
      ip=(int *)buffer;
      if (*ip > 1000000)
      {
        getInt=swapInt;
        getDouble=swapDouble;
      }
      else
      {
        getInt=passInt;
        getDouble=passDouble;
      }
      ip++;
      numat=(*getInt)(ip++);
      coord=(double *)getmem((size_t)(6*numat), sizeof(double));
      s=coord+3*numat;
      p=s+numat;
      d=p+numat;
      nfirst=(int *)getmem((size_t)(3*numat), sizeof(int));
      nlast=nfirst+numat;
      nat=nlast+numat;
      norbs=(*getInt)(ip++);
      nelecs=(*getInt)(ip++);
      dp=(double *)ip;
      for (i=0; i<3; i++)
      {
        for (j=0; j<numat; j++)
          coord[numat*i+j]=(*getDouble)(dp++);
      }
      ip=(int *)dp;
      ip+=2*numat+4;
      dp=(double *)ip;
      for (i=0; i<numat; i++) s[i]=(*getDouble)(dp++);
      for (i=0; i<numat; i++) p[i]=(*getDouble)(dp++);
      for (i=0; i<numat; i++) d[i]=(*getDouble)(dp++);
      ip=(int *)dp;
      for (i=0; i<numat; i++)
      {
        nat[i]=(*getInt)(ip++);
        nat[i]--;
      }
      ip+=2;
      dp=(double *)ip;
      if ((long)dp % 8)
      {
        memmove(ip-1, dp, *(ip-1));
        dp=(double *)(ip-1);
      }
      mos=(double *)getmem((size_t)(norbs*norbs), sizeof(double));
      for (i=0; i<norbs*norbs; i++) mos[i]=(*getDouble)(dp++);

      printf("$coord 1.0\n");
      for (i=0; i<numat; i++)
      {
        strncpy(line, pse[nat[i]], 2);
        line[2]='\0';
        printf("%22.14f%22.14f%22.14f  %s\n", coord[i], coord[numat+i],
               coord[2*numat+i], line);
      }
      printf("$atoms\n");
      for (i=0; i<numat; i++)
      {
        strncpy(line, pse[nat[i]], 2);
        line[2]='\0';
        printf("%s %d \\\n   basis =%s min\n", line, i+1, line);
      }
      printf("$basis\n*\n");
      for (i=0; i<numat; i++)
      {
        if (nat[i] >= 0)
        {
          strncpy(line, pse[nat[i]], 2);
          line[2]='\0';
          printf("%s min\n*\n", line);
          printf("   1  s\n%15.6f 1.0\n", s[i]);
          if (p[i] != 0.0) printf("   1  p\n%15.6f 1.0\n", p[i]);
          if (d[i] != 0.0) printf("   1  d\n%15.6f 1.0\n", d[i]);
          printf("*\n");
          for (j=i+1; j<numat; j++)
            if (nat[j] == nat[i]) nat[j]=(-1);
        }
      }
    }
  }

  if ((file=fopen(argv[1], "r")) == NULL) eof("noFile", argv[1], 1);

  while (fgets(line, MAXLENLINE, file) != NULL)
  {
    makeLower(line);
    if (strstr(line, "cartesian coordinates") && coord == NULL)
    {
      if (strstr(line, "<>")) fgets(line, MAXLENLINE, file);
      fgets(line, MAXLENLINE, file);
      fgets(line, MAXLENLINE, file);
      makeLower(line);
      if (strstr(line, "no.       atom               x         y         z"))
      {
        printf("$coord 1.0\n");
        numat=0;
        fgets(line, MAXLENLINE, file);
        fgets(line, MAXLENLINE, file);
        while (line[0] != '\n')
        {
          (void)strtok(line, " \t");
          word=strtok(NULL, " \t");
          x=atof(strtok(NULL, " \t"));
          y=atof(strtok(NULL, " \t"));
          z=atof(strtok(NULL, " \t"));
          printf("%22.14f%22.14f%22.14f  %s\n", x, y, z, word);
          numat++;
          fgets(line, MAXLENLINE, file);
        }
      }
      else if (strstr(line, "no. atom      x        y        z      no. atom      x        y        z") && numat == 0)
      {
	isVamp=TRUE;
        printf("$coord 1.0\n");
        numat=0;
        fgets(line, MAXLENLINE, file);
        fgets(line, MAXLENLINE, file);
        while (line[0] != '\n')
        {
          (void)strtok(line, " \t");
          j=atoi(strtok(NULL, " \t"))-1;
          x=atof(strtok(NULL, " \t"));
          y=atof(strtok(NULL, " \t"));
          z=atof(strtok(NULL, " \t"));
          printf("%22.14f%22.14f%22.14f  %s\n", x, y, z, pse[j]);
          numat++;
          if (strtok(NULL, " \t") != NULL)
	  {
            j=atoi(strtok(NULL, " \t"))-1;
            x=atof(strtok(NULL, " \t"));
            y=atof(strtok(NULL, " \t"));
            z=atof(strtok(NULL, " \t"));
            printf("%22.14f%22.14f%22.14f  %s\n", x, y, z, pse[j]);
            numat++;
            fgets(line, MAXLENLINE, file);
	  }
        }
      }
    }
/*  if (strstr(line, "NO. OF DOUBLY OCCUPIED LEVELS") && norbs == 0)
    {
      norbs=2*atoi(&line[58]);
    } */
    if (strstr(line, "********"))
    {
      count++;
      if (count == 4)
      {
        fgets(line, MAXLENLINE, file);
        do
        {
          fgets(line, MAXLENLINE, file);
        }
        while (strrchr(line, '+'));
        printf("$title\n%s", line);
      }
    }
    else if (strstr(line, "vibrations are the translation"))
    {
      line[14]='\0';
      dof=3*numat-atoi(&line[13]);
    }
    else if (strstr(line, "translations and rotations (cm-1)"))
    {
      isVamp=TRUE;
      fgets(line, MAXLENLINE, file);
      while (!strstr(line, "Vibration        Frequency       Reduced        I.R.       Raman"))
        fgets(line, MAXLENLINE, file);
      fgets(line, MAXLENLINE, file);
      fgets(line, MAXLENLINE, file);
      fgets(line, MAXLENLINE, file);
      fgets(line, MAXLENLINE, file);
      printf("$vibrational spectrum\n");
      dof=0;
      while (line[0] != '\n')
      {
	dof++;
	(void)strtok(line, " \t");
	printf("%s ", strtok(NULL, " \t\n"));
	printf("%s ", strtok(NULL, " \t\n"));
	(void)strtok(NULL, " \t\n");
	printf("%s ", strtok(NULL, " \t\n"));
	printf("%s\n", strtok(NULL, " \t\n"));
        fgets(line, MAXLENLINE, file);
      }
    }
    else if (strstr(line, "normal coordinate analysis"))
    {
      wn=(double *)getmem((size_t)dof, sizeof(double));
      sym=(char *)getmem((size_t)dof, 8*sizeof(char));
      cnm=(double *)getmem((size_t)(3*numat*dof), sizeof(double));
      k=0;
      while (k < dof)
      {
        for (i=0; i<4; i++)
        {
          fgets(line, MAXLENLINE, file);
          if (line[0] == '1') fgets(line, MAXLENLINE, file);
        }
        fgets(line, MAXLENLINE, file);
	makeLower(line);
        if (strstr(line, "root no.")) hasSymLabels=FALSE;
        else                          hasSymLabels=TRUE;
        if (hasSymLabels)
        {
          j=k;
          word=&line[0];
          while (strtok(word, " \t\n") != NULL)
          {
            if (j > dof) break;
            word=NULL;
            strncpy(&sym[8*j++], strtok(word, " \t\n"), 7);
          }
        }
        fgets(line, MAXLENLINE, file);
        fgets(line, MAXLENLINE, file);
        word=&line[0];
        last=k;
        while ((buffer=strtok(word, " \t")) != NULL)
        {
          if (k > dof)
          {
            k--;
            break;
          }
          if (!hasSymLabels) strcpy(&sym[8*k], "A1");
          wn[k++]=atof(buffer);
          word=NULL;
        }
        last=k-last;
        fgets(line, MAXLENLINE, file);
        for (i=0; i<3*numat; i++)
        {
          fgets(line, MAXLENLINE, file);
          if (line[0] == '1' || line[0] == '\n') fgets(line, MAXLENLINE, file);
          word=strtok(line, " \t");
	  if (!isdigit(word[0]))
	  {
            (void)strtok(NULL, " \t");
            (void)strtok(NULL, " \t");
	  }
          for (j=0; j<last; j++)
            cnm[dof*i+k-last+j]=atof(strtok(NULL, " \t"));
        }
      } /* end of "while (k < dof)" */
      printf("$vibrational normal modes\n");
      for (i=1; i<=3*numat; i++)
      {
        printf("%d 1 ", i);
        for (j=1; j<=dof; j++)
        {
          printf("%14.10f", cnm[(j-1)+dof*(i-1)]);
          if (j % 5 == 0) printf("\n%d %d ", i, j);
        }
        printf("\n");
      }
      if (!isVamp)
      {
        printf("$vibrational spectrum\n");
        i=0;
      }
    }
    else if (strstr(line, "t-dipole") && i < dof)
    {
      (void)strtok(line, " \t");
      word=strtok(NULL, " \t");
      printf("%s %f %s 1.0\n", &sym[8*i], wn[i], word);
      i++;
    }
    else if (strstr(line, "eigenvalues") && !strstr(line, "<>"))
    {
      if (norbs == 0) norbs=50;
      e=(double *)getmem((size_t)norbs, sizeof(double));
      fgets(line, MAXLENLINE, file);
      fgets(line, MAXLENLINE, file);
      i=0;
      while (line[0] != '\n')
      {
        word=strtok(line, " \t\n");
        while (word != NULL)
        {
          e[i++]=atof(word);
          if (i >= norbs)
          {
            norbs+=50;
            e=(double *)expmem((void *)e, (size_t)norbs, sizeof(double));
          }
          word=strtok(NULL, " \t\n");
        }
        fgets(line, MAXLENLINE, file);
      }
      norbs=i;
    }
    else if (strstr(line, "  eigenvectors"))
    {
      i=0;
      if (norbs == 0) norbs=50;
      e=(double *)getmem((size_t)norbs, sizeof(double));
      fgets(line, MAXLENLINE, file);
      makeLower(line);
      buffer=NULL;
      while (!buffer)
      {
        while (strstr(line, "root no.") == NULL && (buffer=strstr(line,
               "net atomic charges")) == NULL) fgets(line, MAXLENLINE, file);
        if (!buffer)
          {
          fgets(line, MAXLENLINE, file);
          fgets(line, MAXLENLINE, file);
          word=strtok(line, " \t\n");
          while (word != NULL)
          {
            e[i++]=atof(word);
            if (i >= norbs)
            {
                norbs+=50;
              e=(double *)expmem((void *)e, (size_t)norbs, sizeof(double));
            }
            word=strtok(NULL, " \t\n");
          }
        }
      }
      norbs=i;
    }
  }
  if (mos == NULL)
    k=0;
  else
    k=norbs;
  if (e != NULL)
  {
    printf("$scfmo\n");
    for (i=0; i<norbs; i++)
    {
      printf("%d a1 eigenvalue= %20.15e  nsaos=%d\n", i+1, e[i], k);
      for (j=0; j<k; j++)
      {
        printf(" %19.14e", mos[i*norbs+j]);
        if ((j+1) % 4 == 0) printf("\n");
      }
      if (j % 4 != 0) printf("\n");
    }
  }
  if (nelecs != 0) printf("$closed shells\n a1 1-%d ( 2 )\n", nelecs/2);
  if (coord != NULL) fremem((void *)&coord);
  if (nfirst != NULL) fremem((void *)&nfirst);
  if (wn != NULL) fremem((void *)&wn);
  if (cnm != NULL) fremem((void *)&cnm);
  if (mos != NULL) fremem((void *)&mos);
  if (e != NULL) fremem((void *)&e);
  fclose(file);
  printf("$end\n");
  return(0);
}

int fileLength(char *name)
{
  struct stat filestat;

  if (access(name, R_OK)) return(-1);
  if (stat(name, &filestat))
    return(-1);
  else
    return(filestat.st_size);
}

int swapInt(int *ip)
{
  char x, *cp;

  cp=(char *)ip;
  x=*(cp+3);
  *(cp+3)=*cp;
  *cp=x;
  x=*(cp+2);
  *(cp+2)=*(cp+1);
  *(cp+1)=x;
  return(*ip);

/* DWORD swap_bytes(DWORD n)
{
  return( ((n << 24) & (0xFF000000))
      | ((n << 8)  & (0x00FF0000))
      | ((n >> 8)  & (0x0000FF00))
      | ((n >> 24) & (0x000000FF)) );
} */
/* find endianness
  int i = 1 ;
  little_endian = *((char *) &i ) ; */
}

double swapDouble(double *dp)
{
  char x, *cp;

  cp=(char *)dp;
  x=*(cp+7);
  *(cp+7)=*cp;
  *cp=x;
  x=*(cp+6);
  *(cp+6)=*(cp+1);
  *(cp+1)=x;
  x=*(cp+5);
  *(cp+5)=*(cp+2);
  *(cp+2)=x;
  x=*(cp+4);
  *(cp+4)=*(cp+3);
  *(cp+3)=x;
  return(*dp);
}

int passInt(int *ip)
{
  return(*ip);
}

double passDouble(double *dp)
{
  return(*dp);
}

void makeLower(char *line)
{
  char *p=line;

  while (*p)
  {
    *p=tolower(*p);
    p++;
  }
}
