#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <dirent.h>
#include <mpi.h>
#include <math.h>
#include "SDL.h"
#include "SDL_image.h"
#include "SDL/SDL_rotozoom.h"

const int FORMILLISEC=2000;
const int offset=25;

int VIDEO_HEIGHT=640;
int VIDEO_WIDTH=480;

/**
 * Structure created to maintain the list of file
 */
struct ImageFile{
   char filename[1024];
   struct ImageFile *nextImage;
};

/**
 * This function checks wheter the filename is a supported file by the SDL library
 */
int isImagefile(char *filename){
   SDL_RWops * src= SDL_RWFromFile(filename, "rb");
   int isimage= (IMG_isBMP(src) || IMG_isPNM(src) || SDLCALL IMG_isXPM(src) || IMG_isXCF(src) 
   ||IMG_isPCX(src) || IMG_isGIF(src) || IMG_isJPG(src) || IMG_isTIF(src) 
   || IMG_isPNG(src) || IMG_isLBM(src));
   SDL_FreeRW(src);
   return(isimage);
}

/**
 * This function will fill the list of filename with the image files. If the i/p name is not a 
 * file it will be ignored. If the i/p name is a directory then all the files inside the directory 
 * will be listed in the array. Also the file is chekced at the same time wheter it is supported or
 * not by the library SDL.
 */
struct ImageFile * listoffiles(int argc,char **argv){
   int i;
   DIR *dp;

   struct dirent *ep;
   struct stat curr_stat;
   struct ImageFile *curr_image;
   struct ImageFile *imageList;
   char temp[1024];
   curr_image=NULL;
   imageList=NULL;

   for(i=1;i<argc;i++){
      if(!stat(argv[i],&curr_stat)){
            // In case its a direcotry
            if(S_ISDIR(curr_stat.st_mode)){
               dp = opendir (argv[i]);
               if (dp != NULL){
                  while ((ep = readdir (dp))!=NULL ){
		     strcpy(temp,argv[i]);
                     //If directory name specified withouth seperator then add a seperator 
                     //before appending the filename.
                     if(argv[i][strlen(argv[i])-1]!='/') strcat(temp,"/");
                     strcat(temp,ep->d_name);
                     if(!isImagefile(temp)) continue;
                     if(imageList==NULL){
                        imageList=malloc(sizeof(struct ImageFile));
                        strcpy(imageList->filename,argv[i]);
                        strcat(imageList->filename,ep->d_name);
                        imageList->nextImage=NULL;
                        curr_image=imageList;
                     }
                     else{
                        curr_image->nextImage= malloc(sizeof(struct ImageFile));
                        strcpy(curr_image->nextImage->filename,argv[i]);
                        strcat(curr_image->nextImage->filename,ep->d_name);
                        curr_image->nextImage->nextImage=NULL;
                        curr_image=curr_image->nextImage;
                     }
                  }
               (void) closedir (dp);
               }
               else
                  printf ("Couldn't open the directory %s\n",argv[i]);
            } 
            else{
               // In case its just a file
               if(!isImagefile(argv[i])) continue;
               if(imageList==NULL){
                  imageList= malloc(sizeof(struct ImageFile));
                  strcpy(imageList->filename,argv[i]);
                  imageList->nextImage=NULL;
                  curr_image=imageList;
               }
               else{
                  curr_image->nextImage= malloc(sizeof(struct ImageFile));
                  strcpy(curr_image->nextImage->filename,argv[i]);
                  curr_image->nextImage->nextImage=NULL;
                  curr_image=curr_image->nextImage;
               }
            }
      }
      else
         printf("Prolem encountered with %s. Trying to skip it ...\n",argv[i]);
   }
   return(imageList);
}

/**
 * Memory freeing is done here + initlialized mode is shoutdown
 */
void garbagecollect(struct ImageFile *imageList){
   struct ImageFile *temp;
   while(imageList!=NULL){
      temp=imageList;
      imageList=imageList->nextImage;
      free(temp);
   }
   SDL_Quit();
   atexit(SDL_Quit);
   MPI_Finalize();
}

/**
 * Initliaization stuff goes here ...
 * Returns 0 if something goes wrong else +ve value
 */
