/* AMIGA2GADGET -- a piece of code to read in AMIGA files and convert to GADGET2 format. 

Compilation flags -->

-DLGADGET -- if GADGET file is in L(ean)-GADGET format, the id() block is 
   integer*8, so need to compile with this flag -- otherwise code 
   could fall over if you attempt to sort particles by their IDs. 
   As this code stands, it's not crucial -- I read id() in, but I
   don't use it.

-DDEBUG --   prints out (x,y,z,vx,vy,vz) to an ascii file called, 
   surprisingly, ascii.out

   original version (mlapm2gadget) by Chris Power (cpower@astro.swin.edu.au)
   adapted for AMIGA by Alexander Knebe (aknebe@aip.de)
   further modified by Arman Khalatyan (akhalatyan@aip.de)


       NOTE:  the output file will be GADGET2 file format !

   */

//#define DEBUG

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <math.h>


#include "../src/param.h"
#include "../src/tdef.h"
#include "../src/common.c"

#include "../src/libutility/utility.h"

#define RHOCRIT      27.755      /* Critical density at z=0 in GADGET units */
#define G            43.09       /* Gravitational constant in GADGET units */
#define GADGET_MUNIT 1.0e10      /* GADGET mass unit (10^10 Msol/h)        */

#define int4bytes int

struct io_header_1
{
   int npart[6];
   double mass[6];
   double time;
   double redshift;
   int flag_sfr;
   int flag_feedback;
   int npartTotal[6];
   int flag_cooling;
   int num_files;
   double BoxSize;
   double Omega0;
   double OmegaLambda;
   double HubbleParam;
   int flag_multiphase;
   int flag_stellarage;
   int flag_sfrhistogram;
   int flag_metals;
   int flag_decouple;
   int flag_effmodel;
   char fill[72];                /* fills to 256 Bytes */
}header1;




struct particle_data 
{
   float     Pos[3];       /* particle position at its current time */  
   float     Vel[3];       /* particle velocity at its current time */  
#ifdef LGADGET
   long long ID;           /* unique particle identifier */  
#else
   int       ID;
#endif
   float     Mass;
   int Type;
} *P;

int massflag;             /* Are the masses contained in the header? */

void write_GADGET (char *fname);
void read_AMIGA   (char *fname);

int main(int argc, char **argv)
{
   char infile[132],outfile[132];
   
   if(argc!=3) 
     {
      printf("Usage: %s <file.amiga> <file.gdt>\n",argv[0]);
      exit(0);
     } 
   else 
     {
      sprintf(infile,"%s",argv[1]);
      sprintf(outfile,"%s",argv[2]);
     }
   
   read_AMIGA(infile);
   write_GADGET(outfile);
   
}

size_t my_fwrite(void *ptr, size_t size, size_t nmemb, FILE * stream)
{
   size_t nwrite;
   
   if((nwrite = fwrite(ptr, size, nmemb, stream)) != nmemb)
     {
      printf("I/O error (fwrite) !\n");
      exit(4);
     }
   return nwrite;
}

void swap_Nbyte(char *data,int n,int m)
{
   return;
}

