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

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

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

/* define your own colour table here... */
/*                     2    4    8    16   32   64  128  256  512  1024  2048   4096  8192  16384  32768  65536  131072 262144  54288  1048576  2097152 */
double r_table[11] = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8,  0.9,  1.0};
double g_table[11] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.6, 0.7, 0.8,  0.9,  1.0};
double b_table[11] = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8,  0.9,  1.0};
double calc_T(double e);


/* NOTE: the stereo2-feature is designed to work with hydro-simulations! */
//#define STEREO2

/* selects only a slice of thickness B/10. in the x-y-plane with z=B/2 +- B/20. */
//#define SLICE


struct {
   double DMdens;
   double gasdens;
   double T;
} maximum;

struct {
   double DMdens;
   double gasdens;
   double T;
} minimum;

/*=======================================================================================
 * read the full quad information from file and convert to human/stereo2 readable format
 *=======================================================================================*/
int main(argc,argv)
int argc;
char **argv;
{
   char          indata[MAXSTRING], outdata[MAXSTRING], outdata_DMdens[MAXSTRING], outdata_gasdens[MAXSTRING], outdata_T[MAXSTRING];
   FILE         *fpin, *fpout, *fpout_DMdens, *fpout_gasdens, *fpout_T;
   gridls       *cur_grid;
   pqptr         cur_pquad;
   cqptr         cur_cquad, icur_cquad;
   nqptr         cur_nquad, icur_nquad;
   nptr          cur_node;
   nptr          tsc_nodes[3][3][3];
   double        divB;
   long          x, y, z;
   long          no_pquad, no_cquad, no_nquad;
   long          ipquad, icquad, inquad;
   double        max_dens, r, g, b;
   long          l1dim;
   double        T, e;
   int           i, j, k, slen, SWAPBYTES;
   
   if(argc<2)
     {
      fprintf(stderr,"Use: %s AMIGA.grid_file\n",*argv);
      exit(1);
     }
   
   printf("====================================================\n");
   printf("  Read AMIGA grid binary and convert to ASCII file\n");
   printf("====================================================\n");
   
   strcpy(indata,argv[1]);
#ifdef STEREO2
   strcpy(outdata_DMdens,  indata);
   strcpy(outdata_gasdens, indata);
   strcpy(outdata_T,       indata);
   strcat(outdata_DMdens,  ".DMdens");
   strcat(outdata_gasdens, ".gasdens");
   strcat(outdata_T,       ".T");
   fprintf(stderr,"writing STEREO2 file: %s\n",   outdata_DMdens);
   fprintf(stderr,"writing STEREO2 file: %s\n",   outdata_gasdens);
   fprintf(stderr,"writing STEREO2 file: %s\n\n", outdata_T);
   if((fpout_DMdens=fopen(outdata_DMdens,"w"))==NULL)
     {
      printf("I cannot open %s\n", outdata_DMdens);
      exit(1);
     }   
   if((fpout_gasdens=fopen(outdata_gasdens,"w"))==NULL)
     {
      printf("I cannot open %s\n", outdata_gasdens);
      exit(1);
     }
   if((fpout_T=fopen(outdata_T,"w"))==NULL)
     {
      printf("I cannot open %s\n", outdata_T);
      exit(1);
     }
#else
   strcpy(outdata,indata);
   strcat(outdata,".ascii");
   fprintf(stderr,"writing ASCII file: %s\n\n",outdata);
   if((fpout=fopen(outdata,"w"))==NULL)
     {
      printf("I cannot open %s\n", outdata);
      exit(1);
     }
#endif

	  
   if((fpin=fopen(indata,"rb"))==NULL)
     {
      printf("I cannot open %s\n", argv[1]);
      exit(1);
     }
   
   
   
   /*======================================================================
    * initialize some values
    *======================================================================*/
   slen = strlen(indata);
   k    = slen-9;
   sscanf(indata+k,"%ld",&l1dim);
   
   cur_grid        = (gridls *) calloc(1, sizeof(gridls));
   cur_grid->l1dim = l1dim;
   max_dens        = -10000;
   r               =     pow(log2(cur_grid->l1dim)/log2(MAX_L1DIM), 0.1);
   g               =     pow(log2(cur_grid->l1dim)/log2(MAX_L1DIM), 1.0);
   b               = 1.0-pow(1.0-log2(cur_grid->l1dim)/log2(MAX_L1DIM), 0.1);
   
   
   
   /*======================================================================
    * read in io.header from file
    *======================================================================*/
   read_amiga_header(fpin, &io, &SWAPBYTES);


   /* print some information */
   printf("some information about input file:\n");
   printf("==================================\n");
   printf("%s\n\n",io.header.header);
   printf("multi-mass       = %d\n",io.header.multi_mass);
   printf("double precision = %d\n",io.header.double_precision);
   printf("no_part          = %ld\n",io.header.no_part);
   printf("no_vpart         = %g\n",io.header.no_vpart);
   printf("boxsize          = %g\n",io.header.boxsize);
   printf("omega0           = %g\n",io.header.omega0);
   printf("omegab           = %g\n",io.header.omegab);
   printf("lambda0          = %g\n",io.header.lambda0);   
   printf("pmass            = %g\n",io.header.pmass);   
   printf("gamma            = %g\n",io.header.gamma);
   printf("H_frac           = %g\n",io.header.H_frac);
   printf("T_init           = %g\n",io.header.T_init);
   printf("z_initial        = %g (a=%lf)\n",
          1.0/io.header.a_initial-1.0,io.header.a_initial);
   printf("z_current        = %g (a=%lf)\n",
          1.0/io.header.a_current-1.0,io.header.a_current);
   printf("no_timestep      = %d\n",io.header.no_timestep);

   
   /*======================================================================
    * read in QUAD hierarchy from file
    *======================================================================*/
   ReadLong(fpin,&no_pquad,SWAPBYTES);
   if(no_pquad == 0)
      cur_grid->pquad = NULL;
   else
      cur_grid->pquad = c_pquad(1);
   cur_pquad = cur_grid->pquad;
   
   /* loop over linked pquads */
   maximum.DMdens  = -1E30;
   maximum.gasdens = -1E30;
   maximum.T       = -1E30;
   minimum.DMdens  = +1E30;
   minimum.gasdens = +1E30;
   minimum.T       = +1E30;
   
   for(ipquad=0; ipquad<no_pquad; ipquad++)
     {
      ReadLong(fpin, &cur_pquad->z,      SWAPBYTES);
      ReadLong(fpin, &cur_pquad->length, SWAPBYTES);
      
      cur_pquad->loc = c_cquad(cur_pquad->length);
      
      for(cur_cquad = cur_pquad->loc, z = cur_pquad->z; 
          cur_cquad < cur_pquad->loc + cur_pquad->length;
          cur_cquad++, z++)
        {
         ReadLong(fpin, &no_cquad, SWAPBYTES);
         icur_cquad = cur_cquad;
         
         /* loop over linked cquads */
         for(icquad=0; icquad<no_cquad; icquad++)
           {
            ReadLong(fpin, &icur_cquad->y,      SWAPBYTES);
            ReadLong(fpin, &icur_cquad->length, SWAPBYTES);
            
            icur_cquad->loc = c_nquad(icur_cquad->length);
            
            for(cur_nquad = icur_cquad->loc, y = icur_cquad->y;
                cur_nquad < icur_cquad->loc + icur_cquad->length; 
                cur_nquad++, y++)
              {
               ReadLong(fpin, &no_nquad, SWAPBYTES);
               icur_nquad = cur_nquad;
               
               /* loop over linked nquads */
               for(inquad=0; inquad<no_nquad; inquad++)
                 {
                  ReadLong(fpin, &icur_nquad->x,      SWAPBYTES);
                  ReadLong(fpin, &icur_nquad->length, SWAPBYTES);
                  
                  icur_nquad->loc = c_node(icur_nquad->length);
                  
                  for(cur_node = icur_nquad->loc, x = icur_nquad->x;
                      cur_node < icur_nquad->loc + icur_nquad->length; 
                      cur_node++, x++)
                    {
                     if(io.header.double_precision)
                       {
                        ReadDouble(fpin, (double *) &cur_node->dens, SWAPBYTES);
                        
#ifdef HYDRO
                        for(i=0; i<NHYDRO; i++)
                           ReadDouble(fpin, (double *) &(cur_node->u[i]), SWAPBYTES);
#ifdef MHD
                        for(i=0; i<NDIM; i++)
                           ReadDouble(fpin, (double *) &(cur_node->B[i]), SWAPBYTES);
#endif
#endif
                       }
                     else
                       {
                        ReadFloat(fpin, (float *) &cur_node->dens, SWAPBYTES);

#ifdef HYDRO
                        for(i=0; i<NHYDRO; i++)
                           ReadFloat(fpin, (float *) &(cur_node->u[i]), SWAPBYTES);
#ifdef MHD
                        for(i=0; i<NDIM; i++)
                           ReadFloat(fpin, (float *) &(cur_node->B[i]), SWAPBYTES);
#endif
#endif
                       }

                     /* tranform density contrast to density */
                     cur_node->dens += 1.0;
                    
                     /* calculate extrema */
                     maximum.DMdens  = MAX(maximum.DMdens,   cur_node->dens);
                     minimum.DMdens  = MIN(minimum.DMdens,   cur_node->dens);
                     
                     if(io.header.omegab > MACHINE_ZERO)
                       {
                        e                = cur_node->u[Uedens]/cur_node->u[Udens];
                        T                = calc_T(e) * pow2(H0*io.header.boxsize/io.header.a_current);
                        maximum.gasdens  = MAX(maximum.gasdens,  cur_node->u[Udens]);
                        minimum.gasdens  = MIN(minimum.gasdens,  cur_node->u[Udens]);
                        maximum.T        = MAX(maximum.T,        T);
                        minimum.T        = MIN(minimum.T,        T);
                       }
                    }
                  
                  icur_nquad->next = c_nquad(1);
                  icur_nquad       = icur_nquad->next;
                  
                 }
              }
            
            icur_cquad->next = c_cquad(1);
            icur_cquad       = icur_cquad->next;
            
           }
        }
      
      cur_pquad->next = c_pquad(1);
      cur_pquad       = cur_pquad->next;
      
     }
      
   fclose(fpin);

   
#ifdef STEREO2
   fprintf(stderr,"\n");
   fprintf(stderr,"min/max DMdens   = %16.8g %16.8g\n", minimum.DMdens,   maximum.DMdens);
   fprintf(stderr,"min/max gasdens  = %16.8g %16.8g\n", minimum.gasdens,  maximum.gasdens);
   fprintf(stderr,"min/max T        = %16.8g %16.8g\n", minimum.T,        maximum.T);
#else
#ifdef MHD
   fprintf(fpout,"#   x(1)      y(2)    z(3)           dens(4)       momdensX(5)      momdensY(6)      momdensZ(7)          Edens(8)       entropy(9)         edens(10)         DMdens(11)                Bx(12)           By(13)           Bz(14)         divB(15)                T(16)\n");
#else // MHD
   fprintf(fpout,"#    x(1)     y(2)     z(3)        dens(4)         momdensX(5)      momdensY(6)      momdensZ(7)       Edens(8)         entropy(9)        edens(10)           DMdens(11)        T(12)\n");
#endif // MHD
#endif // STEREO2
   
   /*======================================================================
    * write relevant information to ASCII file
    *======================================================================*/
   for(cur_pquad=cur_grid->pquad; cur_pquad!=NULL; cur_pquad=cur_pquad->next)
     {
      for(cur_cquad = cur_pquad->loc, z = cur_pquad->z; 
          cur_cquad < cur_pquad->loc + cur_pquad->length;
          cur_cquad++, z++)
        {
         for(icur_cquad=cur_cquad; icur_cquad!=NULL; icur_cquad=icur_cquad->next)
           {
            for(cur_nquad = icur_cquad->loc, y = icur_cquad->y;
                cur_nquad < icur_cquad->loc + icur_cquad->length; 
                cur_nquad++, y++)
              {
               for(icur_nquad=cur_nquad; icur_nquad!=NULL; icur_nquad=icur_nquad->next)
                 {
                  for(cur_node = icur_nquad->loc, x = icur_nquad->x;
                      cur_node < icur_nquad->loc + icur_nquad->length; 
                      cur_node++, x++)
                     
                    {
#ifdef SLICE
                     if( (double)z/(double)cur_grid->l1dim * io.header.boxsize >=  9. * io.header.boxsize/20. &&
                         (double)z/(double)cur_grid->l1dim * io.header.boxsize <= 11. * io.header.boxsize/20.    )
#endif
                       {
#ifdef STEREO2
                        /* dark matter density */
                        r = log10((cur_node->dens-minimum.DMdens)+1)/log10(maximum.DMdens-minimum.DMdens+1.);
                        g = log10((cur_node->dens-minimum.DMdens)+1)/log10(maximum.DMdens-minimum.DMdens+1.);
                        b = 0.1;
                        
                        fprintf(fpout_DMdens,"P %g %g %g    %g %g %g    4\n",
                                ((double)(x+0.5)/(double)(cur_grid->l1dim))*io.header.boxsize,
                                ((double)(y+0.5)/(double)(cur_grid->l1dim))*io.header.boxsize,
                                ((double)(z+0.5)/(double)(cur_grid->l1dim))*io.header.boxsize,
                                r,g,b);
                        
                        
                        /* gas density */
                        r = log10((cur_node->u[Udens]-minimum.gasdens)+1)/log10(maximum.gasdens-minimum.gasdens+1.);
                        g = 0.1;
                        b = log10((cur_node->u[Udens]-minimum.gasdens)+1)/log10(maximum.gasdens-minimum.gasdens+1.);

                        /* same colour-map as for dark matter density */
//                        r = log10((cur_node->u[Udens]-minimum.DMdens)+1)/log10(maximum.DMdens-minimum.DMdens+1.);
//                        g = 0.1;
//                        b = log10((cur_node->u[Udens]-minimum.DMdens)+1)/log10(maximum.DMdens-minimum.DMdens+1.);
                        
                        fprintf(fpout_gasdens,"P %g %g %g    %g %g %g    4\n",
                                ((double)(x+0.5)/(double)(cur_grid->l1dim))*io.header.boxsize,
                                ((double)(y+0.5)/(double)(cur_grid->l1dim))*io.header.boxsize,
                                ((double)(z+0.5)/(double)(cur_grid->l1dim))*io.header.boxsize,
                                r,g,b);
                        
                        /* temperature */
                        e = cur_node->u[Uedens]/cur_node->u[Udens];
                        T = calc_T(e) * pow2(H0*io.header.boxsize/io.header.a_current);
                        r = log10((T-minimum.T)+1)/log10(maximum.T-minimum.T+1.);
                        g = 0.0;
                        b = 0.0;
                        
                        fprintf(fpout_T,"P %g %g %g    %g %g %g    4\n",
                                ((double)(x+0.5)/(double)(cur_grid->l1dim))*io.header.boxsize,
                                ((double)(y+0.5)/(double)(cur_grid->l1dim))*io.header.boxsize,
                                ((double)(z+0.5)/(double)(cur_grid->l1dim))*io.header.boxsize,
                                r,g,b);
                        
#else /* STEREO2 */
                          
#ifdef MHD
                        // get div B:
                          
                        tsc_nodes[1][1][1] = cur_node;
                        get_TSCnodes(cur_grid, cur_pquad, icur_cquad, icur_nquad, tsc_nodes, &z, &y, &x);
                        if(test_tsc(tsc_nodes)==TRUE)
                           divB =  ( tsc_nodes[1][1][2]->B[X] - cur_node->B[X] )
                                 + ( tsc_nodes[1][2][1]->B[Y] - cur_node->B[Y] )
                                 + ( tsc_nodes[2][1][1]->B[Z] - cur_node->B[Z] );
                        else
                           divB = 0.;  
                          
                        
                        if(io.header.omegab > MACHINE_ZERO)
                          {
                           e = cur_node->u[Uedens]/cur_node->u[Udens];
                           T = calc_T(e) * pow2(H0*io.header.boxsize/io.header.a_current);
                           fprintf(fpout,"%8ld %8ld %8ld  %16.8g  %16.8g %16.8g %16.8g  %16.8g %16.8g  %16.8g   %16.8g      %16.8g %16.8g %16.8g %16.8g     %16.8g\n",
                                   x,y,z,
                                   cur_node->u[Udens], 
                                   cur_node->u[UmomdensX], cur_node->u[UmomdensY], cur_node->u[UmomdensZ],
                                   cur_node->u[UEdens],
                                   cur_node->u[Untrpy],
                                   cur_node->u[Uedens],
                                   cur_node->dens,
                                   cur_node->B[X], cur_node->B[Y], cur_node->B[Z],
                                   divB,
                                   T);
                          }
                        else
                          {
                           fprintf(fpout,"%ld %ld %ld %f\n",x,y,z,cur_node->dens);
                          }
                          
#else // MHD //
                          
                          if(io.header.omegab > MACHINE_ZERO)
                          {
                             e = cur_node->u[Uedens]/cur_node->u[Udens];
                             T = calc_T(e) * pow2(H0*io.header.boxsize/io.header.a_current);
                             fprintf(fpout,"%8ld %8ld %8ld  %16.8g  %16.8g %16.8g %16.8g  %16.8g %16.8g  %16.8g   %16.8g     %16.8g\n",
                                     x,y,z,
                                     cur_node->u[Udens], 
                                     cur_node->u[UmomdensX], cur_node->u[UmomdensY], cur_node->u[UmomdensZ],
                                     cur_node->u[UEdens],
                                     cur_node->u[Untrpy],
                                     cur_node->u[Uedens],
                                     cur_node->dens,
                                     T);
                          }
                          else
                          {
                             fprintf(fpout,"%ld %ld %ld %f\n",x,y,z,cur_node->dens);
                          }
                          
#endif // MHD //
                        
#endif /* STEREO2 */
                       }  
                    }
                 }
              }
           }
        }
     }
   
}