int init(){
   SDL_Surface *image_panel;
   if(SDL_Init(SDL_INIT_VIDEO)<0) { 
      printf("Could not initialize SDL %s\n",SDL_GetError());
      exit(-1);
   }
   SDL_ShowCursor(0);
   /**********************************************************************************
    * Getting the maximum resolution possible
    *********************************************************************************/
   int max=0,i;
   SDL_Rect **modes;
   /* Get available fullscreen/hardware modes */
   modes=SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE);
   /* Check is there are any modes available */
   if(modes == (SDL_Rect **)0){
      printf("No modes available!\n");
      exit(-1);
   }
   /* Check if or resolution is restricted */
   if(modes == (SDL_Rect **)-1){
      printf("All resolutions available.\n");
   }
   for(i=0;modes[i];i++)
      if(modes[max]->w*modes[max]->h<modes[i]->w*modes[i]->h) max=i;

   VIDEO_HEIGHT=modes[max]->h;
   VIDEO_WIDTH=modes[max]->w;
   /***********************************************************************************/
   int isVideo = SDL_VideoModeOK(VIDEO_WIDTH,VIDEO_HEIGHT,32,SDL_FULLSCREEN);
   if(!isVideo){
      printf("Cannot init video mode\n");
      SDL_Quit(); 
      atexit(SDL_Quit);
      MPI_Finalize();
      exit(-1);
   }
   return(isVideo);
}

/**
 * This function will display the image for required number of second as set in the 
 * global variable 
 */
void totiledisplay(  struct ImageFile *curr_image,SDL_Surface *image_panel,int rank){
   SDL_Rect rect;
   SDL_Surface *temp_surface,*zoom_image;
   double xfactor,yfactor,img_aspect_ratio,scr_aspect_ratio;
   double chkwidth, chkheight;

   zoom_image=IMG_Load(curr_image->filename);
   img_aspect_ratio = (zoom_image->w)/(zoom_image->h);
   scr_aspect_ratio = (VIDEO_WIDTH)/(VIDEO_HEIGHT);

   if(img_aspect_ratio==scr_aspect_ratio){
      if(rank==0){
         xfactor=(double)zoom_image->w/(double)(VIDEO_WIDTH);
         yfactor=(double)zoom_image->h/(double)(VIDEO_HEIGHT);
      }
      else {
         xfactor=(double)zoom_image->w/((double)(VIDEO_WIDTH*2)+2*offset);
         yfactor=(double)zoom_image->h/((double)(VIDEO_HEIGHT*2)+2*offset);
      }
   }
   else{
      if(rank == 0){
      // Dimensions have to be given statically :(
      chkwidth = abs((zoom_image->w)-1280*2+(2*offset));
      chkheight = abs((zoom_image->h)-1024*2+(2*offset));
      }
      else {
      chkwidth = abs((zoom_image->w)-((VIDEO_WIDTH*2))+2*offset);
      chkheight = abs((zoom_image->h)-((VIDEO_HEIGHT*2))+2*offset);
      }

      if(chkwidth < chkheight){
         if(rank==0){
            xfactor=(double)zoom_image->w/(double)(VIDEO_WIDTH);
            yfactor=xfactor;
         }
         else {
            xfactor=(double)zoom_image->w/((double)(VIDEO_WIDTH*2)+2*offset);
            yfactor=xfactor;
         }
      }
      else {
         if(rank==0){
            yfactor=(double)zoom_image->h/(double)(VIDEO_HEIGHT);
            xfactor=yfactor;
         }
         else{
            yfactor=(double)zoom_image->h/((double)(VIDEO_HEIGHT*2)+2*offset);
            xfactor=yfactor;
         }
      }
   }
   temp_surface=zoomSurface(zoom_image,(double)1/xfactor,(double)1/yfactor,SMOOTHING_ON);
   MPI_Barrier(MPI_COMM_WORLD);
   image_panel = SDL_SetVideoMode(VIDEO_WIDTH, VIDEO_HEIGHT, temp_surface->format->BitsPerPixel,SDL_FULLSCREEN | SDL_HWSURFACE);
   switch(rank){
      case 0:
         rect.x=0;
         rect.y=0;
         rect.w=(temp_surface->w);
         rect.h=(temp_surface->h);
         SDL_BlitSurface(temp_surface,0,image_panel,0);
         break;
      case 1:
         rect.x=0;
         rect.y=0;
         rect.w=(VIDEO_WIDTH)+offset;rect.h=(VIDEO_HEIGHT)+offset;
         SDL_BlitSurface(temp_surface,&rect,image_panel,0);
         break;
      case 2:
         rect.x=(VIDEO_WIDTH)+(2*offset);rect.y=0;
         rect.w=(VIDEO_WIDTH);rect.h=(VIDEO_HEIGHT)+offset;
         SDL_BlitSurface(temp_surface,&rect,image_panel,0);
         break;
      case 3:
         rect.x=(VIDEO_WIDTH)+(2*offset);rect.y=(VIDEO_HEIGHT)+(2*offset);
         rect.w=(VIDEO_WIDTH);rect.h=(VIDEO_HEIGHT);
         SDL_BlitSurface(temp_surface,&rect,image_panel,0);
         break;
      case 4:
         rect.x=0;rect.y=(VIDEO_HEIGHT)+(2*offset);
         rect.w=(VIDEO_WIDTH);rect.h=(VIDEO_HEIGHT);
         SDL_BlitSurface(temp_surface,&rect,image_panel,0);
         break;
   }
   MPI_Barrier(MPI_COMM_WORLD);
   SDL_Flip(image_panel);
}


