// Gausspyramid.c
// An adaptation of EdgeDetection.cpp. Originally authored by Alex Kachurin

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#define START_FRAME 12
#define END_FRAME   13

#define MAX_H 10
#define MAX_W 10
#define GAUSS_LEN 5

struct image {
    int **image;
    int    height;
    int    width;
};

typedef struct image image;

void load_image(char *, int *, int *);
void write_image(image *, char *);
void reduce(image *, image *);
void allocate_images();
void free_images();

///////////////////////////////////GLOBALS//////////////////////////////////////
image original,d1,d2,d3;

int height, width;

char src_name[80];
char d1_name[80];
char d2_name[80];
char d3_name[80];

double gauss5[] = {0.05,0.25,0.40,0.25,0.05};
////////////////////////////////////////////////////////////////////////////////

int main(int argc, char *argv[])
{

    int frame;

    for(frame = START_FRAME; frame <= END_FRAME; frame++)
    {
        sprintf(src_name,"garb34rot%d.pgm", frame);
        sprintf(d1_name,"d1\\%s", src_name);
        sprintf(d2_name,"d2\\%s", src_name);
        sprintf(d3_name,"d3\\%s", src_name);

        printf("\nsrc_name = %s\n", src_name);

        load_image(src_name, &height, &width);
        
        reduce(&original, &d1);
        reduce(&d1, &d2);
        reduce(&d2, &d3);

        write_image(&d1, d1_name);
        write_image(&d2, d2_name);
        write_image(&d3, d3_name);
        
        free_images();
    }

    system("pause");
}


void load_image(char *filename, int *height, int *width) 
{
    FILE *input, *out;
    char c;
    char comment[50];
    int x, y, i;
    
    input = fopen(filename, "rb");
    out = fopen("test.pgm", "wb");
    
    c = getc(input); // 'P'
    c = getc(input); // '5'
    
    fscanf(input, " "); // '\n'
    c = fgetc(input); 
    
    while(c == '#') 
    {
        fgets(comment,40,input);
        while(comment[strlen(comment)-1]!='\n') 
        {
            fgets(comment,40,input);
        }
        fscanf(input," ");
        c = fgetc(input);
    }
    
    ungetc(c,input);
    fscanf(input," %d %d ", &original.width, &original.height);
    c = fgetc(input);

    while(c == '#') 
    {
        fgets(comment,40,input);
        while(comment[strlen(comment)-1]!='\n') 
        {
            fgets(comment,40,input);
        }
        fscanf(input," ");
        c = fgetc(input);
    }
    
    ungetc(c,input);
    fscanf(input," %d",&x);
    fgetc(input);

    allocate_images();

    fprintf(out, "P5\n%d %d\n255\n", original.width, original.height);
    
    for(x = 0; x < original.height; x++) 
    {
        for(y = 0; y < original.width; y++) 
        {
            fscanf(input,"%c", &original.image[x][y]);
            fprintf(out, "%c", original.image[x][y]);       
        }
    }
             
    fclose(input);
    fclose(out);
}


void write_image(image *img, char *filename) 
{
    FILE *output, *test;
    int x,y;
    
    output = fopen(filename,"wb");
    test = fopen("test.txt", "w");

        printf("\nimg(width, height) = (%d, %d)\n", img -> width, img -> height);
        
        fprintf(test, "file = %s\n", filename);
        
        fprintf(output,"P5\n%d %d\n255\n", img -> width, img -> height);
        
        for(x = 0; x < img -> height; x++) 
        {
            for(y = 0; y < img -> width; y++) 
            {
                fprintf(output, "%c", img -> image[x][y]);
                if(img -> image[x][y] == 0) fprintf(test, "%d, %d\n", y, x);
            }
        }

    fclose(output);
    fclose(test);
}


void reduce(image *orig, image *reduced)
{
    int shift = GAUSS_LEN/2;
    int j;
    double sum;
    int x, y, y0, x0;
    image temp;
    
//Applying 1D Gaussian to rows
    temp.height = orig -> height;
    temp.width = orig -> width/2;
    
    printf("\norig(width,height) = (%d, %d)\n", orig -> width, orig -> height);
    
    temp.image = malloc( temp.height * sizeof(double*) );
    for(j = 0; j < temp.height; j++)
    {
        temp.image[j] = calloc( temp.width, sizeof(double) );
    } 
    
    for(x = 0; x < orig -> height; x++)
    {
        for(y = 0; y < orig -> width; y+=2)
        {
            sum = 0;
            for(j = 0; j < GAUSS_LEN; j++)
            {
                y0 = y - shift + j;
                
                if(y0 >= 0 && y0 < orig -> width)
                    sum += gauss5[j] * orig -> image[x][y0];
            }
        
            temp.image[x][y/2] = sum;
            
            if(temp.image[x][y/2] > 255) printf("\nfound 1!\n");
            
        }
    }

//Applying 1D Gaussian to columns
    reduced -> height = orig -> height/2;
    reduced -> width = orig -> width/2;

    for(y = 0; y < temp.width; y++)
    {
        for(x = 0; x < temp.height; x += 2)
        {
            sum = 0;
            for(j = 0; j < GAUSS_LEN; j++)
            {
                x0 = x - shift + j;
                if(x0 >= 0 && x0 < temp.height)
                    sum += gauss5[j] * temp.image[x0][y];
            }

            reduced -> image[x/2][y] = sum;
            
            if(reduced -> image[x/2][y] > 255) printf("\nfound 1!\n");
        }
    } 
}

void allocate_images()
{
    int i;
    
    original.image = malloc( original.height * sizeof(int*) );
    d1.image = malloc( original.height * sizeof(int*) );
    d2.image = malloc( original.height * sizeof(int*) );
    d3.image = malloc( original.height * sizeof(int*) );
    
    for(i = 0; i < original.height; i++)
    {
        original.image[i] = calloc( original.width , sizeof(int) );
        d1.image[i] = calloc( original.width, sizeof(int) );
        d2.image[i] = calloc( original.width, sizeof(int) );
        d3.image[i] = calloc( original.width, sizeof(int) );
    }     
}

void free_images()
{
    int i;
    
    for(i = 0; i < original.height; i++)
    {
        free(original.image[i]);
        free(d1.image[i]);
        free(d2.image[i]);
        free(d3.image[i]);
    }    
    
    free(original.image);
    free(d1.image);
    free(d2.image);
    free(d3.image);
}  

