/*
 * cement.c  adds uchar image into total.pdm (double) file
 * steve; from ndf's diff.c, with help of sbeck on parsing input images
 */

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#include <assert.h>
#include "cement.h"
#include "pnmutils.h"


int verbose=0;

inline double createValue(double a,double p_inv);
void usage(char * string);
void parse_commandline(int argc, char ** argv,
                       struct image_params * a_params,
                       char ** output_filename);



int main(int argc, char ** argv)
{
    struct image_params a_params={NULL,0,0,0,0,0.0};
    char * dest_filename=NULL;
    FILE * a, * dest;
    double p=EXPONENT;  /* exponent for pow */
    double p_inv;  /* 1.0/p */
    double        a_pixel[ARRAY_SIZE];
    unsigned char out_pixel[ARRAY_SIZE];
    int i,j;
    int channels_per_pixel;

    /* Timing measurments */
    struct timeval timeValStart;
    struct timeval timeValEnd;
    struct timezone timeZone;
    int minutes;
    double seconds=0.0;
#ifdef GET_TIME_MEASUREMENTS
    struct timeval runTimeStart;
    struct timeval runTimeStop;
    double fileReadTime=0.0;
    double fileWriteTime=0.0;
    double computeTime=0.0;
#endif
    
    unsigned int pixels=0;
    unsigned int numberOfPixels;
    
    p_inv=1.0/p;

    parse_commandline(argc, argv, &a_params, &dest_filename);

    if ((a=fopen(a_params.filename, "r"))==NULL)
    {
        fprintf(stderr, "Unable to open %s.\n", a_params.filename);
        exit(EXIT_FAILURE);
    }

    if (dest_filename==NULL)
        dest=stdout;
    else
    {
        if ((dest=fopen(dest_filename, "w"))==NULL)
        {
            fprintf(stderr, "Unable to open %s.\n", dest_filename);
            exit(EXIT_FAILURE);
        }
    }

    get_image_type(a, &a_params);

    if (a_params.type=='A')
    {
        channels_per_pixel=3;
        fprintf(dest, "P%c\n", '6');
        get_image_params_preserve_comments(a,&a_params,dest);
    }
    else
    {
        fprintf(stderr, "Portable Lightspace map (pldm) must be type A and it is type %c\n",a_params.type);
        exit(EXIT_FAILURE);
    }

    if ((a_params.exponent!=(float)EXPONENT))
    {
        fprintf(stderr, "Lightspace must have the same exponent.\n");
        exit(EXIT_FAILURE);
    }

    fprintf(dest, "# %s %s %s %s \n",argv[0],argv[1],argv[2],argv[3]);
    fprintf(dest, "%d %d\n%d\n", a_params.width,
            a_params.height, a_params.max_val);

    printf("Generate ppm file: \"%s\", ",dest_filename);
    printf("P%c, %dx%d, Channels_per_pixel: %d ",'6',a_params.width,a_params.height,channels_per_pixel);

    printf("from Lightspace file: \"%s\", ",a_params.filename);
    printf("P%c\n",a_params.type);


    /* Start Timer */
    if(gettimeofday(&timeValStart,&timeZone)==-1)
    {
        fprintf(stderr,"Error getting the time\n");
        exit(0);
    }

    //    while (!feof(a))

    numberOfPixels=a_params.width*a_params.height;

    if(((float)numberOfPixels/NUMBER_PIXELS_PER_READ)!=(int)((float)numberOfPixels/NUMBER_PIXELS_PER_READ))
    {
        if(verbose)
            printf("\nERROR: The number of pixels %d is not evenly divisible by %d. ",numberOfPixels,NUMBER_PIXELS_PER_READ);

        numberOfPixels=(int)((float)numberOfPixels/NUMBER_PIXELS_PER_READ)*NUMBER_PIXELS_PER_READ;
        if(verbose)
            printf("We will go up to %d pixels.\n",numberOfPixels);
    }


    
    for(i=0;i<numberOfPixels;i+=NUMBER_PIXELS_PER_READ)
    {
        if(i%(3*NUMBER_PIXELS_PER_READ)==0)
        {
            printf("  %5.2f%% done.\r",(double)100*i/numberOfPixels);
            fflush(stdout);
        }

        
#ifdef GET_TIME_MEASUREMENTS
        gettimeofday(&runTimeStart,&timeZone);
#endif

        fread(a_pixel, sizeof(double), ARRAY_SIZE, a);

#ifdef GET_TIME_MEASUREMENTS
        gettimeofday(&runTimeStop,&timeZone);

        seconds=(double)runTimeStop.tv_sec+(double)runTimeStop.tv_usec/1000000;
        seconds-=(double)runTimeStart.tv_sec+(double)runTimeStart.tv_usec/1000000;
        fileReadTime+=seconds;

        gettimeofday(&runTimeStart,&timeZone);
#endif
        
        for(j=0;j<ARRAY_SIZE;j+=3)
        {
            out_pixel[j]=(unsigned char) createValue(a_pixel[j],p_inv);
            out_pixel[j+1]=(unsigned char) createValue(a_pixel[j+1],p_inv);
            out_pixel[j+2]=(unsigned char) createValue(a_pixel[j+2],p_inv);
        }

#ifdef GET_TIME_MEASUREMENTS
        gettimeofday(&runTimeStop,&timeZone);

        seconds=(double)runTimeStop.tv_sec+(double)runTimeStop.tv_usec/1000000;
        seconds-=(double)runTimeStart.tv_sec+(double)runTimeStart.tv_usec/1000000;

        computeTime+=seconds;

        gettimeofday(&runTimeStart,&timeZone);
#endif
        
        fwrite(out_pixel, sizeof(unsigned char), ARRAY_SIZE, dest);

#ifdef GET_TIME_MEASUREMENTS
        gettimeofday(&runTimeStop,&timeZone);

        seconds=(double)runTimeStop.tv_sec+(double)runTimeStop.tv_usec/1000000;
        seconds-=(double)runTimeStart.tv_sec+(double)runTimeStart.tv_usec/1000000;
        fileWriteTime+=seconds;
#endif
        
    }

    if(numberOfPixels!=a_params.width*a_params.height)
    {
        if(verbose)
            printf("Cleanup on pixels %d to %d\n",numberOfPixels,a_params.width*a_params.height);

        for(i=numberOfPixels;i<a_params.width*a_params.height;i++)
        {

            fread(a_pixel, sizeof(double),CHANNELS_PER_PIXEL, a);

            out_pixel[0]=(unsigned char) createValue(a_pixel[0],p_inv);
            out_pixel[1]=(unsigned char) createValue(a_pixel[1],p_inv);
            out_pixel[2]=(unsigned char) createValue(a_pixel[2],p_inv);
            fwrite(out_pixel, sizeof(unsigned char), CHANNELS_PER_PIXEL, dest);
            pixels++;

        }
    }

    /* End timer */
    if(gettimeofday(&timeValEnd,&timeZone)==-1)
    {
        printf("Error getting the time\n");
        exit(0);
    }

    pixels=i;

//    if(!feof(a))
//        printf("\nExtra Pixels?\n");
        
    while (!feof(a))
    {
        fread(a_pixel, sizeof(double),CHANNELS_PER_PIXEL, a);
        if(feof(a))
            break;
        
        out_pixel[0]=(unsigned char) createValue(a_pixel[0],p_inv);
        out_pixel[1]=(unsigned char) createValue(a_pixel[1],p_inv);
        out_pixel[2]=(unsigned char) createValue(a_pixel[2],p_inv);
        printf("ExtraPixels A: %f %f %f\t",a_pixel[0],a_pixel[1],a_pixel[2]);
        printf("OUT: %d %d %d\n",out_pixel[0],out_pixel[1],out_pixel[2]);
        fwrite(out_pixel, sizeof(unsigned char), CHANNELS_PER_PIXEL, dest);
        pixels++;

        
    }

    fclose(a);
    fclose(dest);

//    printf("\nTime measurements\n");
    seconds=(double)timeValEnd.tv_sec+(double)timeValEnd.tv_usec/1000000;
    seconds-=(double)timeValStart.tv_sec+(double)timeValStart.tv_usec/1000000;

#ifdef GET_TIME_MEASUREMENTS
    printf("%f s doing File Read %f%%\n",fileReadTime,100*fileReadTime/seconds);
    printf("%f s doing File Writes %f%%\n",fileWriteTime,100*fileWriteTime/seconds);
    printf("%f s doing Computation %f%%\n",computeTime,100*computeTime/seconds);
    printf("%f s doing Timing and looping\n",seconds-fileReadTime-fileWriteTime-computeTime);
#endif
    
    if(seconds>60)
    {
        minutes=seconds/60;
        seconds=seconds-minutes*60;
    }
    else
        minutes=0;

    if(verbose)
    {
        if(minutes==1)
            printf("Run Time is about %d minute %f seconds.\n",minutes,seconds);
        else
            printf("Run Time is about %d minutes %f seconds.\n",minutes,seconds);
    }
    else
	printf("\n");

    exit(EXIT_SUCCESS);
}

