package version10_2_2_1.agnosticMemory;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;

import version10_2_2_1.Interface.InteractionList;
import version10_2_2_1.Interface.PrimitiveInteraction;
import version10_2_2_1.platform.Agent;
import version10_2_2_1.platform.Observer;

/**
 * List of signatures
 * @author simon
 */
public class SignatureList {

	public static int NB_THREAD=5;
	public static int NB_SIG_PER_THREAD=(int)(InteractionList.length/NB_THREAD)+1;
	
	private Agent agent;
	
	public ArrayList<Signature> signatureList;

	public static boolean load=true;				// load signatures
	
	private float[] context0;
	private float[] context1;
	
	private Thread[] t_predict;
	private Thread[] t_learn;
	
	
	private int counter=0;

	private int nbPaths;
	
	
	public float[][] reliability;
	public int[] curiosity_counter;
	
	public WorkMemory[] workMemory;
	
	
	public SignatureList(Agent a){
		agent=a;

		signatureList=new ArrayList<Signature>();
		for (short i=0;i<InteractionList.length-1;i++){
			signatureList.add(new Signature( agent.interactionList.getInteractionOfIndex(i), i));
		}
		
		t_predict=new Thread[NB_THREAD];
		t_learn  =new Thread[NB_THREAD];

		nbPaths=6;
		for (short i=1;i<Projection.LENGTH;i++) nbPaths=nbPaths*6;
		
		
		if (load) load();
		
		reliability=new float[6][6];
		curiosity_counter=new int[6];
		
		workMemory=new WorkMemory[4];
		for (byte i=0;i<4;i++) workMemory[i]=new WorkMemory(signatureList.get(i+InteractionList.nbDF), this);
	}
	
	public Signature get(int i){
		return signatureList.get(i);
	}
	
	public int size(){
		return signatureList.size();
	}
	
	/**
	 * integrate the last result of enaction cycle
	 * @param input : environemental context E_{t-1} (context before enaction)
	 * @param output: environemental context E_t (interactions defined as a success)
	 * @param enacted : enacted primary interaction
	 */
	public void learn(float[] input, float[] output, PrimitiveInteraction enacted){
		
		int inter=-1;
		if (enacted.getAction().getName().equals(">")){
			if (enacted.getPerception().getId()==1) inter=1;
			else if (enacted.getPerception().getId()==2) inter=2;
			else if (enacted.getPerception().getId()==3) inter=3;
			else inter=0;
		}
		else if (enacted.getAction().getName().equals("v")) inter=4;
		else if (enacted.getAction().getName().equals("^")) inter=5;


		// learn dynamic features if corresponding primitive interaction is enacted
		int min=inter*InteractionList.size*InteractionList.size*4;
		int max=(inter+1)*InteractionList.size*InteractionList.size*4;

		context0=output;
		context1=input;
		
		for (int i=0;i<NB_THREAD;i++) t_learn[i]=new LearnThread(i, min, max);
		for (int i=0;i<NB_THREAD;i++) t_learn[i].start();
		try {
			for (int i=0;i<NB_THREAD;i++) t_learn[i].join();
		} catch (InterruptedException e){e.printStackTrace();}
		
		
		// learn primitive interaction if success or fail
		for (int i=InteractionList.nbDF;i<InteractionList.length-1;i++){
			if (output[i]!=0){
				// learn as success
				signatureList.get(i).learn(input,output[i], signatureList);

				// learn alternative interactions
				if (inter==0){
					signatureList.get(1+InteractionList.nbDF).learn(input,-1, signatureList);
					signatureList.get(2+InteractionList.nbDF).learn(input,-1, signatureList);
					signatureList.get(3+InteractionList.nbDF).learn(input,-1, signatureList);
				}
				else if (inter==1){
					signatureList.get(0+InteractionList.nbDF).learn(input,-1, signatureList);
					signatureList.get(2+InteractionList.nbDF).learn(input,-1, signatureList);
					signatureList.get(3+InteractionList.nbDF).learn(input,-1, signatureList);
				}
				else if (inter==2){
					signatureList.get(0+InteractionList.nbDF).learn(input,-1, signatureList);
					signatureList.get(1+InteractionList.nbDF).learn(input,-1, signatureList);
					signatureList.get(3+InteractionList.nbDF).learn(input,-1, signatureList);
				}
				else if (inter==3){
					signatureList.get(0+InteractionList.nbDF).learn(input,-1, signatureList);
					signatureList.get(1+InteractionList.nbDF).learn(input,-1, signatureList);
					signatureList.get(2+InteractionList.nbDF).learn(input,-1, signatureList);
				}
			}
		}
		
		
		/////////////////////////////////////////////////////////////
		// define reliability
		/////////////////////////////////////////////////////////////
		int group=InteractionList.size*InteractionList.size*4;
		for (int n=0;n<6;n++){
			int sum1=0;
			int[] sum=new int[6];
			int[] nb=new int[6];
			for (int i=n*group;i<(n+1)*group;i++){
				if (get(i).nb_s_t==100 || get(i).nb_s_f==100){
					for (int c=0;c<get(i).context_t.size();c++){
						sum[get(i).context_t.get(c).index_primary-InteractionList.nbDF]+=get(i).context_t.get(c).nb_s;
						nb[get(i).context_t.get(c).index_primary-InteractionList.nbDF]++;
					}
					for (int c=0;c<get(i).context_f.size();c++){
						sum[get(i).context_f.get(c).index_primary-InteractionList.nbDF]+=get(i).context_f.get(c).nb_s;
						nb[get(i).context_f.get(c).index_primary-InteractionList.nbDF]++;
					}
				}
			}
			
			for (int n2=0;n2<6;n2++) sum1+=sum[n2];
			
			System.out.print("reliability of "+ n +" :  "+sum1+" ;  average : ");
			for (int n2=0;n2<6;n2++){
				if (nb[n2]>0) reliability[n][n2]=(float)(sum[n2])/(float)(nb[n2]);
				else reliability[n][n2]=0;
				System.out.print( reliability[n][n2]+", ");
			}
			System.out.println();
		}
		
		
		// set curiosity counter
		for (int i=0;i<6;i++){
			if (inter==i) curiosity_counter[i]=0;
			else if (curiosity_counter[i]<100) curiosity_counter[i]++;
		}
	}
	