/**
 * The main function starts from here
 */
int main(int argc,char **argv){

  /*********************************************
   * Initilizing the MPI 
   **********************************************/
   char proc_name[MPI_MAX_PROCESSOR_NAME],buf[255];
   int rank,nameLength,nproc;
   MPI_Status *status;
   MPI_Init(&argc,&argv);
   MPI_Comm_rank(MPI_COMM_WORLD,&rank);
   MPI_Comm_size(MPI_COMM_WORLD,&nproc);   
   MPI_Get_processor_name(proc_name, &nameLength);
   /**********************************************/

   /*********************************************
    * Setting the environment variable
    *********************************************/
   if(rank!=0){                                          //works weiredly on different machines
      sprintf(buf,":0.0",proc_name);                    //in redhat "%s:0.0" is the value
      setenv("DISPLAY",buf, 1);                         //in CentOS ":0.0" works eventhough its default var value
   }
   /**********************************************/

   struct ImageFile *imageList,*browse=NULL;
   SDL_Surface *image_panel=NULL;
   int done=0;SDL_Event event;

   if(argc<=1){
      //wrong argument print where the command got executed
      if(rank==0)
         printf("Usage: %s <dir-name-of-image>/<list of image files to tile>\n",argv[0]);
      exit(0);
   }

   init();  //No need to chk return value as neways it would exit and clean up if anything goes wrong

   imageList=listoffiles(argc,argv);
   if(imageList==NULL){
      garbagecollect(NULL);
      exit(0);
   }
   browse=imageList;

   while(!done){
      if(browse!=NULL){
         totiledisplay(browse,image_panel,rank);
         MPI_Barrier(MPI_COMM_WORLD);
         browse=browse->nextImage;
      }
      while(SDL_PollEvent( &event )) /* Loop until there are no events left on the queue */
         switch(event.type){
            case SDL_KEYDOWN:
            case SDL_QUIT:
               // quit events, exit the event loop 
               done=1;
               break;
            default:
               break;
         }	
      if(rank==0){
         //Notify others of the change
         MPI_Send(&done,1,MPI_INT,1,1,MPI_COMM_WORLD);
         MPI_Send(&done,1,MPI_INT,2,1,MPI_COMM_WORLD);
         MPI_Send(&done,1,MPI_INT,3,1,MPI_COMM_WORLD);
         MPI_Send(&done,1,MPI_INT,4,1,MPI_COMM_WORLD);
      }
      if(rank!=0)
         //rank 0 sayin to others about that its done with
         MPI_Recv(&done,1,MPI_INT,0,1,MPI_COMM_WORLD,status);
      if(done==0)
         SDL_Delay(FORMILLISEC);
   }
   garbagecollect(NULL);
   exit(0);
}
