#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct nodo {
	struct nodo* filho_esquerdo;
	struct nodo* filho_direito;
	int regra_de_producao;
}  nodo_type;

typedef struct producao {
	char var[5];
	char produzido[10];
} producao_type;

typedef struct inferencia {
	char string[40];
	char var[5];
	int producao;
	int usado1;
	int usado2;
} inferencia_type;

typedef struct pilha {
	int linha;
	struct pilha* next;
} pilha_type;

void insere_pilha(pilha_type** base, int linha) {
	pilha_type* aux;

	aux = *base;
	if (aux==NULL) {
		aux = (pilha_type *)malloc(sizeof(pilha_type));
		aux->linha = linha;
		aux->next = NULL;
	} else {
		for (; aux->next != NULL; aux = aux->next);
		aux->next = (pilha_type *)malloc(sizeof(pilha_type));
		aux->next->linha = linha;
		aux->next->next = NULL;
	}
}

int retira_pilha(pilha_type** base){
	pilha_type *aux, *aux2;
	int linha;

	aux = *base;
	if (aux==NULL) {
		fprintf(stderr, "Tentativa de desempilhar de uma pilha vazia!!\n");
		exit(1);
	}
	if (aux->next == NULL) {
		linha = aux->linha;
		free(aux);
		*base=NULL;
	} else {
		for (; aux->next != NULL; aux = aux->next)
			aux2 = aux;
		linha = aux->linha;
		free(aux);
		aux2->next = NULL;
	}
	return linha;
}

int pilha_vazia(pilha_type* base) {
	if (base ==NULL)
		return 1;
	else return 0;
}

void insere_filho_esquerdo(nodo_type **pai, int regra) {
	nodo_type *aux, *filho_e;

	aux = *pai;
	filho_e = (nodo_type *) malloc(sizeof(struct nodo));
	filho_e->regra_de_producao = regra;
	filho_e->filho_direito = NULL;
	filho_e->filho_esquerdo = NULL;
	aux->filho_esquerdo = filho_e;
}

void insere_filho_direito(nodo_type **pai, int regra) {
	nodo_type *aux, *filho_d;

	aux = *pai;
	filho_d = (nodo_type *) malloc(sizeof(struct nodo));
	filho_d->regra_de_producao = regra;
	filho_d->filho_direito = NULL;
	filho_d->filho_esquerdo = NULL;
	aux->filho_direito = filho_d;

}

int string_vazia(char* string) {
	int i;
	for (i = 0; i<strlen(string);i++) {
		if (string[i] != ' ' && string[i] != '\t' && string[i]!='\r')
			return 0;
	}
	return 1;
}

producao_type* producoes;
inferencia_type* inferencia;

void preenche_producoes(FILE** arq, int* tam) {
	FILE* arquivo;
	int tamanho, begin, end;
	char leitura[100];
	char *aux;

	arquivo = *arq;
	tamanho = *tam;
	fprintf(stderr, "teste\n");
	while (1) {
		fgets(leitura, 100, arquivo);
		leitura[strlen(leitura)-1]='\0';
		fprintf(stderr, "lido do arquivo: %s\n", leitura);
		if (string_vazia(leitura)) {
			break;
		} else {
			aux = leitura;
			begin = 0;
			for(;aux[0] == ' ' || aux[0] == '\t'; aux++)
				begin++;
			end = begin;
			for(;aux[0] != ' ' && aux[0] != '-' && aux[0] != '\t';aux++);
				end++;
			//aloca primeiro parametro com end-begin
			tamanho +=1;
			producoes = realloc(producoes, (tamanho*sizeof(struct producao)));
			strncpy(producoes[tamanho-1].var, &leitura[begin], (end-begin));
			producoes[tamanho-1].var[end-begin] = '\0';
			begin = end;
			for (;aux[0] != '>';aux++)
				begin++;
			aux++;
			begin++;
			for(;aux[0] == ' ' || aux[0] == '\t';aux++)
				begin++;
			end = begin;
			for(;aux[0] != ' ' && aux[0] != '\t' && aux[0] != '\0' && aux[0] != '\r';aux++) {
				end++;
			}
			strncpy(producoes[tamanho-1].produzido, &leitura[begin], (end-begin));
			producoes[tamanho-1].produzido[(end-begin)] = '\0';
			fprintf(stderr, "'%s' -> '%s'\n", producoes[tamanho-1].var,
							producoes[tamanho-1].produzido);
		}
	}
	*tam = tamanho;
}

