package decoder;

import is2.data.DataF;
import is2.data.Edges;
import is2.data.Parse;
import is2.data.ParseNBest;

import java.util.ArrayList;
import java.util.concurrent.Callable;

import decoder.ParallelRearrangeNBest.PA;

import extractors.Extractor;

/**
 * @author Dr. Bernd Bohnet, 30.08.2009
 * 
 * This class implements a parallel edge rearrangement for non-projective parsing;
 * The linear method was first suggest by Rayn McDonald et. al. 2005.  
 */
final public class ParallelRearrangeNBest2 implements Callable<Object> {

	// new parent child combination to explore
	final static class PA {
		final float p;
		final short ch, pa;
		

		public short[] heads,types;	

		public PA(Parse p, short ch2, short pa2) { 
			this.p =(float)p.f1;
			heads =p.heads;
			types=p.labels;
			ch=ch2;pa=pa2;
			
		}
	}

	// list of parent child combinations
	private static ArrayList<PA> parents = new ArrayList<PA>();

	// some data from the dependency tree
	private short[] pos;
	private DataF x;
	private float lastNBest;
	private float threshold;
	private Extractor extractor;


	/**
	 * Initialize the parallel rearrange thread
	 * @param pos the part-of-speech 
	 * @param x the data
	 * @param lastNBest 
	 * @param isChild2 is a child
	 * @param edgesC the part-of-speech edge mapping
	 * @param s the heads
	 * @param ts the types
	 */
	public ParallelRearrangeNBest2(short[] pos , DataF x, float lastNBest, Extractor extractor, float threshold) {



		this.lastNBest =lastNBest;
		this.pos =pos;
		this.x=x;

		this.extractor = extractor;
		this.threshold = threshold;
	}

	public ArrayList<ParseNBest> parses = new  ArrayList<ParseNBest>();

	@Override
	public Object call() {

		try {
		
		while(true) {
			PA p = getPA();

			if (p==null) return parses;
		
		short oldP = p.heads[p.ch], oldT = p.types[p.ch]; 
		p.heads[p.ch]=p.pa;

		short[] labels = Edges.get(pos[p.pa], pos[p.ch],p.ch<p.pa);

		for(int l=0;l<labels.length;l++) {

			p.types[p.ch]=labels[l];
			float p_new = extractor.encode3(pos, p.heads, p.types, x);

			if (p_new<lastNBest || ((p.p+this.threshold)>p_new)) continue;

			ParseNBest x = new ParseNBest();
			x.signature(p.heads, p.types);
			x.f1=p_new;
			parses.add(x);
		}

		// change back
		p.heads[p.ch]= oldP; p.types[p.ch]=oldT;

		// consider changes to labels only
		labels = Edges.get(pos[oldP], pos[p.ch],p.ch<oldP);

		for(int l=0;l<labels.length;l++) {

			p.types[p.ch]=labels[l];	
			float p_new = (float) extractor.encode3(pos, p.heads, p.types, x);

			// optimization: add only if larger than smallest of n-best
			if (p_new<lastNBest || ((p.p+this.threshold)>p_new)) continue;

			ParseNBest x = new ParseNBest();
			x.signature(p.heads, p.types);
			x.f1=p_new;
			parses.add(x);
		}

		p.heads[p.ch]= oldP; p.types[p.ch]=oldT;
		}
		} catch(Exception e) {
			e.printStackTrace();
		}
		return parses;
	}

	/**
	 * Add a child-parent combination which are latter explored for rearrangement
	 * 
	 * @param p2
	 * @param ch2
	 * @param pa
	 */
	public static void add(Parse p, short ch2, short pa) {
		parents.add(new PA(p,ch2,pa));
	}

	public static PA getPA() {
		synchronized(parents) {
			if (parents.size()==0) return null;
			return parents.remove(parents.size()-1);
		}
	}



}
