package svm.instances.dependency;

import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import svm.Mode;
import svm.SVMStruct;
import svm.SVMStructKernel;
import svm.instances.dependency.kernel.BagOfWordKernel;
import svm.instances.dependency.kernel.NoInputKernel;
import svm.instances.dependency.kernel.TokenSequenceKernel;
import base.CachedKernel;
import base.Example;
import base.Kernel;

public class DependencyParserProblem
{

	public static void main(String[] args)
	{
		implicit();
		// explicit();
	}

	private static void explicit()
	{
		DependencyParserInstance in = new DependencyParserInstance();

		SVMStruct<TokenSequence, DependencyTree> svm = new SVMStruct<TokenSequence, DependencyTree>(in, 200, 0.05, Mode.MARGIN_RESCALING);
		List<Example<TokenSequence, DependencyTree>> training;

		System.out.print("loading treebank...");
		List<Example<TokenSequence, DependencyTree>> data = ConllLoader.loadTreebank();
		System.out.println("done.");
		training = data.subList(0, (int) (0.5 * data.size()));

		svm.train(training);

		for (Entry<String, Integer> e : svm.wMap.entrySet())
			System.out.println(e.getKey() + ": " + svm.w.x[e.getValue()]);

		List<Example<TokenSequence, DependencyTree>> test = data.subList((int) 0.5 * data.size(), data.size());
		double l = svm.empiricalError(test);

		System.out.println("Test Error: " + l);
	}

	private static void implicit()
	{
		Kernel<TokenSequence> k = new TokenSequenceKernel();
		for (double C : new double[] { 1000 })//, 10.0 , 50.0 ,100.0 ,200.0, 500.0})
		{
			// double C = 10d;
			double eps = 0.05;
			long time = System.currentTimeMillis();
			DependencyParserKernelInstance in = new DependencyParserKernelInstance();

			in.in = k;

			final SVMStructKernel<TokenSequence, DependencyTree> svm;
			final List<Example<TokenSequence, DependencyTree>> data;
			List<Example<TokenSequence, DependencyTree>> training;
			List<Example<TokenSequence, DependencyTree>> test;
			// File f = new File("/data/d1/pfahler/kernelsvm.obj");
			// if (f.exists())
			// {
			//
			// try
			// {
			// System.out.println("Wir laden das einfach von der HDD");
			// ObjectInputStream s = new ObjectInputStream(new GZIPInputStream(new FileInputStream(f)));
			// svm = (SVMStructKernel<TokenSequence, DependencyTree>) s.readObject();
			// svm.C = C;
			// svm.eps = eps;
			// data = (List<Example<TokenSequence, DependencyTree>>) s.readObject();
			// training = data.subList(0, (int) (0.5 * data.size()));
			// test = data.subList((int) 0.5 * data.size(), data.size());
			// }
			// catch (ClassNotFoundException | IOException e1)
			// {
			// // TODO Auto-generated catch block
			// e1.printStackTrace();
			// System.out.println("FUCK!");
			// return;
			// }
			// }
			// else
			{
				svm = new SVMStructKernel<TokenSequence, DependencyTree>(in, C, eps, Mode.MARGIN_RESCALING);
				System.out.print("loading treebank...");
				data = ConllLoader.loadTreebank("data/tuebadz.conll",100);
				System.out.println("done. ("+data.size()+")");
				training = data.subList(0, (int) (0.5 * data.size()));
				test = data.subList((int) (0.5 * data.size())+1,data.size());//ConllLoader.loadTreebank("data/german/tiger/test/german_tiger_test.conll",-1);
				if (in.in instanceof CachedKernel)
					((CachedKernel) in.in).initKernelCache(data.size()+test.size());
				System.out.println("Average Input Kernel Value: " + training.parallelStream().mapToDouble((e) -> {
					double s = 0;
					for (Example<TokenSequence, DependencyTree> ee : data)
					{
						double bla = svm.inputKernel.k(ee.x, e.x);
						if (Double.isNaN(bla))
						{
							svm.inputKernel.k(ee.x, e.x);
							throw new RuntimeException("HOLY MOLY" + ee.x + " " + e.x);

						}
						s += bla;
					}
					return s;

				}).average().getAsDouble() + " [ really just precomputing the input kernel matrix ;-) ]");
				// FileOutputStream file;
				// try
				// {
				// file = new FileOutputStream("/data/d1/pfahler/kernelsvm.obj");
				// ObjectOutputStream out = new ObjectOutputStream(new GZIPOutputStream(file));
				// out.writeObject(svm);
				// out.writeObject(data);
				// out.close();
				// file.close();
				// }
				// catch (FileNotFoundException e1)
				// {
				// // TODO Auto-generated catch block
				// e1.printStackTrace();
				// }
				// catch (IOException e1)
				// {
				// // TODO Auto-generated catch block
				// e1.printStackTrace();
				// }
			}
			System.out.println(System.currentTimeMillis() - time + "ms for computing/loading the kernel");

			time = System.currentTimeMillis();

			// System.out.println(training.get(0));System.out.println(training.get(1));System.out.println(training.get(110));
			svm.train(training);
			for (double alpha : svm.alphas)
			{
				System.out.print(alpha + "\t");
			}
			System.out.println("");
			System.out.println(System.currentTimeMillis() - time + "ms for computing the model");

			time = System.currentTimeMillis();


			List<DependencyTree> l = test.parallelStream().map((e) -> svm.predict(e.x)).collect(Collectors.toList());
			System.out.println(l.size());
			double validTrees = 0;
			double loss = 0;
			for(int ii=0;ii<l.size();ii++)
			{
				TokenSequence x = test.get(ii).x;
				DependencyTree yhat = l.get(ii);
				DependencyTree y = test.get(ii).y;

				for(int j=1;j<x.tokens.length;j++)
				{
					System.out.println(x.tokens[j]+"\t"+x.posTag[j]+"\t"+x.posTag2[j]+"\t"+y.heads[j-1]+"\t"+yhat.heads[j-1]);
				}
				System.out.println();
				loss += svm.instance.loss(y, yhat);

				int[] marks = new int[yhat.heads.length];
				int j = 0;
				for (int i = 0; i < yhat.heads.length; i++)
				{
					if(yhat.heads[i]==0)
					{
						tiefensuche(i,yhat.heads,marks);
						j++;
					}
				}
//				System.out.println(j+"\t"+Arrays.toString(yhat.heads) + "\t" + Arrays.toString(marks));
				int valid = 1;
				for(int i:marks)
					if(i>1 || i==0)
						valid= 0;
				validTrees+=valid;
			}

			loss/=test.size();
			validTrees/= test.size();

			System.out.println("Test Error: " + " "+ loss);
			System.out.println(System.currentTimeMillis() - time + "ms for testing the model");
			System.out.println("Number of valid trees: " + validTrees);
		}
	}
	static void tiefensuche(int root, int[] heads, int[] marks){
		marks[root]++;
		for(int i=0;i<heads.length;i++)
		{
			if(heads[i]-1==root && marks[i]==0)
				tiefensuche(i,heads,marks);
			else if(heads[i]-1==root && marks[i]==1)
				marks[i]=2;
		}
	}
}
