/* Segundo Trabalho da cadeira de Fundamentos de Computação Gráfica (Images, T1) */
/* prof. Marcelo Gattass                                                         */
/* Rafael Diniz                                                                  */

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

#include "image.h"

// HSV version, only Y is computed
CImage *gaussiano(CImage *img, int destroy_src)
{
    CImage *out_img;
    int cols = img->C[2]->ncols;
    int lines = img->C[2]->nrows;
    int new_value;

    fprintf(stderr, "Filtro gaussiano em imagem %dx%d\n", cols, lines);

    out_img = CreateCImage(cols, lines);
    
    int n = cols * lines;
    // copy original image to output image
    for (int i = 0; i < n; i++)
    {
	out_img->C[0]->val[i] = img->C[0]->val[i];
	out_img->C[1]->val[i] = img->C[1]->val[i];
	out_img->C[2]->val[i] = img->C[2]->val[i];
    }

    for (int i = cols + 1; i < (n - cols - 1); i++)
    {
	new_value = 0;
	new_value += img->C[2]->val[i - cols - 1] * 1;
	new_value += img->C[2]->val[i - cols] * 2;
	new_value += img->C[2]->val[i - cols + 1] * 1;
	new_value += img->C[2]->val[i - 1] * 2;
	new_value += img->C[2]->val[i] * 4;
	new_value += img->C[2]->val[i + 1] * 2;
	new_value += img->C[2]->val[i + cols - 1] * 1;
	new_value += img->C[2]->val[i + cols] * 2;
	new_value += img->C[2]->val[i + cols + 1] * 1;

	new_value /= 16;

	out_img->C[2]->val[i] = new_value;
    }

    if (destroy_src)
	DestroyCImage(&img); 
    
    return out_img;

}


// HSV version, only Y is computed
CImage *mediana(CImage *img, int destroy_src)
{
    int cols = img->C[2]->ncols;
    int lines = img->C[2]->nrows;
    int window[9]; // 3x3px window
    CImage *out_img;

    fprintf(stderr, "Filtro da mediana em imagem %dx%d\n", cols, lines);

    out_img = CreateCImage(cols, lines);    

    int n = cols * lines;
    // copy original image to output image
    for (int i = 0; i < n; i++)
    {
	out_img->C[0]->val[i] = img->C[0]->val[i];
	out_img->C[1]->val[i] = img->C[1]->val[i];
	out_img->C[2]->val[i] = img->C[2]->val[i];
    }

    for (int i = cols + 1; i < (n - cols - 1); i++)
    {
	window[0] = img->C[2]->val[i - cols - 1];
	window[1] = img->C[2]->val[i - cols];
	window[2] = img->C[2]->val[i - cols + 1];
	window[3] = img->C[2]->val[i - 1];
	window[4] = img->C[2]->val[i];
	window[5] = img->C[2]->val[i + 1];
	window[6] = img->C[2]->val[i + cols - 1];
	window[7] = img->C[2]->val[i + cols];
	window[8] = img->C[2]->val[i + cols + 1];

	// sort window values
	int k; int sorted = 0;
	for (int j = 0; j < 64; j++) // 64 = (9-1)^2, bubblesort argoritm
	{
	    if (sorted == 8)
		break;
	    // swap window[j] with window[j + 1] 
	    k = (j % 8);
	    if (window[k] > window[k + 1]){
		int temp = window[k + 1];
		window[k + 1] = window[k];
		window[k] = temp;
		sorted = 0;
	    }
	    else {
		sorted++;
	    }
	}
	
	// select the median value
	out_img->C[2]->val[i] = window[4];
	
    }

    if (destroy_src)
	DestroyCImage(&img); 
    
    return out_img;
}

