#define PROG_NAME "compute_normals"

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <i2.h>
#include <frgb.h>
#include <values.h>
#include <float_image.h>
#include <float_pnm_image_io.h>
#include <argparser.h>
#include "normais.h"
#include "imagem_vetores.h"
#include "tabela.h"
#include "super_tabela.h"
#include "hash.h"
#include <time.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include <sys/times.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>



#define PROG_HELP \
  PROG_NAME " \\\n" \
  "    -nLights {NUM} \\\n" \
  "    -prefix {FILE_PREFIX} \\\n" \
  "    [ -refNormalMap {FILE_NAME} ] \\\n" \
  "    [ -channels {R | G | B| RG | RB | GB | RGB } \\\n" \
  "    -gamma {NUM} [ -gray ] \\\n" \
  "    -sceneImages {FILENAME}...  \\\n" \
  "    [ -rectangle {XMIN] {XMAX] {YMIN] {YMAX} ] \\\n" \
  "    [ -sigma {NUM} \\\n" \
  "    [ -logProbFunction {NUM} ] \\\n" \
  "    [ -albedoFunction {NUM} ] \\\n" \
  "    { -brute | -hash | -bruteAndHash } \\\n" \
  "    { -nsig | -alpha | -nsigOrAlpha } \\\n" \
  "    -gaugeCenter {NUM} {NUM} \\\n" \
  "    -gaugeRadius {NUM} \\\n" \
  "    [ -gaugeAlbedo {NUM} {NUM} {NUM} ] \\\n" \
  "    -gaugeImages {FILENAME}... gauge_image_name \\\n" \
  "    [ -tableSize {NUM} ] \\\n" \
  "    [ -nGauges {NUM} -gaugePos {NUM} {NUM} ... ] \\\n" \
  "    [ -debug {NP} {H[1]} {V[1]} ... {H[NP]} {V[NP]} ] \\\n" \
  "    " argparser_help_info_HELP ""

#define PROG_INFO \
  "NAME\n" \
  "  Etc. etc..\n" \
  "\n" \
  "SYNOPSIS\n" \
  PROG_HELP "\n" \
  "\n" \
  "OPTIONS" \
  "  Etc. etc.."

typedef struct options_t {
	/* General parameters: */
	int nLights;
	char *prefix;
	char *channels;              /* Channels to process (a substring of "RGB"). */
	/* Input image parameters: */
	double gamma;
	double bias;
	bool_t gray;
	/* Observation matching parameters: */
	/* Bucket Grid only*/
	/* Gauge parameters: */
	r2_t gaugeCenter;
	double gaugeRadius;
	r2_t gaugeStretch; /*extra stretching of gauge image*/
	frgb_t gaugeAlbedo;
	char** gauge_image_name; /* Gauge image filenames are {gauge_image_name[0..nLights-1]} */
	char** gauge_direction_name; /*files containing directions of light source - if not NULL will be used to generate virtual gauge table*/
	r3_t* gaugeDirections; /*contains light source directions of virtual gauge*/
	/* Light table parameters: */
	int tableSize;
	/* Debugging */
	int nGauges;  /* Not used yet. */
	r2_t* gaugePos;
} options_t;

options_t *parse_options(int argc, char **argv);