	public void setPredictions(float[] img){

		// compute prediction of primary interactions
		for (short i=0;i<6;i++) signatureList.get(i+InteractionList.nbDF).prediction(img);
		
		//setCounter(0);
		
		for (int i=0;i<NB_THREAD;i++) t_predict[i]=new PredictThread(i);
		for (int i=0;i<NB_THREAD;i++) t_predict[i].start();
		try {
			for (int i=0;i<NB_THREAD;i++) t_predict[i].join();
		} catch (InterruptedException e){e.printStackTrace();}
		
		
		// define images of signatures
		if (counter%500==0){

			for (byte i=0;i<4;i++){
				System.out.println("--------------------- update memory "+i+" ----------------------");
				workMemory[i].resetMemory();
				workMemory[i].projection(signatureList.get(InteractionList.nbDF+i));
			}
		}
		
		// compute position of object instances
		if (counter>=2000){

			// detect instances and proto-objects
			for (short k=0;k<4;k++){
				//System.out.println("----------- detection workMemory "+k+" ----------");
				workMemory[k].detectPaths(context0);
			}
			// remove invalid instances and proto-objects, detect movable objects and define hypothesis
			for (short k=0;k<4;k++){
				System.out.println("----------- validation workMemory "+k+" ----------");
				workMemory[k].validePaths(context0);
			}
			for (short k=0;k<4;k++){
				workMemory[k].detectConstructible(this);
			}

			// detect objects that can be constructed
			ArrayList<Hypothesis>[] constructibles=new ArrayList[4];
			for (int k=0;k<4;k++){
				constructibles[k]=workMemory[k].getConstructibles(k);	
			}

			//----------------------------------------------------------------------------
			for (int k=0;k<4;k++){
				for (int c=0;c<constructibles[k].size();c++) constructibles[k].get(c).print();
				System.out.println("**************");
			}
			//----------------------------------------------------------------------------
			
			
			// extract properties of movable objects
			for (int k=0;k<4;k++){
				
				// detect equivalences between movable objects
				workMemory[k].detectEquivalences();
				
				
				// find connections between movable objects and proto-objects
				for (int k2=0;k2<4;k2++){

					// it must exist a "constructible" object k2 and a movable object instance k
					if (k!=k2 && constructibles[k2].size()>0 && workMemory[k].movableInstances.size()>0){
						
						//----------------------------------------------------------------------------
						//System.out.println();
						//System.out.println("   *******************************");
						//System.out.println("   *** movable "+k+"; object "+k2);
						//System.out.println("   *******************************");
						//----------------------------------------------------------------------------
						
						for (int m=0;m<workMemory[k].movableInstances.size();m++){
							
							// list proto-objects of k2 that are recognized in movable object k
							for (int p=0;p<workMemory[k2].pathList.size();p++){
								for (int p2=0;p2<workMemory[k2].pathList.get(p).size();p2++){
									
									int length=workMemory[k2].originList.get(p).get(p2).length;

									if (workMemory[k].movablePattern.get(m).contains(workMemory[k2].originList.get(p).get(p2)[length-1])){
										
										// must begin with the path of the movable
										if (workMemory[k].movableInstances.get(m).get(0).length<=workMemory[k2].pathList.get(p).get(p2).length){
											
											int diff=workMemory[k2].pathList.get(p).get(p2).length-workMemory[k].movableInstances.get(m).get(0).length;
											boolean identical=true;
											int l=workMemory[k].movableInstances.get(m).get(0).length-1;
											while (identical && l>=0){
												if (workMemory[k].movableInstances.get(m).get(0)[l]!=workMemory[k2].pathList.get(p).get(p2)[l+diff]) identical=false;
												l--;
											}
											
											if (identical){
												
												Byte[] temp_path=new Byte[diff];
												boolean default_true=false;
												for (l=0;l<diff;l++){
													temp_path[l]=workMemory[k2].pathList.get(p).get(p2)[l];
													if (get(workMemory[k2].originList.get(p).get(p2)[l+1]).nb_s_t==100) default_true=true;
												}
												
												//----------------------------------------------------------------------------
												//System.out.print(" path {");
												//for (l=temp_path.length-1;l>=0;l--){
												//	System.out.print(temp_path[l]+", ");
												//}
												//System.out.println("] included ");
												//----------------------------------------------------------------------------
												
												// add this path as a property of the movable object
												if (!default_true) workMemory[k].constructible.addProto(k2, temp_path);
											}
										}
									}		
								}
							}
						}
					}
				}
			}
			
			
			
			for (int k=0;k<4;k++){
				
				// define the final list of movable, including extended positions
				workMemory[k].detectFinalMovables();
				
				// if there is a link between the movable object and the object to construct
				for (int k2=0;k2<4;k2++){
			
					if (k!=k2 && workMemory[k].constructible.proto[k2].size()>0){
			
						//----------------------------------------------------------------------------
						System.out.println();
						System.out.println("   *******************************");
						System.out.println("   *** movable "+k+"; object "+k2);
						System.out.println("   *******************************");
						//----------------------------------------------------------------------------
						
						
						// get non-movable parts of the constructible object
						ArrayList<Short> elements=new ArrayList<Short>();
			
						for (int m=0;m<workMemory[k].movableInstances.size();m++){

							// for each hypothesis...
							for (int c=0;c<constructibles[k2].size();c++){
						
								// ... collect non-movable elements and check if the hypothesis is valid (at least one proto-object can be moved with m)
								boolean valide=false;
								ArrayList<Short> temp=new ArrayList<Short>();
								for (int h=0;h<constructibles[k2].get(c).prototypes.size();h++){
							
									int i=0;
									boolean found=false;
									while (!found && i<workMemory[k].movablePattern.get(m).size()){

										if (constructibles[k2].get(c).prototypes.get(h).contains(workMemory[k].movablePattern.get(m).get(i))){
											found=true;
											valide=true;
										}
										i++;
									}

									if (!found){
										for (i=0;i<constructibles[k2].get(c).prototypes.get(h).size();i++){
											temp.add(constructibles[k2].get(c).prototypes.get(h).get(i));
										}
									}
								}
						
								// if the hypothesis is valid, add the collected list of non-moving elements
								if (valide){
									for (int i=0;i<temp.size();i++){
										if (!elements.contains(temp.get(i)))
											elements.add(temp.get(i));
									}
								}
							}
						}
				
						//----------------------------------------------------------------------------
						System.out.print(" --- looks for elements {");
						for (int i=0;i<elements.size();i++) System.out.print(elements.get(i)+", ");
						System.out.println("}");
						//----------------------------------------------------------------------------
						
						// measure variation of distance produced by movable m
						for (int m=0;m<workMemory[k].movableInstances1.size();m++){

							// change reference to movable position
							workMemory[k2].changeReference(workMemory[k].movableInstances1.get(m),0);
							workMemory[k2].changeReference(workMemory[k].movableInstances2.get(m),1);

							for (int e=0;e<elements.size();e++){
								//----------------------------------------------------------------------------
								System.out.println();
								System.out.print(" *** element "+elements.get(e)+" (movable [");
								for (int l=workMemory[k].movableInstances1.get(m).length-1;l>=0;l--) System.out.print(workMemory[k].movableInstances1.get(m)[l]+", ");
								System.out.print("] ---> [");
								for (int l=workMemory[k].movableInstances2.get(m).length-1;l>=0;l--) System.out.print(workMemory[k].movableInstances2.get(m)[l]+", ");
								System.out.println("] )");
								//----------------------------------------------------------------------------
									
								// measure current distance of the proto-object
								ArrayList<Byte[]> minPaths1=new ArrayList<Byte[]>();
								

								for (int proj=0;proj<workMemory[k2].reference[0][elements.get(e)].originList.size();proj++){
									
									// find list of minimal paths in the first projection
									int id=workMemory[k2].reference[0][elements.get(e)].originList.get(proj);
									for (int p=0;p<workMemory[k2].memory[id].size();p++){
										
										boolean found=true;
										
										// path cannot contain the constructed interaction (useless to build something we already have)
										int l=workMemory[k2].memory[id].pathList.get(p).length-1;
										while (found && l>=0){
											if ((int)workMemory[k2].memory[id].pathList.get(p)[l]==k2) found=false;
											l--;
										}
										
										// filter paths that end with an ending sub-path
										int lmax=-1;
										if (found){
											
											for (int i=0;i<workMemory[k].constructible.proto[k2].size();i++){
												if (//workMemory[k].constructible.proto[k2].get(i).length>lmax &&
												  workMemory[k].constructible.proto[k2].get(i).length<=workMemory[k2].memory[id].pathList.get(p).length){
													
													boolean identical=true;
													l=0;
													while (found && l<workMemory[k].constructible.proto[k2].get(i).length){
														if ((int)workMemory[k].constructible.proto[k2].get(i)[l]!=(int)workMemory[k2].memory[id].pathList.get(p)[l]) identical=false;
														l++;
													}
													
													if (identical){
														lmax=workMemory[k].constructible.proto[k2].get(i).length;
														
														Byte[] path_temp=new Byte[workMemory[k2].memory[id].pathList.get(p).length-lmax];
														for (l=0;l<path_temp.length;l++){
															path_temp[l]=workMemory[k2].memory[id].pathList.get(p)[l+lmax];
														}
														minPaths1.add(path_temp);
														
														//----------------------------------------------------------------------------
														//System.out.print("------------> ");
														//for (int j=workMemory[k].constructible.proto[k2].get(i).length-1;j>=0;j--){
														//	System.out.print(workMemory[k].constructible.proto[k2].get(i)[j]+", ");
														//}
														//System.out.print("----end of--> ");
														//for (int j=workMemory[k2].memory[id].pathList.get(p).length-1;j>=0;j--){
														//	System.out.print(workMemory[k2].memory[id].pathList.get(p)[j]+", ");
														//}
														//System.out.println();
														//----------------------------------------------------------------------------
													}
												}
											}
										}
									}
								}
								
								
								//----------------------------------------------------------------------------
								//for (int i=0;i<minPaths1.size();i++){
								//	System.out.print(" --- minPath 1 {");
								//	for (int l=minPaths1.get(i).length-1;l>=0;l--) System.out.print(minPaths1.get(i)[l]+", ");
								//	System.out.println("}");
								//}
								//----------------------------------------------------------------------------

								//////////////////////////////////////////////////////////////////////////////////////////////////////////
								// estimate distance after movement
								ArrayList<Byte[]> minPaths2=new ArrayList<Byte[]>();

								for (int proj=0;proj<workMemory[k2].reference[1][elements.get(e)].originList.size();proj++){
									
									// find list of minimal paths in the second projection
									int id=workMemory[k2].reference[1][elements.get(e)].originList.get(proj);
									for (int p=0;p<workMemory[k2].memory[id].size();p++){
										
										boolean found=true;
										
										// path cannot contain more than one instance of the constructed interaction
										int l=workMemory[k2].memory[id].pathList.get(p).length-1;
										while (found && l>=0){
											if ((int)workMemory[k2].memory[id].pathList.get(p)[l]==k2) found=false;
											l--;
										}
										
										// filter paths that end with an ending sub-path
										int lmax=-1;
										if (found){
											
											for (int i=0;i<workMemory[k].constructible.proto[k2].size();i++){
												if (//workMemory[k].constructible.proto[k2].get(i).length>lmax &&
												  workMemory[k].constructible.proto[k2].get(i).length<=workMemory[k2].memory[id].pathList.get(p).length){
													
													boolean identical=true;
													l=0;
													while (found && l<workMemory[k].constructible.proto[k2].get(i).length){
														if ((int)workMemory[k].constructible.proto[k2].get(i)[l]!=(int)workMemory[k2].memory[id].pathList.get(p)[l]) identical=false;
														l++;
													}
													
													if (identical){
														lmax=workMemory[k].constructible.proto[k2].get(i).length;
														
														Byte[] path_temp=new Byte[workMemory[k2].memory[id].pathList.get(p).length-lmax];
														for (l=0;l<path_temp.length;l++){
															path_temp[l]=workMemory[k2].memory[id].pathList.get(p)[l+lmax];
														}
														minPaths2.add(path_temp);
														
														//----------------------------------------------------------------------------
														//System.out.print("------------> ");
														//for (int j=workMemory[k].constructible.proto[k2].get(i).length-1;j>=0;j--){
														//	System.out.print(workMemory[k].constructible.proto[k2].get(i)[j]+", ");
														//}
														//System.out.print("----end of--> ");
														//for (int j=workMemory[k2].memory[id].pathList.get(p).length-1;j>=0;j--){
														//	System.out.print(workMemory[k2].memory[id].pathList.get(p)[j]+", ");
														//}
														//System.out.println();
														//----------------------------------------------------------------------------
													}
												}
											}
										}
										
									}
								}
								

								//----------------------------------------------------------------------------
								//for (int i=0;i<minPaths2.size();i++){
								//	System.out.print(" --- minPath 2 {");
								//	for (int l=minPaths2.get(i).length-1;l>=0;l--) System.out.print(minPaths2.get(i)[l]+", ");
								//	System.out.println("}");
								//}
								//----------------------------------------------------------------------------


								// remove redundant
								for (int i=0;i<minPaths1.size();i++){
									for (int j=0;j<minPaths1.size();j++){
										if (minPaths1.get(i).length<minPaths1.get(j).length){
											int diff=minPaths1.get(j).length-minPaths1.get(i).length;
											boolean identical=true;
											int l=minPaths1.get(i).length-1;
											while (identical && l>=0){
												if ((int)minPaths1.get(i)[l]!=(int)minPaths1.get(j)[l+diff]) identical=false;
												l--;
											}
											if (identical){
												minPaths1.remove(j);
												if (i<j) j--;
												else{
													i--;
													j--;
												}
											}
										}
									}
								}
								for (int i=0;i<minPaths2.size();i++){
									for (int j=0;j<minPaths2.size();j++){
										if (minPaths2.get(i).length<minPaths2.get(j).length){
											int diff=minPaths2.get(j).length-minPaths2.get(i).length;
											boolean identical=true;
											int l=minPaths2.get(i).length-1;
											while (identical && l>=0){
												if ((int)minPaths2.get(i)[l]!=(int)minPaths2.get(j)[l+diff]) identical=false;
												l--;
											}
											if (identical){
												minPaths2.remove(j);
												if (i<j) j--;
												else{
													i--;
													j--;
												}
											}
										}
									}
								}
								
								//----------------------------------------------------------------------------
								//for (int i=0;i<minPaths1.size();i++){
								//	System.out.print(" --- minPath 1 {");
								//	for (int l=minPaths1.get(i).length-1;l>=0;l--) System.out.print(minPaths1.get(i)[l]+", ");
								//	System.out.println("}");
								//}

								//for (int i=0;i<minPaths2.size();i++){
								//	System.out.print(" --- minPath 2 {");
								//	for (int l=minPaths2.get(i).length-1;l>=0;l--) System.out.print(minPaths2.get(i)[l]+", ");
								//	System.out.println("}");
								//}
								//----------------------------------------------------------------------------
								
								int max_common=0;
								for (int i=0;i<minPaths1.size();i++){
									for (int j=0;j<minPaths2.size();j++){
										if (minPaths1.get(i).length>=minPaths2.get(j).length){
											
											int diff=minPaths1.get(i).length-minPaths2.get(j).length;
											boolean identical=true;
											int l=minPaths2.get(j).length-1;
											int common=0;
											while (identical && l>=0){
												if ((int)minPaths1.get(i)[l+diff]!=(int)minPaths2.get(j)[l]) identical=false;
												else common++;
												l--;
											}
											if (common>max_common) max_common=common;

										}
										else{
											int diff=minPaths2.get(j).length-minPaths1.get(i).length;
											boolean identical=true;
											int l=minPaths1.get(i).length-1;
											int common=0;
											while (identical && l>=0){
												if ((int)minPaths1.get(i)[l]!=(int)minPaths2.get(j)[l+diff]) identical=false;
												else common++;
												l--;
											}
											if (common>max_common) max_common=common;
										}
									}
									
									
								}
								System.out.println(" #### common path : "+max_common);
								int distance=10;
								int variation1=10;     // 10 => no measurable
								int variation2=10;
								
								// detect inclusions
								for (int i=0;i<minPaths1.size();i++){
									for (int j=0;j<minPaths2.size();j++){
										if (minPaths1.get(i).length>minPaths2.get(j).length){
											int diff=minPaths1.get(i).length-minPaths2.get(j).length;
											boolean identical=true;
											int l=minPaths2.get(j).length-1;
											int p=0;
											while (identical && l>=0 && p<max_common){
												if ((int)minPaths1.get(i)[l+diff]!=(int)minPaths2.get(j)[l]) identical=false;
												p++;
												l--;
											}
											if (identical && p==max_common){
												System.out.print("+++++ [");
												for (l=minPaths1.get(i).length-1;l>=0;l--) System.out.print(minPaths1.get(i)[l]+", ");
												System.out.print("] include : [");
												for (l=minPaths2.get(j).length-1;l>=0;l--) System.out.print(minPaths2.get(j)[l]+", ");
												System.out.println("]");
												
												if (diff<variation1) variation1=diff;
												if (minPaths1.get(i).length<distance) distance=minPaths1.get(i).length;
											}
										}
									}
								}
								for (int i=0;i<minPaths1.size();i++){
									for (int j=0;j<minPaths2.size();j++){
										if (minPaths1.get(i).length<minPaths2.get(j).length){
											int diff=minPaths2.get(j).length-minPaths1.get(i).length;
											boolean identical=true;
											int l=minPaths1.get(i).length-1;
											int p=0;
											while (identical && l>=0 && p<max_common){
												if ((int)minPaths1.get(i)[l]!=(int)minPaths2.get(j)[l+diff]) identical=false;
												p++;
												l--;
											}
											if (identical && p==max_common){
												System.out.print("----- [");
												for (l=minPaths1.get(i).length-1;l>=0;l--) System.out.print(minPaths1.get(i)[l]+", ");
												System.out.print("] is included in [");
												for (l=minPaths2.get(j).length-1;l>=0;l--) System.out.print(minPaths2.get(j)[l]+", ");
												System.out.println("]");
												
												if (diff<variation2) variation2=diff;
												if (minPaths1.get(i).length<distance) distance=minPaths1.get(i).length;
											}
											
										}
									}
								}
								for (int i=0;i<minPaths1.size();i++){
									for (int j=0;j<minPaths2.size();j++){
										if (minPaths1.get(i).length==minPaths2.get(j).length){
											boolean identical=true;
											int l=minPaths1.get(i).length-1;
											while (identical && l>=0){
												if ((int)minPaths1.get(i)[l]!=(int)minPaths2.get(j)[l]) identical=false;
												l--;
											}
											if (identical){
												System.out.print("===== [");
												for (l=minPaths1.get(i).length-1;l>=0;l--) System.out.print(minPaths1.get(i)[l]+", ");
												System.out.print("] is equal to [");
												for (l=minPaths2.get(j).length-1;l>=0;l--) System.out.print(minPaths2.get(j)[l]+", ");
												System.out.println("]");
												
												variation1=0;
												variation2=0;
												if (minPaths1.get(i).length<distance) distance=minPaths1.get(i).length;
											}
										}
									}
								}
								
								if ((variation1!=10 && variation2==10) || (variation1==10 && variation2!=10)){
								
									//----------------------------------------------------------------------------
									System.out.print("===> [");
									for (int i=workMemory[k].movables.get(workMemory[k].movableIndex.get(m)).length-1;i>=0;i--){
										System.out.print(workMemory[k].movables.get(workMemory[k].movableIndex.get(m))[i]+", ");
									}
									System.out.print("] : "+distance+" , ");
									if (variation1<10) System.out.print(variation1+" ; ");
									if (variation2<10) System.out.print(-variation2+" ; ");
									System.out.println();
									//----------------------------------------------------------------------------
									
									workMemory[k].setDistance(m, elements.get(e), k2, distance, variation1, variation2);
								}
							}
						}
					}
				}
				//----------------------------------------------------------------------------
				for (int i=0;i<workMemory[k].construction.size();i++){
					if (workMemory[k].construction.get(i).isActive()){
						System.out.print(" ===> "+workMemory[k].construction.get(i).type+" ---> "
								                   +workMemory[k].construction.get(i).element+" + [");
						for (int l=workMemory[k].movables.get(workMemory[k].construction.get(i).indexMovable).length-1;l>=0;l--){
							System.out.print(workMemory[k].movables.get(workMemory[k].construction.get(i).indexMovable)[l]+", ");
						}
						System.out.println("] = "+workMemory[k].construction.get(i).getVariation()+" ; d="+workMemory[k].construction.get(i).getDistance());
					}
				}
				
				//----------------------------------------------------------------------------
				System.out.println();
			}
		}
		counter++;
	}
	


	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// get utility values of interactions
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	// learn unreliable interactions
	public float getLearningUtility(int id, boolean jump, boolean group){
		// utilities of primary interactions
		int result=get(id+InteractionList.nbDF).getLearningUtility(agent.environmentContext.enactedEnsembles[0]);
		
		// utilities of secondary interactions
		if (get(id+InteractionList.nbDF).getPrediction()>0
		|| (get(id+InteractionList.nbDF).getPrediction()>=0 && !jump)
		|| !group
		){
			for (int j=id*InteractionList.size*InteractionList.size*4;j<(id+1)*InteractionList.size*InteractionList.size*4;j++){
				result+=get(j).getLearningUtility(agent.environmentContext.enactedEnsembles[0]);
			}
		}
		return result/InteractionList.length;
	}
	
	


