#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "allvars.h"
#include "proto.h"

void init(int index)
{
   // MESH SIZE
   int local_grid_size_x;// = 8;
   int local_grid_size_y;// = 8;
   
   // set the local size of the image
   local_grid_size_x = grid_size_x;
   nsectors = NUM_OF_SECTORS;
   if (nsectors < 0) nsectors = size;
   local_grid_size_y = grid_size_y/nsectors;
   //nsectors = size;

   // LOCAL grid size
   xaxis = local_grid_size_x;
   yaxis = local_grid_size_y;

   clock_gettime(CLOCK_MONOTONIC, &begin);
   start = clock();

   // INPUT FILES (only the first ndatasets entries are used)
   strcpy(datapath,param.datapath_multi[index]);
   sprintf(num_buf, "%d", index);
   
   //Changing the output file names
   op_filename();
             
   // Read metadata
   fileName(datapath, in.metafile);
   readMetaData(filename);

   // Local Calculation
   metaData_calculation();

   // Allocate Data Buffer
   allocate_memory();
  
   // Reading Data
   readData();
 
   #ifdef USE_MPI
         MPI_Barrier(MPI_COMM_WORLD);
   #endif

   clock_gettime(CLOCK_MONOTONIC, &finish);
   end = clock();
   timing.setup_time = ((double) (end - start)) / CLOCKS_PER_SEC;
   timing.setup_time1 = (finish.tv_sec - begin.tv_sec);
   timing.setup_time1 += (finish.tv_nsec - begin.tv_nsec) / 1000000000.0;
 
}

void op_filename() {

   
   strcpy(buf, num_buf);
   strcat(buf, outparam.outfile);
   strcpy(out.outfile, buf);
   
   strcpy(buf, num_buf);
   strcat(buf, outparam.outfile1);
   strcpy(out.outfile1, buf); 

   strcpy(buf, num_buf);
   strcat(buf, outparam.outfile2);
   strcpy(out.outfile2, buf);

   strcpy(buf, num_buf);
   strcat(buf, outparam.outfile3);
   strcpy(out.outfile3, buf);

   strcpy(buf, num_buf);
   strcat(buf, outparam.fftfile);
   strcpy(out.fftfile, buf);

   strcpy(buf, num_buf);
   strcat(buf, outparam.fftfile2);
   strcpy(out.fftfile2, buf);

   strcpy(buf, num_buf);
   strcat(buf, outparam.fftfile3);
   strcpy(out.fftfile3, buf);
    
   strcpy(buf, num_buf);
   strcat(buf, outparam.logfile);
   strcpy(out.logfile, buf);

   strcpy(buf, num_buf);
   strcat(buf, outparam.extension);
   strcpy(out.extension, buf);

   strcpy(buf, num_buf);
   strcat(buf, outparam.timingfile);
   strcpy(out.timingfile, buf);

}

void read_parameter_file(char *fname)
{
   if(rank == 0)
   {
   	if(file.pFile = fopen (fname,"r"))
   	{
      		char buf1[30], buf2[100], buf3[30], num[30];
                int i = 1;
      		while(fscanf(file.pFile, "%s" "%s", buf1, buf2) != EOF)
      		{
       			if(strcmp(buf1, "num_threads") == 0)
        		{
           			param.num_threads = atoi(buf2);
        		}
        		if(strcmp(buf1, "Datapath1") == 0)
       			{
            			strcpy(param.datapath_multi[0], buf2);
 				i++;
        		}
        		if(strcmp(buf1, "ndatasets") == 0)
        		{
           			param.ndatasets = atoi(buf2);
        		}
        		if(strcmp(buf1, "ufile") == 0)
        		{
            			strcpy(in.ufile, buf2);
        		}
        		if(strcmp(buf1, "vfile") == 0)
        		{
            			strcpy(in.vfile, buf2);
        		}
        		if(strcmp(buf1, "wfile") == 0)
        		{
            			strcpy(in.wfile, buf2);
        		}
        		if(strcmp(buf1, "weightsfile") == 0)
        		{
            			strcpy(in.weightsfile, buf2);
        		}
        		if(strcmp(buf1, "visrealfile") == 0)
        		{
           			 strcpy(in.visrealfile, buf2);
        		}
        		if(strcmp(buf1, "visimgfile") == 0)
        		{
            			strcpy(in.visimgfile, buf2);
        		}
        		if(strcmp(buf1, "metafile") == 0)
        		{
            			strcpy(in.metafile, buf2);
        		}
                        if(param.ndatasets > 1)
                        {
                   
                            sprintf(num, "%d", i);
                            strcat(strcpy(buf3,"Datapath"),num);
                            if(strcmp(buf1,buf3) == 0)
                            {
   				strcpy(param.datapath_multi[i-1], buf2);
                                i++;
                            } 
                        }
      		}
      		fclose(file.pFile);
      
       }
       else
       {
     		printf("error opening paramfile");
     		exit(1);
       }
    }
   
    /* Communicating the relevent parameters to the other process */

    MPI_Bcast(&in, sizeof(struct ip), MPI_BYTE, 0, MPI_COMM_WORLD);
    MPI_Bcast(&param, sizeof(struct parameter), MPI_BYTE, 0, MPI_COMM_WORLD);
}
void fileName(char datapath[900], char file[30]) {
     strcpy(filename,datapath);
     strcat(filename,file);
}