options_t *parse_options(int argc, char **argv){
	argparser_t *pp = argparser_new(stderr, argc, argv);
	argparser_set_help(pp, PROG_HELP);
	argparser_set_info(pp, PROG_INFO);
	argparser_process_help_info_options(pp);
	
	options_t *o = (options_t *)malloc(sizeof(options_t));
	
	if (argparser_keyword_present(pp, "-channels")) {
		o->channels = argparser_get_next(pp);
	} else {
		o->channels = "RGB";
	}
	
	fprintf(stderr, "  -channels %s \\\n", o->channels);
	
	
	if (argparser_keyword_present(pp, "-tableSize")) {
		o->tableSize = argparser_get_next_int(pp, 2, 1000);
	} else {
		o->tableSize = 30;
	}
	fprintf(stderr, "  -tableSize %d \\\n", o->tableSize);
	
	argparser_get_keyword(pp, "-gaugeCenter");
	o->gaugeCenter.c[0] = argparser_get_next_double(pp, 0.0, 100000.0);
	o->gaugeCenter.c[1] = argparser_get_next_double(pp, 0.0, 100000.0);
	fprintf(stderr, "  -gaugeCenter %lf %lf \\\n", o->gaugeCenter.c[0], o->gaugeCenter.c[1]);
	
	
	
	argparser_get_keyword(pp, "-gaugeRadius");
	o->gaugeRadius = argparser_get_next_double(pp, 1.0, 100000.0);
	fprintf(stderr, "  -gaugeRadius %lf \\\n", o->gaugeRadius);
	
	if( argparser_keyword_present(pp, "-gaugeStretch") ){
		o->gaugeStretch.c[0] = argparser_get_next_double(pp, -100000.0, 100000.0);
		o->gaugeStretch.c[1] = argparser_get_next_double(pp, -100000.0, 100000.0);
		
	}else{
		o->gaugeStretch = (r2_t){{0,0}};
	}
	fprintf(stderr, "  -gaugeStretch %lf %lf\\\n", o->gaugeStretch.c[0],o->gaugeStretch.c[1]);
	
	argparser_get_keyword(pp, "-nLights");
	o->nLights = argparser_get_next_int(pp, 3, 1000);
	fprintf(stderr, "  -nLights %d \\\n", o->nLights);
	
	argparser_get_keyword(pp, "-prefix");
	o->prefix = argparser_get_next(pp);
	fprintf(stderr, "  -prefix %s \\\n", o->prefix);
	
	
	if (argparser_keyword_present(pp, "-nGauges")) {
		o->nGauges = argparser_get_next_int(pp, 1, 100);
	} else {
		o->nGauges = 1;
	}
	fprintf(stderr,"    -nGauges %d \\\n",o->nGauges);
	
	argparser_get_keyword(pp, "-gaugeImages");
	o->gauge_image_name = (char**) malloc(sizeof(char*)*(o->nLights*o->nGauges));
	fprintf(stderr, "Gabaritos:\n");
	int ind;
	for (ind = 0; ind < (o->nLights*o->nGauges); ind++){
		o->gauge_image_name[ind] = argparser_get_next(pp);
		fprintf(stderr, "    G[%02d] = %s\n",  ind, o->gauge_image_name[ind]);
	}
	
	if(argparser_keyword_present(pp, "-gaugeDirections") ){
		o->gauge_direction_name = (char**) malloc(sizeof(char*)*(o->nLights*o->nGauges));
		fprintf(stderr, "Gabaritos - DIRECOES:\n");
		for (ind = 0; ind < (o->nLights*o->nGauges); ind++){
			o->gauge_direction_name[ind] = argparser_get_next(pp);
			fprintf(stderr, "    Dir[%02d] = %s\n",  ind, o->gauge_image_name[ind]);
		}
		o->gaugeDirections = (r3_t*)malloc(sizeof(r3_t)*o->nLights);
	}
	else{
		o->gauge_direction_name = NULL;
		o->gaugeDirections = NULL;
	} 
	
	o->gaugePos = (r2_t*)malloc(sizeof(r2_t)*(o->nGauges));
	if (argparser_keyword_present(pp, "-gaugePos")) {
		for (ind =0; ind < o->nGauges; ind++){
		o->gaugePos[ind].c[0] = argparser_get_next_double(pp, -100000.0, +200000.0);
		o->gaugePos[ind].c[1] = argparser_get_next_double(pp, -100000.0, +200000.0);
		fprintf(stderr, "    [%02d] %lf  %lf \\\n", ind, o->gaugePos[ind].c[0], o->gaugePos[ind].c[1]);
		}
	} else {
		if (o->nGauges > 1) { 
			argparser_error(pp, "Must spcify \"-gaugePos\" when there are multiple gauges");
		} else {
			for (ind =0; ind < o->nGauges; ind++){
				o->gaugePos[ind] = (r2_t){{ 0,0 }};
			}
		}
	}
	
	if (argparser_keyword_present(pp, "-gaugeAlbedo")) {
		o->gaugeAlbedo.c[0] = argparser_get_next_double(pp, 0.001, 1.000);
		o->gaugeAlbedo.c[1] = argparser_get_next_double(pp, 0.001, 1.000);
		o->gaugeAlbedo.c[2] = argparser_get_next_double(pp, 0.001, 1.000);
	} else {
		o->gaugeAlbedo.c[0] = o->gaugeAlbedo.c[1] = o->gaugeAlbedo.c[2] = 1.000;
	}
	fprintf(stderr, "  -gaugeAlbedo %5.3lf %5.3lf %5.3lf \\\n", o->gaugeAlbedo.c[0],o->gaugeAlbedo.c[1],o->gaugeAlbedo.c[2]);
	
	argparser_get_keyword(pp, "-gamma");
	o->gamma = argparser_get_next_double(pp, 0.100, 9.000);
	o->bias = (o->gamma == 1.000 ? 0.000 : VIEW_BIAS); /* Hack... */
	fprintf(stderr, "Gamma das imagens:%lf bias: %lf\n", o->gamma, o->bias);
	
	o->gray = argparser_keyword_present(pp, "-gray");
	
	argparser_finish(pp);
	
	return o;
}