	// move toward attractive objects
	public float[] getInstanceUtility(){
		
		float[] utility=new float[6];
		for (int k=0;k<6;k++) utility[k]=0;
		
		// for each non-enactable interaction
		for (int i=0;i<4;i++){
			if (get(i+InteractionList.nbDF).getPrediction()<0){
			
				for (int j1=0;j1<workMemory[i].instanceList.size();j1++){
						
					int l=workMemory[i].instanceList.get(j1).length;
	
					if (l==0 || get(workMemory[i].instanceList.get(j1)[l-1]+InteractionList.nbDF).getPrediction()>0){
						float val=get(i+InteractionList.nbDF).interaction.valence() * (float)Math.exp(-0.5*(float)l);
						
						if (l>0){
							System.out.print("instance ");
							for (int m=l-1;m>=0;m--) System.out.print(workMemory[i].instanceList.get(j1)[m]+", ");
							System.out.println(" of inter "+i+" add "+val+" to inter "+workMemory[i].instanceList.get(j1)[l-1]);
						}
						if (l>0) utility[workMemory[i].instanceList.get(j1)[l-1]]+=val;
					}
				}
			}
		}
		return utility;
	}

	
	// move toward attractive objects
	public float[] getConstructionUtility(){
		
		float[] utility=new float[6];
		for (int k=0;k<6;k++) utility[k]=0;

		float[] utility_max=new float[6];
 		
		// for each non-enactable interaction
		for (int k=0;k<4;k++){
			
			for (int j=0;j<6;j++) utility_max[j]=-1000000f;
			
			for (int i=0;i<workMemory[k].construction.size();i++){
				
				if (workMemory[k].construction.get(i).isActive()){
					
					System.out.print(" ===> "+workMemory[k].construction.get(i).type+" ---> "
							                   +workMemory[k].construction.get(i).element+" + [");
					for (int l=workMemory[k].movables.get(workMemory[k].construction.get(i).indexMovable).length-1;l>=0;l--){
						System.out.print(workMemory[k].movables.get(workMemory[k].construction.get(i).indexMovable)[l]+", ");
					}
					System.out.println("] = "+workMemory[k].construction.get(i).getVariation()+" ; d="+workMemory[k].construction.get(i).getDistance());
					
					
					int id=workMemory[k].construction.get(i).type;												// object that can be constructed
					int l=workMemory[k].movables.get(workMemory[k].construction.get(i).indexMovable).length;	// length of the sequence
					int d=workMemory[k].construction.get(i).getDistance();										// distance between proto-objects
					
					// compute attractiveness of the constructible affordance
					float val=signatureList.get(id+InteractionList.nbDF).interaction.valence() 
					          * workMemory[k].construction.get(i).getVariation()
					          * (float)Math.exp(-0.001*(float)d);
					
					// compute influence of this constructible affordance
					val=val* (float)Math.exp(-0.5*(float)l);
					
					if (l>0){
						if (val>utility_max[workMemory[k].movables.get(workMemory[k].construction.get(i).indexMovable)[l-1]]){
							utility_max[workMemory[k].movables.get(workMemory[k].construction.get(i).indexMovable)[l-1]]=val;
						}
						System.out.println(" add value "+val+" to "+workMemory[k].movables.get(workMemory[k].construction.get(i).indexMovable)[l-1]);
					}
					else{
						if (val>utility_max[k]){
							utility_max[k]=val;
						}
						System.out.println(" add value "+val+" to "+k);
					}
				}
			}
			for (int j=0;j<6;j++){
				if (utility_max[j]!=-1000000f) utility[j]+=utility_max[j];
			}
		}
		
		for (int j=0;j<6;j++){
			System.out.println(" utilities "+j+" = "+utility[j]);
		}
		
		return utility;
	}
	
	
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// save/load procedures
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public void save(){
		System.out.println("=====================prepare to save agnostic memory...======================");
		
		String fileName = Observer.path+"spaceMemory/spaceMemoryV10_2_2_1 "+agent.getIdent()+".txt";
		
		try {
			PrintWriter file  = new PrintWriter(new FileWriter(fileName));
			
			// save counter
			file.println(agent.action.getNbStep());
			file.println("***");
			
			// save signatures
			for (int i=0;i<signatureList.size();i++){
			  
				// write data related to the signature
				file.println(signatureList.get(i).nb_s_t+" "+signatureList.get(i).nb_test_t+" "+signatureList.get(i).nb_s_f+" "+signatureList.get(i).nb_test_f);
				
				// write the test map
				for (int l=0;l<InteractionList.length;l++) file.print(signatureList.get(i).testMap[l]+" ");
				file.println();
				
				
				// write contexts "true"
				file.println(signatureList.get(i).context_t.size());
				for (int n=0;n<signatureList.get(i).context_t.size();n++){
					saveContext(file, signatureList.get(i).context_t.get(n));
				}
				
				// write contexts "false"
				file.println(signatureList.get(i).context_f.size());
				for (int n=0;n<signatureList.get(i).context_f.size();n++){
					saveContext(file, signatureList.get(i).context_f.get(n));
				}
			}
			file.close();
			System.out.println("memory file saved");
		}
		catch (Exception e) {e.printStackTrace();}
	}
	