int write_gadget_head(struct io_header_1 *myhead,FILE *fd)
/* int read_gadget_head(int *npart,double *massarr,double *time,double *redshift,FILE *fd)*/
{
   int blocksize;
   char label[5]={"    "};
   int bl;
   /*-----------*/   
   strcpy(label,"HEAD");
   bl=8;
   swap_Nbyte((char*)&bl,1,4);
   my_fwrite(&bl, sizeof(int4bytes), 1, fd);
   my_fwrite(label, 4*sizeof(char), 1, fd);
   bl=256+8;
   swap_Nbyte((char*)&bl,1,4);
   my_fwrite(&bl, sizeof(int4bytes), 1, fd);
   bl=8;
   swap_Nbyte((char*)&bl,1,4);
   my_fwrite(&bl, sizeof(int4bytes), 1, fd);
   /*-----------*/
   bl=256;
   swap_Nbyte((char*)&bl,1,4);
   my_fwrite(&bl, sizeof(int4bytes), 1, fd);
   
   swap_Nbyte((char*)myhead->npart,6,4);           my_fwrite((void*)myhead->npart,6*sizeof(int), 1, fd);
   swap_Nbyte((char*)myhead->mass,6,8);            my_fwrite((void*)myhead->mass,6*sizeof(double), 1, fd);
   swap_Nbyte((char*)&myhead->time,1,8);           my_fwrite((void*)&myhead->time,sizeof(double), 1, fd);
   swap_Nbyte((char*)&myhead->redshift,1,8);       my_fwrite((void*)&myhead->redshift,sizeof(double), 1, fd);
   swap_Nbyte((char*)&myhead->flag_sfr,1,4);       my_fwrite((void*)&myhead->flag_sfr,sizeof(int), 1, fd);
   swap_Nbyte((char*)&myhead->flag_feedback,1,4);  my_fwrite((void*)&myhead->flag_feedback,sizeof(int), 1, fd);
   swap_Nbyte((char*)myhead->npartTotal,6,4);      my_fwrite((void*)myhead->npartTotal,6*sizeof(int), 1, fd);
   swap_Nbyte((char*)&myhead->flag_cooling,1,4);   my_fwrite((void*)&myhead->flag_cooling,sizeof(int), 1, fd);
   swap_Nbyte((char*)&myhead->num_files,1,4);      my_fwrite((void*)&myhead->num_files,sizeof(int), 1, fd);
   swap_Nbyte((char*)&myhead->BoxSize,1,8);        my_fwrite((void*)&myhead->BoxSize,sizeof(double), 1, fd);
   swap_Nbyte((char*)&myhead->Omega0,1,8);         my_fwrite((void*)&myhead->Omega0,sizeof(double), 1, fd);
   swap_Nbyte((char*)&myhead->OmegaLambda,1,8);    my_fwrite((void*)&myhead->OmegaLambda,sizeof(double), 1, fd);
   swap_Nbyte((char*)&myhead->HubbleParam,1,8);    my_fwrite((void*)&myhead->HubbleParam,sizeof(double), 1, fd);
   swap_Nbyte((char*)&myhead->flag_multiphase,1,4);my_fwrite((void*)&myhead->flag_multiphase,sizeof(int), 1, fd);
   swap_Nbyte((char*)&myhead->flag_stellarage,1,4);    my_fwrite((void*)&myhead->flag_stellarage,sizeof(int), 1, fd);
   swap_Nbyte((char*)&myhead->flag_sfrhistogram,1,4);  my_fwrite((void*)&myhead->flag_sfrhistogram,sizeof(int), 1, fd);
   swap_Nbyte((char*)&myhead->flag_metals,1,4);        my_fwrite((void*)&myhead->flag_metals,sizeof(int), 1, fd);
   swap_Nbyte((char*)&myhead->flag_decouple,1,4);      my_fwrite((void*)&myhead->flag_decouple,sizeof(int), 1, fd);
   swap_Nbyte((char*)&myhead->flag_effmodel,1,4);      my_fwrite((void*)&myhead->flag_effmodel,sizeof(int), 1, fd);
   my_fwrite((void*)myhead->fill,72*sizeof(char), 1, fd);
   
   my_fwrite(&bl, sizeof(int4bytes), 1, fd);
   
   
   return(blocksize);
}

#define RECLEN fwrite(&blklen,sizeof(int),1,fd);

