package version11_2_2_2.agnosticMemory;

import java.awt.Color;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;

import version11_2_2_2.environment.Robot;
import version11_2_2_2.platform.Observer;
import version11_2_2_2.Interface.ContinuousInteraction;
import version11_2_2_2.Interface.Variation;
import version11_2_2_2.platform.Agent;

/**
 * Environmental context.
 * This class can also generate a predicted and an improved context
 * @author simon gay
 */
public class EnvironmentContext{
	
	private Agent agent;

	public int[] enactedActions;
	public float[][] visual;
	
	public int id0;
	public int id1;

	public int[] timeline;
	public ContinuousInteraction[] interactionList;
	
	public int nbEvent7=0;
	public int nbEvent10=0;
	
	public static int size1=30;					// width of visual system
	public static int size2=15;					// depth of visual system
	
	public static int length=size1*size2*3;		// number of secondary result per group
	
	public EnvironmentContext(Agent a){
		
		agent=a;
		
		enactedActions=new int[11];
		
		visual=new float[11][EnvironmentContext.size1*EnvironmentContext.size2*3+2];
		for (int t=0;t<11;t++){
			for (int i=0;i<EnvironmentContext.size1*EnvironmentContext.size2*3+2;i++){
				visual[t][i]=0;
			}
		}

		id0=-1;
		id1=-1;

		interactionList=new ContinuousInteraction[12210];
		
		timeline=new int[11];
		
		load();
	}
	
	
	/**
	 * update the environmental context
	 * @param inter : last enacted primitive interaction
	 */
	//public void updateContext(int action, int[] percep, PrimitiveInteraction inter, float[] enacted){
	public void updateContext(int variation, float[] percep){	

		System.out.println("Enacted : "+variation);
		
		// update event timeline
		for (int t=10;t>0;t--){
			enactedActions[t]=enactedActions[t-1];
		}
		enactedActions[0]=variation;
		
		// visual results timeline
		for (int t=10;t>0;t--) visual[t]=visual[t-1];
		visual[0]=percep;
		
		// update id0 and get id1 of current interaction
		id0=id1;
		id1=ContinuousInteraction.getId(enactedActions);
		
		// update timeline of enacted interactions
		for (int t=10;t>0;t--){
			timeline[t]=timeline[t-1];
		}
		timeline[0]=id1;
		
		// if id1 does not exist, create it
		if (interactionList[id1]==null){
			interactionList[id1]=new ContinuousInteraction(enactedActions);
		}
		
		// learn signatures
		if (id0!=-1){
			
			// predecessor is the last interaction with at least an event at time 0
			interactionList[id1].addPredecessor(timeline[interactionList[id1].length]);
			
			int length=interactionList[id1].length;				// t of the interaction used as a context
			if (length<11) System.out.println("+++ context : "+length+" ; "+timeline[length]);
			
			// define variation
			Variation var=interactionList[id1].getVar(variation, id1);

			if (interactionList[id0].variations(variation)==null){
				interactionList[id0].setVariation(variation, var);
			}
			
			
			
			////////////////////////////////////////////////////////////////////////////////////////////////////////////
			// learn signature of enacted interaction

			// detect alternative delta
			int counter=0;

			for (int i=0;i<12;i++){
				if (i!=variation && interactionList[id0].variations(i)!=null){
					if (interactionList[id0].variations(variation).isAlternative(interactionList[id0].variations(i))){
						counter++;
						
						int successor=interactionList[id0].variations(i).successor;
						//System.out.println(interactionList[id0].variations(i).getName()+ "alternative of "+variation);
						//System.out.println(" -> learn "+successor+" as a failure in "+timeline[interactionList[successor].length]+" context");
						
						interactionList[successor].signature.learn(visual[interactionList[successor].length], -1);	// learn alternative result of id0 as a failure
					
						/////////////////////////////////////////////////////////////////////////////
						// share learning with surrounding interactions
						// horizontal
						if (successor-1>=110 && interactionList[successor-1]!=null // >=110 because in first row, previous inter was just enacted before
						&& interactionList[successor-1].signature.learned){
							interactionList[successor-1].signature.sharedLearn(visual[interactionList[successor].length], -1);
						}
						if (successor+1<interactionList.length && interactionList[successor+1]!=null 
						&& interactionList[successor+1].signature.learned){
							interactionList[successor+1].signature.sharedLearn(visual[interactionList[successor].length], -1);
						}
						// vertical
						if (successor-110>=110 && interactionList[successor-110]!=null // >=110 because first row is horizontal only
						&& interactionList[successor-110].signature.learned){
							interactionList[successor-110].signature.sharedLearn(visual[interactionList[successor].length], -1);
						}
						if (successor+110<interactionList.length && interactionList[successor+110]!=null 
						&& interactionList[successor+110].signature.learned){
							interactionList[successor+110].signature.sharedLearn(visual[interactionList[successor].length], -1);
						}
						/////////////////////////////////////////////////////////////////////////
					}
				}
			}
			
			if (counter>0 || interactionList[id1].signature.learned){
				//System.out.print("variation : "+interactionList[id0].variations(variation).getName()/*+" ; alternatives : "*/);
				//System.out.println(" -> learn "+interactionList[id0].variations(variation).successor+" as a success with "+id0+" context");
				
				interactionList[id1].signature.learn(visual[length], 1);			// learn id1 as a success
				interactionList[id1].enaction();
				
				/////////////////////////////////////////////////////////////////////////////
				// share learning with surrounding interactions
				// horizontal
				if (id1-1>=110 && interactionList[id1-1]!=null 
				&& interactionList[id1-1].signature.learned){
					interactionList[id1-1].signature.sharedLearn(visual[interactionList[id1].length], 1);
				}
				if (id1+1<interactionList.length && interactionList[id1+1]!=null 
				&& interactionList[id1+1].signature.learned){
					interactionList[id1+1].signature.sharedLearn(visual[interactionList[id1].length], 1);
				}
				// vertical
				if (id1-110>=110 && interactionList[id1-110]!=null 
				&& interactionList[id1-110].signature.learned){	
					interactionList[id1-110].signature.sharedLearn(visual[interactionList[id1].length], 1);
				}
				if (id1+110<interactionList.length && interactionList[id1+110]!=null 
				&& interactionList[id1+110].signature.learned){
					interactionList[id1+110].signature.sharedLearn(visual[interactionList[id1].length], 1);
				}
				/////////////////////////////////////////////////////////////////////////
			}
			
			
			// utilization of the continuity between interactions
			for (int i=0;i<interactionList.length;i++){
				if (interactionList[i]!=null){
					
					// propagate variations
					// vertical
					if (i-110>=0 && i+110<interactionList.length && interactionList[i-110]!=null && interactionList[i+110]!=null){
						for (int v=1;v<12;v++){
							if (interactionList[i].variations(v)==null
							 && interactionList[i+110].variations(v)!=null && interactionList[i-110].variations(v)!=null
							 && interactionList[i+110].variations(v).successor==interactionList[i-110].variations(v).successor){
	
								interactionList[i].setVariation(v, new Variation(v,interactionList[i+110].variations(v).successor));
							}
						}
					}
					// horizontal
					if (i-1>=110 && i+1<interactionList.length && interactionList[i-1]!=null && interactionList[i+1]!=null){
						for (int v=1;v<12;v++){
							if (i>=110 && interactionList[i].variations(v)==null
							 && interactionList[i+1].variations(v)!=null && interactionList[i-1].variations(v)!=null
							 && interactionList[i+1].variations(v).successor-220==interactionList[i-1].variations(v).successor){
								if (interactionList[interactionList[i-1].variations(v).successor+110]!=null)
									interactionList[i].setVariation(v, new Variation(v,interactionList[i-1].variations(v).successor+110));
							}
						}
					}
					// diagonal
					if (i-111>=110 && i+111<interactionList.length && interactionList[i-111]!=null && interactionList[i+111]!=null){
						for (int v=1;v<12;v++){
							if (i>=110 && interactionList[i].variations(v)==null
							 && interactionList[i+111].variations(v)!=null && interactionList[i-111].variations(v)!=null
							 && interactionList[i+111].variations(v).successor-222==interactionList[i-111].variations(v).successor){
								if (interactionList[interactionList[i-111].variations(v).successor+111]!=null)
									interactionList[i].setVariation(v, new Variation(v,interactionList[i-111].variations(v).successor+111));
							}
						}
					}
					
					

					
					// interpolate signatures
					if (!interactionList[i].signature.learned && interactionList[i].signature.signature[EnvironmentContext.length]==0){
						
						// vertical
						if (i-110>=0 && i+110<interactionList.length && interactionList[i-110]!=null && interactionList[i+110]!=null
							&& interactionList[i-110].signature.learned && interactionList[i+110].signature.test_s>0 && interactionList[i-110].signature.learned && interactionList[i+110].signature.test_f>0){
								interactionList[i].signature.interpolation(interactionList[i-110].signature, interactionList[i+110].signature);
						}
						// horizontal
						else if (i-1>=0 && i+1<interactionList.length && interactionList[i-1]!=null && interactionList[i+1]!=null
							&& interactionList[i-1].signature.learned && interactionList[i+1].signature.test_s>0 && interactionList[i-1].signature.learned && interactionList[i+1].signature.test_f>0){
								interactionList[i].signature.interpolation(interactionList[i-1].signature, interactionList[i+1].signature);
						}
						// diagonal
						else if (i-111>=0 && i+111<interactionList.length && interactionList[i-111]!=null && interactionList[i+111]!=null
							&& interactionList[i-111].signature.learned && interactionList[i+111].signature.test_s>0 && interactionList[i-111].signature.learned && interactionList[i+111].signature.test_f>0){
								interactionList[i].signature.interpolation(interactionList[i-111].signature, interactionList[i+111].signature);
						}
					}
				}
			}

			
			// learn secondary interactions of the enacted interaction
			interactionList[id1].learnSecondary(visual[length], visual[0]);
			
			/////////////////////////////////////////////////////////////////////////////
			// share learning with surrounding interactions
			// horizontal
			if (id1-1>=110 && interactionList[id1-1]!=null 
			&& interactionList[id1-1].signature.learned){
				interactionList[id1-1].shareLearnSecondary(visual[length], visual[0]);
			}
			if (id1+1<interactionList.length && interactionList[id1+1]!=null 
			&& interactionList[id1+1].signature.learned){
				interactionList[id1+1].shareLearnSecondary(visual[length], visual[0]);
			}
			// vertical
			if (id1-110>=110 && interactionList[id1-110]!=null 
			&& interactionList[id1-110].signature.learned){	
				interactionList[id1-110].shareLearnSecondary(visual[length], visual[0]);
			}
			if (id1+110<interactionList.length && interactionList[id1+110]!=null 
			&& interactionList[id1+110].signature.learned){
				interactionList[id1+110].shareLearnSecondary(visual[length], visual[0]);
			}
			/////////////////////////////////////////////////////////////////////////
			
			
			// display enacted interaction
			System.out.print("Observed : ");
			interactionList[id0].print();
			System.out.print(" - "+var.getName()+" -> ");
			interactionList[id1].print();
			System.out.println();
		}

		
		////////////////////////////////////////////////////////////////////////////
		// update counters
		nbEvent7=0;
		nbEvent10=0;
		
		for (int t=0;t<7;t++){
			if (enactedActions[t]>0) nbEvent7++;
		}
		for (int t=0;t<10;t++){
			if (enactedActions[t]>0) nbEvent10++;
		}
		
		System.out.println("nb event for last 7 steps : "+nbEvent7+" ; for last 10 steps : "+nbEvent10);
		
		////////////////////////////////////////////////////////////////////////////
		
		
		// update space memory
		agent.spaceMemory.setPredictions();
		agent.spaceMemory.updatePatterns();
		agent.spaceMemory.updateActions();
		
		
		// print detected object instances
		String name="";
		for (int b=0;b<agent.spaceMemory.backmoveList.length;b++){
			//if (backmove!=null){
				if (agent.spaceMemory.backmoveList[b]!=null){
					
					if (interactionList[b].events.get(0)[0]==4 || interactionList[b].events.get(0)[0]==6
					 || interactionList[b].events.get(0)[0]==8 || interactionList[b].events.get(0)[0]==10){
						name="Bump : ";
					}
					else if (interactionList[b].events.get(0)[0]==5 || interactionList[b].events.get(0)[0]==7
					       || interactionList[b].events.get(0)[0]==9 || interactionList[b].events.get(0)[0]==11){
						name="Eat  : ";
					}
					else name="Forw : ";

					for (int i=0;i<agent.spaceMemory.backmoveList[b].detected.size();i++){
							int[] sequence=agent.spaceMemory.backmoveList[b].getEventListDetected(i, (short)getOffset(b), this);
							System.out.print(name);
							for (int k=0;k<sequence.length;k++){
								if (sequence[k]==0) System.out.print(".");
								else if (sequence[k]==10) System.out.print("A");
								else if (sequence[k]==11) System.out.print("B");
								else if (sequence[k]>=100) System.out.print(" ("+(sequence[k]-100)+") ");
								else System.out.print(sequence[k]);
							}
							System.out.println(agent.spaceMemory.backmoveList[b].getDelayDetected(i));
					}
				}
			}
	}
	