	private void saveContext(PrintWriter file, Context c){
		
		// save date related to current context
		file.println(c.index_primary+" "+c.nb_s+" "+c.nb_test+" "+c.failure+" "+c.type+" "+c.level);
		
		// save context itself
		for (int i=0;i<c.context.size();i++){
			file.print(c.context.get(i)+" ");
		}
		file.println();
		
		// save included contexts
		file.println(c.included.size());
		for (int i=0;i<c.included.size();i++){
			saveContext(file, c.included.get(i));
		}
	}
	
	
	public void load(){
		String fileName = Observer.path+"spaceMemory/spaceMemoryV10_2_2_1 "+agent.getIdent()+".txt";;
		
		String[] elements;
		System.out.println("load file");
		try {
			InputStream ips=new FileInputStream(fileName); 
			InputStreamReader ipsr=new InputStreamReader(ips);
			BufferedReader br=new BufferedReader(ipsr);
			String line;
			
			// set agent's step counter
			line=br.readLine();
			agent.action.setNbstep(Integer.parseInt(line));
			counter=Integer.parseInt(line);
			// separator
			line=br.readLine();
			
			// load signatures
			int i=0;
			while ((line=br.readLine())!=null){
				elements=line.split(" ");
				
				// get data related to the signature
				signatureList.get(i).nb_s_t=Byte.parseByte(elements[0]);
				signatureList.get(i).nb_test_t=Byte.parseByte(elements[1]);
				signatureList.get(i).nb_s_f=Byte.parseByte(elements[2]);
				signatureList.get(i).nb_test_f=Byte.parseByte(elements[3]);
				
				// read the test map
				line=br.readLine();
				elements=line.split(" ");
				for (int l=0;l<InteractionList.length;l++) signatureList.get(i).testMap[l]=Byte.parseByte(elements[l]);
				
				// load contexts "true"
				line=br.readLine();
				elements=line.split(" ");
				for (int n=0;n<Integer.parseInt(elements[0]);n++){
					signatureList.get(i).context_t.add(loadContext(br));
				}
				
				// load contexts "false"
				line=br.readLine();
				elements=line.split(" ");
				for (int n=0;n<Integer.parseInt(elements[0]);n++){
					signatureList.get(i).context_f.add(loadContext(br));
				}

				if (i%1000==0) System.out.println(i);
				i++;
			}
		} 
		catch (Exception e){System.out.println("no file found : space memory");}
	}
	
