#include "project.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

net* create_network(int num_inputs, int num_hidden, int num_outputs) {
	neuron *inputs, *outputs, *hidden;
	int i, j;
	outputs = (neuron*) malloc(num_outputs*sizeof(neuron));
	hidden = (neuron*) malloc(num_hidden*sizeof(neuron));
	inputs = (neuron*) malloc(num_inputs*sizeof(neuron));

	srand(1);

	for (i = 0; i < num_outputs; i++) {
		outputs[i].num_weights = num_hidden;
		outputs[i].weights = (edge*) malloc(num_hidden*sizeof(edge));
		for (j = 0; j < num_hidden; j++) {
			outputs[i].weights[j].source = &(hidden[j]);
			outputs[i].weights[j].weight = ((double)rand()) / RAND_MAX;
			//printf("from hidden %i to output %i weight %f\n", j, i, outputs[i].weights[j].weight);
		}
	}

	for (i = 0; i < num_hidden; i++) {
		hidden[i].num_weights = num_inputs;
		hidden[i].weights = (edge*) malloc(num_inputs*sizeof(edge));
		for (j = 0; j < num_inputs; j++) {
			hidden[i].weights[j].source = &(inputs[j]);
			hidden[i].weights[j].weight = ((double)rand()) / RAND_MAX;
			//printf("from input %i to hidden %i weight %f\n", j, i, hidden[i].weights[j].weight);
		}
	}

	net* network = (net*) malloc(sizeof(net));
	network->inputs = inputs;
	network->hidden = hidden;
	network->outputs = outputs;
	network->num_inputs = num_inputs;
	network->num_hidden = num_hidden;
	network->num_outputs = num_outputs;

	return network;
}

void process_inputs(net* n, double inputs[]) {
	int i, j;

	for (i=0; i < n->num_inputs; i++) {
		n->inputs[i].value = rescale(inputs[i]);
		//printf("input %i value %f scaled to %f\n", i, inputs[i], n->inputs[i].value);
	}

	for (i=0; i < n->num_hidden; i++) {
		n->hidden[i].value = 0;
		for (j=0; j < n->hidden[i].num_weights; j++) {
			n->hidden[i].value += n->hidden[i].weights[j].source->value * n->hidden[i].weights[j].weight;
		}
		n->hidden[i].value = rescale(n->hidden[i].value);
		//printf("hidden %i scaled to %f\n", i, n->hidden[i].value);
	}

	for (i=0; i < n->num_outputs; i++) {
		n->outputs[i].value = 0;
		for (j=0; j < n->outputs[i].num_weights; j++) {
			n->outputs[i].value += n->outputs[i].weights[j].source->value * n->outputs[i].weights[j].weight;
		}
		n->outputs[i].value = rescale(n->outputs[i].value);
		//printf("output %i scaled to %f\n", i, n->outputs[i].value);
	}
}

double get_output(net* n, int output) {
	return n->outputs[output].value;
}

double input_scale(double value) {
	//printf("%f\n", value);
	return value / 255;
}

double rescale(double value) {
	return 1 / (1 + exp(-value));
}