double powLookup[256];

void initilizeLookup(double p)
{
    int i;

    for(i=0;i<256;i++)
        powLookup[i]=pow((double)i,p);
}

inline double createValue(double a, double p_inv)
{
    return(pow(a,p_inv));
}

void usage(char * string)
{
  fprintf(stderr, "Use: %s [LightSpaceFile] -o [ImageSpaceFile]\n",string);
  fprintf(stderr, "\t[LightSpaceFile]\t-A plsm (portable light space map) file that will be created.\n");
  fprintf(stderr, "\t[ImageSpaceFile]\t-A ppm file that will form the start of a lightspace.\n");
  exit(EXIT_SUCCESS);
}

void parse_commandline(int argc, char ** argv,
                       struct image_params * a_params,
                       char ** output_filename)
{
    int i;

    if (argc<3)
        usage(argv[0]);

    for (i=1;i<argc;i++)
    {
        if (!strcasecmp(argv[i], "-o"))
        {
            if ((i+1)>=argc)
                usage(argv[0]); /* e.g. if last argument not output file */
            *output_filename=(char *)strdup(argv[++i]);
        }
        else if (a_params->filename==NULL)
            a_params->filename=(char *)strdup(argv[i]); /* if file a still null */
    }

    if ((a_params->filename==NULL) || (*output_filename==NULL))
        usage(argv[0]);
}