	private Context loadContext(BufferedReader br) throws IOException{
		
		String line;
		String[] elements;
		Context temp=new Context();
		
		// load date related to current context
		line=br.readLine();
		elements=line.split(" ");
		temp.index_primary=Short.parseShort(elements[0]);
		temp.nb_s=Byte.parseByte(elements[1]);
		temp.nb_test=Byte.parseByte(elements[2]);
		temp.failure=Byte.parseByte(elements[3]);
		temp.type=Byte.parseByte(elements[4]);
		temp.level=Byte.parseByte(elements[5]);
		
		
		// load context itself
		line=br.readLine();
		elements=line.split(" ");
		for (int i=0;i<elements.length;i++){
			temp.context.add(Short.parseShort(elements[i]));
		}

		// load included contexts
		line=br.readLine();
		elements=line.split(" ");
		for (int i=0;i<Integer.parseInt(elements[0]);i++){
			temp.included.add(loadContext(br));
		}

		return temp;
	}
	
	
	//////////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////////
	
	private class PredictThread extends Thread {
		
		private int id;
		
		public PredictThread(int i){
			super();
			id=i;
		}
		public void run(){
			
			int min=id*NB_SIG_PER_THREAD;
			int max=Math.min((id+1)*NB_SIG_PER_THREAD, InteractionList.nbDF);
			
			for (int i=min;i<max;i++){
				signatureList.get(i).prediction(context0);
			}
		}
	}
	
	
	private class LearnThread extends Thread {
		
		private int id;
		private int min;
		private int max;
		
		public LearnThread(int i, int minimum, int maximum){
			super();
			id=i;
			min=minimum;
			max=maximum;
		}
		public void run(){
			
			int min2= min + id * ((int)((max-min)/NB_THREAD)+1);
			int max2= Math.min(min +(id+1)* ((int)((max-min)/NB_THREAD)+1) , max);
			
			for (int i=min2;i<max2;i++){
				if (context0[i]==1) signatureList.get(i).learn(context1,1, signatureList);
				else signatureList.get(i).learn(context1,-1, signatureList);
			}
		}
	} 
	
}