void readMetaData(char fileLocal[1000]) {

     if(rank == 0) 
     {
        if(file.pFile = fopen (fileLocal,"r"))
        {
     		fscanf(file.pFile,"%ld",&metaData.Nmeasures);
     		fscanf(file.pFile,"%ld",&metaData.Nvis);
     		fscanf(file.pFile,"%ld",&metaData.freq_per_chan);
     		fscanf(file.pFile,"%ld",&metaData.polarisations);
     		fscanf(file.pFile,"%ld",&metaData.Ntimes);
     		fscanf(file.pFile,"%lf",&metaData.dt);
     		fscanf(file.pFile,"%lf",&metaData.thours);
	     	fscanf(file.pFile,"%ld",&metaData.baselines);
     		fscanf(file.pFile,"%lf",&metaData.uvmin);
     		fscanf(file.pFile,"%lf",&metaData.uvmax);
     		fscanf(file.pFile,"%lf",&metaData.wmin);
     		fscanf(file.pFile,"%lf",&metaData.wmax);
     		fclose(file.pFile);
        } 
        else
        {
                printf("error opening meta file");
                exit(1);
        }
     }
      
      /* Communicating the relevent parameters to the other process */

      MPI_Bcast(&metaData, sizeof(struct meta), MPI_BYTE, 0, MPI_COMM_WORLD);
}

void metaData_calculation() {
   
     int nsub = 1000;
     printf("Subtracting last %d measurements\n",nsub);
     metaData.Nmeasures = metaData.Nmeasures-nsub;
     metaData.Nvis = metaData.Nmeasures*metaData.freq_per_chan*metaData.polarisations;

     // calculate the coordinates of the center
     double uvshift = metaData.uvmin/(metaData.uvmax-metaData.uvmin);

     if (rank == 0)
     {
          printf("N. measurements %ld\n",metaData.Nmeasures);
          printf("N. visibilities %ld\n",metaData.Nvis);
     }

     // Set temporary local size of points
     long nm_pe = (long)(metaData.Nmeasures/size);
     long remaining = metaData.Nmeasures%size;

     startrow = rank*nm_pe;
     if (rank == size-1)nm_pe = nm_pe+remaining;

     long Nmeasures_tot = metaData.Nmeasures;
     metaData.Nmeasures = nm_pe;
     long Nvis_tot = metaData.Nvis;
     metaData.Nvis = metaData.Nmeasures*metaData.freq_per_chan*metaData.polarisations;
     metaData.Nweights = metaData.Nmeasures*metaData.polarisations;

     #ifdef VERBOSE
          printf("N. measurements on %d %ld\n",rank,metaData.Nmeasures);
          printf("N. visibilities on %d %ld\n",rank,metaData.Nvis);
     #endif

}