void preenche_inferencias(FILE** arq, int* tam) {
	FILE* arquivo;
	int tamanho, begin, end;
	char leitura[100];
	char *aux, temp[4];

	arquivo = *arq;
	tamanho = *tam;
	fgets(leitura, 100, arquivo);
	while (!feof(arquivo)) {
		leitura[strlen(leitura)-1]='\0';
		if (!string_vazia(leitura)) {
			aux = leitura;
			begin = 0;
			for(;aux[0] == ' ' || aux[0] == '\t'; aux++)
				begin++;
			end = begin;
			for(;aux[0] != ' ' && aux[0] != '\t';aux++)
				end++;
			tamanho +=1;
			inferencia = realloc(inferencia, (tamanho*sizeof(struct inferencia)));
			strncpy(inferencia[tamanho-1].string, &leitura[begin], (end-begin));
			inferencia[tamanho-1].string[end-begin] = '\0';
			begin = end;
			for(;aux[0] == ' ' || aux[0] == '\t'; aux++)
				begin++;
			end = begin;
			for(;aux[0] != ' ' && aux[0] != '\t' ;aux++)
				end++;
			strncpy(inferencia[tamanho-1].var, &leitura[begin], (end-begin));
			inferencia[tamanho-1].var[end-begin] = '\0';
			begin = end;
			for(;aux[0] == ' ' || aux[0] == '\t'; aux++)
				begin++;
			end = begin;
			for(;aux[0] != ' ' && aux[0] != '\t' ;aux++)
				end++;
			strncpy(temp, &leitura[begin], (end-begin));
			temp[end-begin] = '\0';
			inferencia[tamanho-1].producao = atoi(temp);
			begin = end;
			for(;aux[0] == ' ' || aux[0] == '\t'; aux++)
				begin++;
			end = begin;
			for(;aux[0] != ' ' && aux[0] != '\t' && aux[0] != '\0' && aux[0]!='\r';aux++)
				end++;
			if (aux[0]=='-') {
				inferencia[tamanho-1].usado1=0;
				inferencia[tamanho-1].usado2=0;
			} else {
				strncpy(temp, &leitura[begin], (end-begin));
				temp[end-begin]='\0';
				inferencia[tamanho-1].usado1 = atoi(temp);
				begin=end;
				for(;aux[0] == ' ' || aux[0] == '\t'; aux++)
					begin++;
				if (aux[0] != '\0' && aux[0]!='\r') {
					end=begin;
					for(;aux[0] != ' ' && aux[0] != '\t' && aux[0] != '\0' && aux[0]!='\r';aux++)
						end++;
					strncpy(temp, &leitura[begin], (end-begin));
					inferencia[tamanho-1].usado2 = atoi(temp);
				}else {
					inferencia[tamanho-1].usado2 = 0;
				}
			}
			fprintf(stderr, "_%s_ _%s_ _%d_ _%d_ _%d_\n", inferencia[tamanho-1].string, inferencia[tamanho-1].var, inferencia[tamanho-1].producao, inferencia[tamanho-1].usado1, inferencia[tamanho-1].usado2);
		}
		fgets(leitura, 100, arquivo);

	}
	*tam = tamanho;
}


char *saida;
FILE* arquivo_saida;

void imprime_saida() {
	fprintf(arquivo_saida, "%s => ", saida);
}

void muda_string(int linha) {
	int i;
	char *aux, *aux2;
	for(i =0; i<strlen(saida); i++) {
		if ((int)saida[i]>64 && (int)saida[i]<91) {
			fprintf(stderr, "Linha: %d\n", linha);
			fprintf(stderr, "Producao: %d",inferencia[linha].producao);
			fprintf(stderr, "Produzido: %s\nString: %s\n",producoes[inferencia[linha].producao].produzido, saida);
			aux = (char *)malloc(sizeof(char)*(strlen(producoes[inferencia[linha].producao].produzido)+strlen(saida)));
			aux2 = aux;
			aux = strncpy(aux, saida, i);
			aux+=i;
			aux = strncpy(aux,producoes[inferencia[linha].producao].produzido, strlen(producoes[inferencia[linha].producao].produzido));
			aux +=strlen(producoes[inferencia[linha].producao].produzido);
			aux = strncpy(&aux[i+strlen(producoes[inferencia[linha].producao].produzido)], &saida[i+1], strlen(saida)-1);
			free(saida);
			saida = aux2;
			break;
		}
	}
}

void percorre(int linha) {
	if (linha!=0) {
		//gera saida
		muda_string(linha);
		percorre(inferencia[linha].usado1);
		percorre(inferencia[linha].usado2);
	}
}

int main(int argc, char **argv) {
	FILE* arquivo_entrada;
	int linha_inferencia, tamanho_producoes, tamanho_inferencia, i;
	pilha_type* base_pilha;
	nodo_type *raiz, *aux;

	if (argc !=2) {
		fprintf(stderr, "Entre apenas com o nome do arquivo de entrada\n");
		exit(1);
	}

	if ((arquivo_saida = fopen("saida.txt", "w"))==NULL) {
		fprintf(stderr, "Erro ao abrir arquivo de saida\n");
		exit(1);
	}

	if ((arquivo_entrada = fopen(argv[1], "r")) == NULL){
		fprintf(stderr, "Erro ao abrir arquivo de entrada\n");
		exit(1);
	}

	tamanho_producoes = 1;
	tamanho_inferencia = 1;
	producoes = (producao_type *)malloc(sizeof(struct producao));
	inferencia = (inferencia_type *)malloc(sizeof(struct inferencia));

	preenche_producoes(&arquivo_entrada, &tamanho_producoes);
	preenche_inferencias(&arquivo_entrada, &tamanho_inferencia);
	base_pilha=NULL;
	//insere ultima linha
	linha_inferencia = tamanho_inferencia-1;
	insere_pilha(&base_pilha, linha_inferencia);
	raiz = (nodo_type *)malloc(sizeof(nodo_type));
	raiz->filho_esquerdo = NULL;
	raiz->filho_direito = NULL;
	raiz->regra_de_producao = inferencia[linha_inferencia].producao;
	aux = raiz;
	saida = (char *)malloc(sizeof(char)*3);
	saida = strncpy(saida, inferencia[linha_inferencia].var, strlen(inferencia[linha_inferencia].var));
	imprime_saida();
	fprintf(stderr, "Tamanho inf: %d\n", tamanho_inferencia);
	for(i=1;i<tamanho_inferencia;i++) {
		fprintf(stderr, "Regra: %d\n", inferencia[i].producao);
	}
	percorre(linha_inferencia);
	fclose(arquivo_entrada);
	fclose(arquivo_saida);
	return 0;
}