	// compute certitude predictions of potential successors of the current interaction
	public void predictNextStep(){
		if (id1>=0){
			for (int i=0;i<12;i++){
				if (interactionList[id1].variations(i)!=null){
					int successor=interactionList[id1].variations(i).successor;
					interactionList[successor].prediction(visual[interactionList[successor].length-1], true);
					interactionList[successor].predictSecondary(visual[interactionList[successor].length-1], true);
				}
			}
		}
	}
	
	public int getOffset(int interaction){
		// get last event
		int t=0;
		int e=-1;
		while (e==-1 && t<11){
			if (enactedActions[t]!=0){
				e=enactedActions[t];
			}
			else t++;
		}
		
		// compare with the interaction
		if (interactionList[interaction].events.size()==1){	
			if (t>=10) return 1;
			else return -1;			// interactions with a unique event require empty context of events
		}
		else{
			if (e!=interactionList[interaction].events.get(1)[0]) return -1;  // not the same event : not compatible
			else return interactionList[interaction].events.get(1)[1]-t;
		}
	}
	
	
	
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// save/load procedures
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public void save(){
		System.out.println("=====================prepare to save agnostic memory...======================");
		
		String fileName = Observer.path+"spaceMemory/spaceMemoryV11_2_2_2 "+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<interactionList.length;i++){
				
				if (interactionList[i]!=null){
					
					// save id, nb enaction and events
					file.print(interactionList[i].id+" "+interactionList[i].nbEnaction+" ");
					for (int e=0;e<interactionList[i].events.size();e++) file.print(interactionList[i].events.get(e)[0]+" "+interactionList[i].events.get(e)[1]+" ");
					file.println();
					
					// save list of predecessor
					for (int p=0;p<interactionList[i].predecessors.size();p++) file.print(interactionList[i].predecessors.get(p)+" ");
					file.println();
					
					// save variations
					for (int v=0;v<11;v++){
						if (interactionList[i].variations(v)!=null) file.print(interactionList[i].variations(v).successor+" "+interactionList[i].nbIntention[v]+" ");
						else file.print(-1+" "+0+" ");
					}
					file.println();
					
					// save signature
					if (!interactionList[i].signature.learned) file.println("0");
					else{
						for (int w=0;w<EnvironmentContext.length+1;w++){
							file.print(interactionList[i].signature.signature[w]+" ");
						}
						file.println();
						file.println(interactionList[i].signature.delta_s+" "+interactionList[i].signature.delta_f);
					}
					
					// save signatures of secondary interactions
					for (int j=0;j<EnvironmentContext.size1*EnvironmentContext.size2*3;j++){
						for (int w=0;w<interactionList[i].secondarySignatures[j].nbWeights;w++){
							file.print(interactionList[i].secondarySignatures[j].signature[w]+" "+interactionList[i].secondarySignatures[j].weightList[w]+" ");
						}
						file.println(interactionList[i].secondarySignatures[j].bias);
						file.println(interactionList[i].secondarySignatures[j].delta_s+" "+interactionList[i].secondarySignatures[j].delta_f);
					}
				}
			}
			file.close();
			System.out.println("memory file saved");
		}
		catch (Exception e) {e.printStackTrace();}
	}
	
	public void load(){
		String fileName = Observer.path+"spaceMemory/spaceMemoryV11_2_2_2 "+agent.getIdent()+".txt";;
		
		String[] elements;
		System.out.println("load file "+fileName);
		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));

			// separator
			line=br.readLine();

			// load signatures
			int i=0;
			while ((line=br.readLine())!=null){
				elements=line.split(" ");
				
				// load interaction
				int id=Integer.parseInt(elements[0]);
				int[] events=new int[11];
				for (int t=0;t<11;t++) events[t]=0;
				for (int e=2;e<elements.length;e+=2) events[Integer.parseInt(elements[e+1])]=Integer.parseInt(elements[e]);
				interactionList[id]=new ContinuousInteraction(events);
				interactionList[id].nbEnaction=Integer.parseInt(elements[1]);
				
				// load list of predecessor
				line=br.readLine();
				elements=line.split(" ");
				for (int p=0;p<elements.length;p++){
					//interactionList[id].predecessors.add(Integer.parseInt(elements[p]));
				}

				// load variations
				line=br.readLine();
				elements=line.split(" ");
				for (int v=0;v<11;v++){
					if (Integer.parseInt(elements[v*2])!=-1){
						interactionList[id].setVariation(v, new Variation(v,Integer.parseInt(elements[v*2])));
						interactionList[id].nbIntention[v]=Short.parseShort(elements[v*2+1]);
					}
				}

				// load signature
				line=br.readLine();
				elements=line.split(" ");
				if (elements.length>1){
					float max=0;
					for (int w=0;w<EnvironmentContext.length+1;w++){
						interactionList[id].signature.signature[w]=Float.parseFloat(elements[w]);
						if (w<EnvironmentContext.length && Math.abs(interactionList[id].signature.signature[w])>max) 
							max=Math.abs(interactionList[id].signature.signature[w]);
					}
					interactionList[id].signature.maxPattern1=max;
					interactionList[id].signature.learned=true;
					
					line=br.readLine();
					elements=line.split(" ");
					interactionList[id].signature.delta_s=Float.parseFloat(elements[0]);
					interactionList[id].signature.delta_f=Float.parseFloat(elements[1]);
				}

				// load signatures of secondary interactions
				for (int j=0;j<EnvironmentContext.size1*EnvironmentContext.size2*3;j++){
					line=br.readLine();
					elements=line.split(" ");
					float max=0;
					
					interactionList[id].secondarySignatures[j].nbWeights=(byte) ((elements.length-1)/2);
					for (int w=0;w<(elements.length-1)/2;w++){
						interactionList[id].secondarySignatures[j].signature[w]=Float.parseFloat(elements[w*2]);
						interactionList[id].secondarySignatures[j].weightList[w]=Short.parseShort(elements[w*2+1]);
						
						if (Math.abs(interactionList[id].secondarySignatures[j].signature[w])>max)
							max=Math.abs(interactionList[id].secondarySignatures[j].signature[w]);
					}
					interactionList[id].secondarySignatures[j].bias=Float.parseFloat(elements[elements.length-1]);
					interactionList[id].secondarySignatures[j].maxPattern1=max;
					
					line=br.readLine();
					elements=line.split(" ");
					interactionList[id].secondarySignatures[j].delta_s=Float.parseFloat(elements[0]);
					interactionList[id].secondarySignatures[j].delta_f=Float.parseFloat(elements[1]);
				}

				if (i%200==0) System.out.println(i);
				i++;
			}
		} 
		catch (Exception e){System.out.println("no file found : space memory");}
		
		// define predecessor
		/*for (int i=0;i<interactionList.length;i++){
			if (interactionList[i]!=null){
				for (int v=0;v<12;v++){
					if (interactionList[i].variations(v)!=null){
						if (v!=0) interactionList[interactionList[i].variations(v).successor].addPredecessor(i);
						else{
							if (interactionList[i].isChange()){
								int j=interactionList[i].variations(v).successor;
								boolean end=false;
								interactionList[j].addPredecessor(i);
								
								while (j!=0 && !end){
									if (interactionList[j].variations(0)==null) end=true;
									else{
										interactionList[interactionList[j].variations(0).successor].addPredecessor(i);
										j=interactionList[j].variations(0).successor;
									}
								}
							}

						}
					}
				}
			}
		}*/
		
	}
}