void allocate_memory() {


     // DAV: all these arrays can be allocatate statically for the sake of optimization. However be careful that if MPI is used
     // all the sizes are rescaled by the number of MPI tasks
     //  Allocate arrays
     
     data.uu = (double*) calloc(metaData.Nmeasures,sizeof(double));
     data.vv = (double*) calloc(metaData.Nmeasures,sizeof(double));
     data.ww = (double*) calloc(metaData.Nmeasures,sizeof(double));
     data.weights = (float*) calloc(metaData.Nweights,sizeof(float));
     data.visreal = (float*) calloc(metaData.Nvis,sizeof(float));
     data.visimg = (float*) calloc(metaData.Nvis,sizeof(float));

}

void readData() {

    if(rank == 0) 
    {

         printf("READING DATA\n");

         fileName(datapath, in.ufile);
         if(file.pFile = fopen (filename,"rb"))
         {
         	fseek (file.pFile,startrow*sizeof(double),SEEK_SET);
         	fread(data.uu,metaData.Nmeasures*sizeof(double),1,file.pFile);
         	fclose(file.pFile);
         }
         else
         {
                printf("error opening ucoord file");
                exit(1);
         }

         fileName(datapath, in.vfile);
         if(file.pFile = fopen (filename,"rb"))
         {
         	fseek (file.pFile,startrow*sizeof(double),SEEK_SET);
         	fread(data.vv,metaData.Nmeasures*sizeof(double),1,file.pFile);
         	fclose(file.pFile);
         }
         else
         {
                printf("error opening vcoord file");
                exit(1);
         }

         fileName(datapath, in.wfile);
         if(file.pFile = fopen (filename,"rb"))
         {
         	fseek (file.pFile,startrow*sizeof(double),SEEK_SET);
         	fread(data.ww,metaData.Nmeasures*sizeof(double),1,file.pFile);
         	fclose(file.pFile);
         }
         else
         {
                printf("error opening wcoord file");
                exit(1);
         }

         fileName(datapath, in.weightsfile);
         if(file.pFile = fopen (filename,"rb"))
         {
         	fseek (file.pFile,startrow*metaData.polarisations*sizeof(float),SEEK_SET);
         	fread(data.weights,(metaData.Nweights)*sizeof(float),1,file.pFile);
         	fclose(file.pFile);
         }
         else
         {
                printf("error opening weights file");
                exit(1);
         }

         fileName(datapath, in.visrealfile);
         if(file.pFile = fopen (filename,"rb"))
         {
         	fseek (file.pFile,startrow*metaData.freq_per_chan*metaData.polarisations*sizeof(float),SEEK_SET);
         	fread(data.visreal,metaData.Nvis*sizeof(float),1,file.pFile);
         	fclose(file.pFile);
         }
         else
         {
                printf("error opening visibilities_real file");
                exit(1);
         }

         fileName(datapath, in.visimgfile);
         if(file.pFile = fopen (filename,"rb"))
         {
         	fseek (file.pFile,startrow*metaData.freq_per_chan*metaData.polarisations*sizeof(float),SEEK_SET);
         	fread(data.visimg,metaData.Nvis*sizeof(float),1,file.pFile);
         	fclose(file.pFile);
         }
         else
         {
                printf("error opening visibilities_img file");
                exit(1);
         }
     
     }

     #ifdef USE_MPI
        MPI_Barrier(MPI_COMM_WORLD);
     #endif

     /* Communicating the relevent parameters to the other process */
     
     MPI_Bcast(data.uu, metaData.Nmeasures * sizeof(double), MPI_BYTE, 0, MPI_COMM_WORLD);
     MPI_Bcast(data.vv, metaData.Nmeasures * sizeof(double), MPI_BYTE, 0, MPI_COMM_WORLD);
     MPI_Bcast(data.ww, metaData.Nmeasures * sizeof(double), MPI_BYTE, 0, MPI_COMM_WORLD);
     MPI_Bcast(data.weights, metaData.Nweights * sizeof(float), MPI_BYTE, 0, MPI_COMM_WORLD);
     MPI_Bcast(data.visreal, metaData.Nvis * sizeof(float), MPI_BYTE, 0, MPI_COMM_WORLD);
     MPI_Bcast(data.visimg, metaData.Nvis * sizeof(float), MPI_BYTE, 0, MPI_COMM_WORLD);
}
