package net.woodyfolsom.msproj.ann;

import java.util.List;
import java.util.Set;

import org.encog.engine.network.activation.ActivationSigmoid;
import org.encog.ml.data.MLDataPair;
import org.encog.ml.data.MLDataSet;
import org.encog.ml.data.basic.BasicMLDataSet;
import org.encog.ml.train.MLTrain;
import org.encog.neural.networks.BasicNetwork;
import org.encog.neural.networks.layers.BasicLayer;
import org.encog.neural.networks.training.propagation.back.Backpropagation;

public class WinFilter extends AbstractNeuralNetFilter implements
		NeuralNetFilter {

	public WinFilter() {
		// create a neural network, without using a factory
		BasicNetwork network = new BasicNetwork();
		network.addLayer(new BasicLayer(null, false, 2));
		network.addLayer(new BasicLayer(new ActivationSigmoid(), true, 4));
		network.addLayer(new BasicLayer(new ActivationSigmoid(), true, 2));
		network.getStructure().finalizeStructure();
		network.reset();

		this.neuralNetwork = network;
	}

	@Override
	public void learn(MLDataSet trainingData) {
		throw new UnsupportedOperationException(
				"This filter learns a Set<List<MLDataPair>>, not an MLDataSet");
	}

	/**
	 * Method is necessary because with temporal difference learning, some of
	 * the MLDataPairs are related by being a sequence of moves within a
	 * particular game.
	 */
	@Override
	public void learn(Set<List<MLDataPair>> trainingSet) {
		MLDataSet mlDataset = new BasicMLDataSet();

		for (List<MLDataPair> gameRecord : trainingSet) {
			for (int t = 0; t < gameRecord.size() - 1; t++) {
				mlDataset.add(gameRecord.get(t).getInput(), this.neuralNetwork.compute(gameRecord.get(t)
						.getInput()));
			}
			mlDataset.add(gameRecord.get(gameRecord.size() - 1));
		}

		// train the neural network
		final MLTrain train = new TemporalDifference(neuralNetwork, mlDataset, 0.7, 0.8, 0.25);
		//final MLTrain train = new Backpropagation(neuralNetwork, mlDataset, 0.7, 0.8);
		actualTrainingEpochs = 0;

		do {
			if (actualTrainingEpochs > 0) {
				int gameStateIndex = 0;
				for (List<MLDataPair> gameRecord : trainingSet) {
					for (int t = 0; t < gameRecord.size() - 1; t++) {
						MLDataPair oldDataPair = mlDataset.get(gameStateIndex);
						this.neuralNetwork.compute(oldDataPair.getInput());
						gameStateIndex++;
					}
					gameStateIndex++;
				}
			}
			train.iteration();
			System.out.println("Epoch #" + actualTrainingEpochs + " Error:"
					+ train.getError());
			actualTrainingEpochs++;
		} while (train.getError() > 0.01
				&& actualTrainingEpochs <= maxTrainingEpochs);
	}

	@Override
	public void reset() {
		neuralNetwork.reset();
	}

	@Override
	public void reset(int seed) {
		neuralNetwork.reset(seed);
	}

	@Override
	public BasicNetwork getNeuralNetwork() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int getInputSize() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int getOutputSize() {
		// TODO Auto-generated method stub
		return 0;
	}
}