void write_GAD_BLOCKNAME(char *name,int blsize,  FILE *fd)
{
   printf("Writing: |%c%c%c%c| len= %d\n", name[0], name[1],name[2],name[3], blsize);
   int blklen=8;/*blklen= 1int + 4char */
      
      fwrite(&blklen,sizeof(int),1,fd);
      
      fwrite(&name[0],sizeof(char),4,fd);
      blsize+=8;
      fwrite(&blsize,sizeof(int),1,fd);
      fwrite(&blklen,sizeof(int),1,fd);
      
}
void write_GADGET(char *fname) 
{
   
   
   
   FILE *fd;             /* File handle */
   
   int i,j,k;            /* Dummy variables */
   int blklen;           /* Dummy variable -- denotes record length so files
      are readable as a series of FORTRAN blocks */
   
   int NumPart;      /* Number of particles in simulation */
   float dummy[3];   /* Dummy float variable, for reading (x,y,z,vx,vy,vz,m) */
   char blname[21];
   float *fv;
   int *iv,npartwithMass, iType,mass_offset;
   long long *llv, l;
   int np;
   memcpy(&blname[0],"HEADPOS VEL ID  MASS", 21);
   
   /******Make particle Types*********/
   NumPart=header1.npart[1];
   
   
   npartwithMass=0;   
   for(i=0;i<6;i++) 
      header1.npart[i]=0;
   header1.mass[1]= P[i].Mass;
   iType=1;
   for(i=0;i<NumPart;i++)
     {       
      if(header1.mass[iType]== P[i].Mass)
        {
         header1.npart[iType]++;
        }else
          {
           if(iType<3)
             {
              iType++;
              header1.mass[iType]= P[i].Mass;
              header1.npart[iType]++;
             }else
               {
                header1.mass[iType]=0;
                header1.npart[iType]++;
                npartwithMass++;
               }	    
          }
      
     }
   for(i=0;i<6;i++) 
      header1.npartTotal[i]=header1.npart[i];
   /***************/
   
   if((fd=fopen(fname,"w")))
     {
      
      for(i=0;i<6;i++) 
         printf("np[%d]=%9d\t nall[%d]=%9d\t massarr[%d]=%g\n",i,header1.npart[i],i,header1.npartTotal[i],i,header1.mass[i]);
      
      printf("\nExpansion factor: %lf\t Redshift: %lf\n",header1.time,header1.redshift);
      printf("BoxSize (Mpc/h): %lf\n",header1.BoxSize);
      printf("Omega0: %lf\t OmegaLambda: %lf \t HubbleParam: %lf\n",header1.Omega0,header1.OmegaLambda,header1.HubbleParam);
      
      blklen=sizeof(header1);
      
      write_gadget_head(&header1,fd);
      
      NumPart=0;
      
      for(i=0;i<6;i++)
         NumPart+=header1.npartTotal[i];
      
      printf("\n\nWriting %d particles to %s...\n\n",NumPart,fname);
      
      blklen=3*NumPart*sizeof(float);
      write_GAD_BLOCKNAME( &blname[4],blklen,  fd);
      
      fv=malloc(blklen);
      np=0l;
      for(i=0;i<NumPart;i++)
        {
         
         fv[np]=P[i].Pos[0];
         fv[np+1]=P[i].Pos[1];
         fv[np+2]=P[i].Pos[2];
         np+=3;
         //fprintf(stderr, "%f %f %f \n",fv[i], fv[i+1], fv[i+2] );
        }
      RECLEN;
      fwrite(&fv[0],sizeof(float),NumPart*3,fd);
      RECLEN;
      printf(">>>>>>>>NumPart=%d blklen=%d <<<<<<<\n", NumPart, blklen);
      
      printf("\nFinished writing positions\n");
      write_GAD_BLOCKNAME( &blname[8],blklen,  fd);
      np=0l;
      for(i=0;i<NumPart;i++)
        {
         
         fv[np]=P[i].Vel[0];
         fv[np+1]=P[i].Vel[1];
         fv[np+2]=P[i].Vel[2];
         np+=3;
        }
      RECLEN;
      my_fwrite((char*)fv,blklen,1,fd);
      RECLEN;      
      printf("Finished writing velocities\n");
      free(fv);
#ifdef LGADGET      
      blklen=NumPart*sizeof(long long);
      write_GAD_BLOCKNAME( &blname[12],blklen,  fd);
      llv=(long long*)malloc(blklen);
      for(l=0;l<NumPart;l++)
        {
         llv[l]=(long long)(l+1);
        }
      
      RECLEN;
      my_fwrite((char*)llv,blklen,1,fd);
      RECLEN;
      free(llv);
#else
      blklen=NumPart*sizeof(int);
      write_GAD_BLOCKNAME( &blname[12],blklen,  fd);
      iv=(int*)malloc(blklen);
      
      for(i=0;i<NumPart;i++)
        {
         iv[i]=(int)(i+1);
         
        }
      RECLEN;
      my_fwrite((char*)iv,blklen,1,fd);
      RECLEN;
      free(iv);
#endif
      printf("Finished writing identities\n");
      
      
      
      mass_offset=0;
      for(i=0;i<6;i++) 
         if(header1.npart[i]>0 && header1.mass[i]>0)
            mass_offset+=header1.npart[i];
      printf("mass_offset=%d npartwithMass=%d\n", mass_offset, npartwithMass);
      if(npartwithMass!=0)
        {
         blklen=npartwithMass*sizeof(float);
         write_GAD_BLOCKNAME( &blname[16],blklen,  fd);
         fv=(float*)malloc(blklen);
         for(i=mass_offset-1;i<npartwithMass;i++)
           {
            fv[i]=P[i].Mass;	      
           }
         RECLEN;
         my_fwrite((char*)fv,blklen,1,fd);
         RECLEN;
        }                  
     }
here:         
      fclose(fd);
}