int main(int argc,char** argv){
	options_t *o = parse_options(argc, argv);
		
	/* Lê as imagens dos gabaritos: */
	float_image_t *** G_list = (float_image_t ***)malloc(sizeof(float_image_t **)*o->nGauges);
	int ind;
	for (ind = 0; ind < o->nGauges; ind++) {
		char** pnames;
		pnames= o->gauge_image_name + (ind*o->nLights);
		G_list[ind]= float_pnm_image_list_read( o->nLights,pnames, o->gamma, o->bias, TRUE,TRUE,TRUE);
		assert(G_list[ind][0]->sz[0] == 3);
	}
	fprintf(stderr, "Imagens de gabaritos lidas.\n");
	if(o->gaugeDirections != NULL){
		int i;
		for(i = 0; i < o->nLights;i++){
			FILE* arq_dir;
			fprintf(stderr,"Abrindo arquivo de direcao %s ...\n",o->gauge_direction_name[i]);
			arq_dir = fopen(o->gauge_direction_name[i],"rt");
			if(arq_dir == NULL){
				fprintf(stderr,"Nao conseguiu abrir arquivo !\n");
				return 1;
			}
			double dx,dy,dz;
			int test_read;
			test_read = fscanf(arq_dir,"%lf %lf %lf",&dx,&dy,&dz);
			if(test_read != 3){
				fprintf(stderr,"Error reading file - %d numbers found\n",test_read);
				return 1;
			}
			o->gaugeDirections[i].c[0] = dx;
			o->gaugeDirections[i].c[1] = dy;
			o->gaugeDirections[i].c[2] = dz;
			fprintf(stderr,"Direction: %+8.5f %+8.5f %+8.5f\n",dx,dy,dz);
			fclose(arq_dir);
		}
	}

	r2_t *pontos;
	r3_t *normais;
	int num_pontos;
	fprintf(stderr, "Gerando os pontos de amostragem...\n");
	gera_pontos_no_gabarito_eliptco(o->tableSize, o->gaugeCenter, o->gaugeRadius,o->gaugeStretch, &pontos, &normais, &num_pontos);
	
	/* Gera máscara do gabarito: */
	float_image_t  *M = gera_mascara_do_gabarito(G_list[0][0]->sz[1], G_list[0][0]->sz[2], o->gaugeCenter, o->gaugeRadius,o->gaugeStretch);
	char *nome_arq_M = NULL;
	asprintf(&nome_arq_M, "%s_gauge_mask.ppm", o->prefix);
	float_pnm_image_write(nome_arq_M, M, 1.000, 0.000,TRUE,TRUE,TRUE);
	free(nome_arq_M);

	int canal;
	int ind_gabarito;
	for( ind_gabarito = 0; ind_gabarito < o->nGauges; ind_gabarito++){
  		for (canal = 0; canal < 3; canal++) {
    			/* Devemos proessar este canal? */
    			int process_channel = strchr(o->channels, "RGB"[canal]) != NULL;
			if(process_channel){
				char *nome_arq_table = NULL;
				asprintf(&nome_arq_table, "%s_%d_G%d_TableData.txt", o->prefix, canal,ind_gabarito);
				Tabela* tab;
				fprintf(stderr,"---------------------------------------------------------------------");
    				fprintf(stderr, "INICIO DO PROCESSAMENTO DO CANAL %d: \n", canal);
				if(o->gaugeDirections == NULL){
					tab = cria_tabela
					( G_list[ind_gabarito],
					M,
					o->gaugeAlbedo.c[canal],
					o->nLights,
					canal,
					pontos,
					normais,
					num_pontos
					);
				}else{
					fprintf(stderr,"Virtual Table selected\n");
					tab = cria_tabela_virtual
						(o->gaugeDirections,
						o->gaugeRadius,
						o->gaugeCenter,
						o->gaugeAlbedo.c[canal],
						o->nLights,
						canal,
						pontos, 
						normais,
						num_pontos
					);
				}
				if (tab == NULL) { fprintf(stderr, "Erro na geracao de tabela!\n");  return 1; }
				SaveTable(nome_arq_table,tab);
    				fprintf(stderr, "Tabela gerada.\n");
			}
		}
	}
	return 0;
}

