package version10_2_2_1.agnosticMemory;

import java.util.ArrayList;

import version10_2_2_1.Interface.InteractionList;


// an equation defines the boolean equation of an image of an interaction
// an equation is under the form of :
// an interaction if type = 0
// an equation of type   node1 + node2 + ... + nodeN   if interaction false by default
// an equation of type /(node1 + node2 + ... + nodeN)  if interaction true by default
public class Projection {

	public static final int LENGTH=8;
	
		public byte level;
		public byte type;			// 0 -> leaf, 1 -> equation default true, -1 -> equation default false
		public Signature leaf;
		public ArrayList<Node>[] equation_t;
		public ArrayList<Node>[] equation_f;
		
		// first level
		public Projection(Signature s, ArrayList<Signature> sig, byte previous){
			level=0;
			type=0;
			leaf=s;
			
			// define new type
			if (leaf.nb_s_t==100) type=1;
			if (leaf.nb_s_f==100) type=-1;
			
			if (type!=0){
				equation_t=new ArrayList[6];
				equation_f=new ArrayList[6];
				for (short i=0;i<6;i++){
					equation_t[i]=new ArrayList<Node>();
					equation_f[i]=new ArrayList<Node>();
				}
				projection(sig, previous);
			}
		}
		
		private Projection(Signature s, byte l, ArrayList<Signature> sig, byte previous){
			level=l;
			type=0;
			leaf=s;
			
			// projection only if still in the range
			if (level<LENGTH){
				
				// define new type
				if (leaf.nb_s_t==100) type=1;
				if (leaf.nb_s_f==100) type=-1;
				
				if (type!=0){
					equation_t=new ArrayList[6];
					equation_f=new ArrayList[6];
					for (short i=0;i<6;i++){
						equation_t[i]=new ArrayList<Node>();
						equation_f[i]=new ArrayList<Node>();
					}
					projection(sig, previous);
				}
			}
		}
		
		// replace leaf elements of the equation by their  own equations
		public void projection(ArrayList<Signature> sig, byte previous){

			for (int c=0;c<leaf.context_t.size();c++){
				// if the context is related to the primary interaction mov and is reliable, add new node to characterize this context
				if (leaf.context_t.get(c).nb_s==10){
					int index=leaf.context_t.get(c).index_primary-InteractionList.nbDF;
					
					if (index!=2 && !(previous==0 && index==1) && !(previous==4 && index==5) && !(previous==5 && index==4)){		// hypothesis to reduce the number of path
						equation_t[index].add(new Node(leaf.context_t.get(c), sig,(byte)(level+1), (byte)index));
					}
				}
			}
			
			for (int c=0;c<leaf.context_f.size();c++){
				// if the context is related to the primary interaction mov and is reliable, add new node to characterize this context
				if (leaf.context_f.get(c).nb_s==10){
					int index=leaf.context_f.get(c).index_primary-InteractionList.nbDF;
					
					if (index!=2 && !(previous==0 && index==1) && !(previous==4 && index==5) && !(previous==5 && index==4)){
						equation_f[index].add(new Node(leaf.context_f.get(c), sig, (byte)(level+1), (byte)index));
					}
				}
			}
		}
		
		
		// define if the subcontext is active according to its equation
		public boolean getResult(Byte[] path){
			
			if (path[level]==-1) return (leaf.getPrediction()==1);
			else if (type==0){
				//for (short i=0;i<=level;i++) System.out.print(" - ");
				//System.out.println("leaf");
				return false;				// end of the projection
			}
			else{
				boolean default_t=(leaf.nb_s_t==100);		// case default true 
				boolean default_f=(leaf.nb_s_f==100);		// case default false
				
				boolean ret_t=false;
				boolean ret_f=false;
				
				int i=0;
				while (!ret_t && i<equation_f[path[level]].size()){
					//for (short l=0;l<=level;l++) System.out.print(" - ");
					//System.out.print("node_f "+i+ ": ");
					if (equation_f[path[level]].get(i).getResult(path)){
						//System.out.println("true");
						ret_t=true;
					}
					//else System.out.println("false");
					i++;
				}
				i=0;
				while (!ret_f && i<equation_t[path[level]].size()){
					//for (short l=0;l<=level;l++) System.out.print(" - ");
					//System.out.print("node_t "+i+ ": ");
					if (equation_t[path[level]].get(i).getResult(path)){
						//System.out.println("true");
						ret_f=true;
					}
					//else System.out.println("false");
					i++;
				}
				
				return (default_t && !ret_f) || (default_f && ret_t);
			}
		}
	

		public void getImage(short[] path, ArrayList<Context> list_t,ArrayList<Context> list_f){
			
			if (path[level]==-1){
				if (type!=0){
					for (int i=0;i<leaf.context_t.size();i++){
						if (leaf.context_t.get(i).nb_s==10) 
							list_t.add(leaf.context_t.get(i));
					}
					for (int i=0;i<leaf.context_f.size();i++){
						if (leaf.context_f.get(i).nb_s==10) 
							list_f.add(leaf.context_f.get(i));
					}
				}
			}
			else{
				if (type!=0){
					for (int i=0;i<equation_t[path[level]].size();i++){
						for (int j=0;j<equation_t[path[level]].get(i).eq.size();j++)
							equation_t[path[level]].get(i).eq.get(j).getImage(path,list_f,list_t);
					}
					for (int i=0;i<equation_f[path[level]].size();i++){
						for (int j=0;j<equation_f[path[level]].get(i).eq.size();j++)
							equation_f[path[level]].get(i).eq.get(j).getImage(path,list_t,list_f);
					}
				}
			}
			
		}
		
		public boolean isPathValid(Byte[] path){
			if (path[level]==-1) return true;
			else{
				if (type==0) return false;
				else{
					for (short i=0;i<equation_t[path[level]].size();i++){
						if (equation_t[path[level]].get(i).isPathValid(path)) return true;
					}
					for (short i=0;i<equation_f[path[level]].size();i++){
						if (equation_f[path[level]].get(i).isPathValid(path)) return true;
					}
					return false;
				}
			}
		}
	
	//-------------------------------------------------------------------------------------------
	// a node is a primitive context with its included contexts
	// a context is under the form of an equation eq && /included
	private class Node{
		public ArrayList<Projection> eq;			// interactions that compose the context
		private ArrayList<Node> included;		// included contexts of this context
		
		public Node(Context c, ArrayList<Signature> sig, byte l, byte previous){
			
			eq=new ArrayList<Projection>();
			included=new ArrayList<Node>();
			
			// add elements of the context
			for (short i=0;i<c.size();i++){
				eq.add(new Projection(sig.get(c.context.get(i)), l, sig, previous) );
			}
			
			// add included contexts
			for (short i=0;i<c.included.size();i++){
				included.add(new Node(c.included.get(i),sig,l, previous));
			}
		}
		
		public boolean getResult(Byte[] path){
			
			short i=0;
			while (i<eq.size()){
				if (!eq.get(i).getResult(path)) return false;
				i++;
			}
			
			i=0;
			while (i<included.size()){
				if (included.get(i).getResult(path)) return false;
				i++;
			}
			
			return true;
		}
		
		public boolean isPathValid(Byte[] path){
			for (short i=0;i<eq.size();i++){
				if (eq.get(i).isPathValid(path)) return true;
			}
			return false;
		}
	}
	
}