void read_AMIGA(char *fname) 
{
   FILE              *fd;                 /* File handle */
#ifdef DEBUG
   FILE              *fpout;
#endif
   int               NumPart;             /* Number of particles */
   int               i, j, k;                   /* Dummy variable */
   double            dw[7];
   float             fw[7];           /* Weightings */
   double            x_fac, v_fac, m_fac; /* Conversion factors */
   int               BUFSIZE, SWAPBYTES;
   int          nw[8];
   
   
   
   if((fd=fopen(fname,"r")))
     {
      /* read AMIGA header */
      read_amiga_header(fd, &io, &SWAPBYTES);
      
      printf(" NumPart       = %ld\n",io.header.no_part);
      printf(" BoxSize       = %lf\n",io.header.boxsize);
      printf(" Omega0        = %lf\n",io.header.omega0);
      printf(" OmegaLambda   = %lf\n",io.header.lambda0);
      printf(" Particle mass = %g\n",io.header.pmass);
      printf(" z_current     = %lf (a=%lf)\n",1.0/io.header.a_current-1.0,io.header.a_current);
      
      /* allocating memory for particles */
      if(!(P=malloc(io.header.no_part*sizeof(struct particle_data))))
        {
         perror("\n\nFailed to allocate memory for data\n");
         exit(1);
        }
      
      for(i=0;i<6;i++) 
         header1.npart[i]=header1.npartTotal[i]=header1.mass[i]=0;
      
      printf("\nNOTE: assuming single particle mass -- writing this to GADGET file header\n");
      
      /** Assume single mass simulation --> species type 2 **/
      massflag             = 1;
      header1.npart[1]     = header1.npartTotal[1]=io.header.no_part;
      header1.mass[1]      = io.header.pmass/GADGET_MUNIT;
      header1.time         = io.header.a_current;  
      header1.redshift     = (1.0/io.header.a_current)-1.0;
      header1.BoxSize      = io.header.boxsize/GADGET_LUNIT; 
      header1.Omega0       = io.header.omega0; 
      header1.OmegaLambda  = io.header.lambda0;
      
      
      x_fac  = io.header.boxsize/GADGET_LUNIT;
      v_fac  = H0*io.header.boxsize/(io.header.a_current*sqrt(io.header.a_current));
      m_fac  = io.header.pmass/GADGET_MUNIT;
      printf("x_fac=%f v_fac=%f m_fac=%f  \n", x_fac, v_fac, m_fac); 
      printf("\n Reading particle data...");
      
#ifdef DEBUG	 
      fpout=fopen("ascii.out","w");
#endif      
      
      if(io.header.multi_mass == 1)
         BUFSIZE = 7;
      else
         BUFSIZE = 6;
      
      if(io.header.double_precision)
        {
         for(i = 0; i < io.header.no_part; i++)
           {
            for(j=0; j<BUFSIZE; j++)
              {
               ReadDouble(fd,&(dw[j]),SWAPBYTES);
              }
            
            P[i].Pos[0] = dw[0] * x_fac;
            P[i].Pos[1] = dw[2] * x_fac;
            P[i].Pos[2] = dw[4] * x_fac;
            
            P[i].Vel[0] = dw[1] * v_fac;
            P[i].Vel[1] = dw[3] * v_fac;
            P[i].Vel[2] = dw[5] * v_fac;
            
            
            if(io.header.multi_mass)
               P[i].Mass   = dw[6] * m_fac;
            
            
#ifdef DEBUG	 
            fprintf(stderr,"%g %g %g %g %g %g\n",dw[0],dw[2],dw[4],dw[1],dw[3],dw[5]);
#endif	 
            
            
           }
        }
      else
        {
         for(i = 0; i < io.header.no_part; i++)
           {
            for(j=0; j<BUFSIZE; j++)
              {
               ReadFloat(fd,&(fw[j]),SWAPBYTES);
              }
            
            P[i].Pos[0] = fw[0] * x_fac;
            P[i].Pos[1] = fw[2] * x_fac;
            P[i].Pos[2] = fw[4] * x_fac;
            
            P[i].Vel[0] = fw[1] * v_fac;
            P[i].Vel[1] = fw[3] * v_fac;
            P[i].Vel[2] = fw[5] * v_fac;
            
            
            if(io.header.multi_mass)
               P[i].Mass   = fw[6] * m_fac;
            
            
#ifdef DEBUG	 
            fprintf(stderr,"%g %g %g %g %g %g\n",fw[0],fw[2],fw[4],fw[1],fw[3],fw[5]);
#endif	 
            
           }
      
      fclose(fd);

      printf("done\n");
      fprintf(stderr,"\n       reading of AMIGA data done\n");
      fprintf(stderr,"==========================================\n\n");

#ifdef DEBUG
      fclose(fpout);
#endif
       }
     }
      else
        {
         fprintf(stderr,"could not open %s\n",fname);
        }
}