// HSV version, only Y is computed
CImage *sobel(CImage *img, int destroy_src)
{
    int cols = img->C[2]->ncols;
    int lines = img->C[2]->nrows;
    int new_value; 
    CImage *out_img;

    fprintf(stderr, "Filtro de Sobel em imagem %dx%d\n", cols, lines);

    out_img = CreateCImage(cols, lines);    
    
    int n = cols * lines;
    // copy original image to output image
    for (int i = 0; i < n; i++)
    {
	out_img->C[0]->val[i] = img->C[0]->val[i];
	out_img->C[1]->val[i] = img->C[1]->val[i];
	out_img->C[2]->val[i] = img->C[2]->val[i];
    }

    for (int i = cols + 1; i < (n - cols - 1); i++)
    {
	new_value = 0;
	new_value += img->C[2]->val[i - cols - 1] * 1;
	new_value += img->C[2]->val[i - cols + 1] * (-1);
	new_value += img->C[2]->val[i - 1] * 2;
	new_value += img->C[2]->val[i + 1] * (-2);
	new_value += img->C[2]->val[i + cols - 1] * 1;
	new_value += img->C[2]->val[i + cols + 1] * (-1);

	out_img->C[2]->val[i] = (new_value > 0)? new_value: -new_value;
    }
    
    if (destroy_src)
	DestroyCImage(&img); 

    return out_img;

}

CImage *detect_players(CImage *mask, CImage *src, int destroy_src)
{
    int cols = src->C[2]->ncols;
    int lines = src->C[2]->nrows;

    CImage *out_img;

    fprintf(stderr, "Algoritmo que acha os jogadores em imagem %dx%d\n", cols, lines);

    out_img = CreateCImage(cols, lines);    

    int n = cols * lines;

    for (int i = 0; i < n; i++)
    {
	    out_img->C[0]->val[i] = 240;
	    out_img->C[1]->val[i] = 240;
	    out_img->C[2]->val[i] = 240;
    }


    for (int i = cols + 1; i < (n - cols - 1); i++)
    {
	if (mask->C[2]->val[i] > 6)
	{

	    for (int j = 0; j < 3; j++)
	    {
		out_img->C[j]->val[i - cols - 1] = src->C[j]->val[i - cols - 1];
		out_img->C[j]->val[i - cols] = src->C[j]->val[i - cols];
		out_img->C[j]->val[i - cols + 1] = src->C[j]->val[i - cols + 1];
		out_img->C[j]->val[i - 1] = src->C[j]->val[i - 1];
		out_img->C[j]->val[i] = src->C[j]->val[i];
		out_img->C[j]->val[i + 1] = src->C[j]->val[i + 1];
		out_img->C[j]->val[i + cols - 1] = src->C[j]->val[i + cols - 1];
		out_img->C[j]->val[i + cols] = src->C[j]->val[i + cols];
		out_img->C[j]->val[i + cols + 1] = src->C[j]->val[i + cols + 1];
	    }
	    
	}
	
    }

    if (destroy_src)
    {
	DestroyCImage(&mask); 
	DestroyCImage(&src); 
    }
    return out_img;

}

int main(int argc, char *argv[])
{
    CImage  *img_rgb, *img_hsv;
    int median_pass = 1;
    int gauss_pass = 2;

    if (argc < 3) 
    {
	fprintf(stderr,"usage: %s input_image.ppm output_image.ppm <median_iterations> <gaussian_iterations> \n", argv[0]);
	fprintf(stderr,"\n");
	exit(-1);
    }
    if (argc > 3)
	median_pass = atoi(argv[3]);
    if (argc > 4)
	gauss_pass = atoi(argv[4]);

    img_rgb   = ReadCImage(argv[1]);
    img_hsv   = CImageRGBtoHSV(img_rgb);
     
    CImage *gauss_img = img_hsv;
    for (int i = 0; i < gauss_pass; i++)
	gauss_img = gaussiano(gauss_img, 1);

    CImage *median_img = gauss_img;
    for (int i = 0; i < median_pass; i++)
	median_img = mediana(median_img, 1);
    
    CImage *sobel_img = sobel(median_img, 1);
    
    CImage *output_img = detect_players(sobel_img, img_rgb, 1);
    
    WriteCImage(output_img, argv[2]);
    
    DestroyCImage(&output_img);
    
    return 0;   
}
