package version7_2_2_2.agnosticMemory;

import version7_2_2_2.Interface.PrimitiveInteraction;
import version7_2_2_2.environment.Environment;
import version7_2_2_2.platform.Agent;

/**
 * Space memory.
 * This class can also generate a predicted and an improved context
 * @author simon gay
 */
public class AgnosticMemory{
	
	private Agent agent;
	
	public float[][] enactedEnsembles;				// enacted ensembles E_t and E_{t-1}
	
	public float[] predictedContext;				// predicted context E_{t+1}
	public float[] improvedMap;					// completed current context E'_t
	
	private boolean predict=false;
	private boolean improve=false;
	
	private Simplified[] maps2;
	
	private float[] maximums;
	
	public float[][] imageMap;
	
	private int length=7;
	
	public Simplified[] patternList;
	
	public AgnosticMemory(Agent a){
		agent = a;
		
		enactedEnsembles=new float[2][SignatureList.length];
		for (int i=0;i<SignatureList.length;i++){
			enactedEnsembles[0][i]=0;
			enactedEnsembles[1][i]=0;
		}
		
		predictedContext=new float[SignatureList.length];
		improvedMap=new float[SignatureList.length];
		
		//maps=new float[length][6][SignatureList.size1][SignatureList.size2][3];
		imageMap=new float[3][SignatureList.length];
		maps2=new Simplified[length];
		
		maximums=new float[length];
		for (int i=0;i<length;i++){
			maps2[i]=new Simplified();
			maximums[i]=1;
		}
		
		patternList=new Simplified[SignatureList.length-1];
		for (int i=0;i<SignatureList.length-1;i++){
			patternList[i]=new Simplified(agent.signatureList.get(i).pattern);
		}
		
	}
	
	
	/**
	 * update the components of the Space Memory
	 * @param inter : last enacted primitive interaction
	 */
	public void updateMemory(PrimitiveInteraction inter){
		
		////////////////////////////////////////////////////////////////////////////////
		// update the components of the Space Memory
		
		// update context
		updateContext(inter);
		
		// update signatures
		agent.signatureList.learn(enactedEnsembles[1],enactedEnsembles[0] , inter);
		
		// compute predictions
		agent.signatureList.setPredictions(enactedEnsembles[0]);
		
		////////////////////////////////////////////////////////////////////////////////
		// predict current context with previous environmental context
		float max=0;
		
		if (predict){
			
			for (int i=0;i<SignatureList.length-1;i++){
				predictedContext[i]=0;
			}
			for (int i=0;i<SignatureList.length-1;i++){
				if (agent.signatureList.get(i).average_delta1<0.7 && agent.signatureList.get(i).average_delta2<0.7){
					float prediction=agent.signatureList.get(i).prediction(enactedEnsembles[0]);
					predictedContext[i]=prediction;
				}
			}
		}
		
		////////////////////////////////////////////////////////////////////////////////
		// improve interactional context
		if (improve){
			float prediction=0;
			
			max=0;
			
			// reset improved context
			for (int e=0;e<SignatureList.length-1;e++){
				improvedMap[e]=0;
			}
				
			// for each primitive interaction
			for (int e=0;e<SignatureList.length-1;e++){
				
				// get the prediction in current context
				prediction=agent.signatureList.get(e).prediction(enactedEnsembles[0]);
				
				for (int i=0;i<SignatureList.length-1;i++){
					if (agent.signatureList.get(e).pattern[i]>7)
						improvedMap[i]+= prediction * agent.signatureList.get(e).pattern[i];
					
					if (Math.abs(improvedMap[i])>max) max=Math.abs(improvedMap[i]);
				}
			}
			
			if (max>1){
				for (int e=0;e<SignatureList.length-1;e++){
					improvedMap[e]=improvedMap[e]/(max/5);
				}
			}
		}
		
		// detect object instances
		detect(1);
		detect(2);
	}
	
	//////////////////////////////////////////////////////////////////////////////////////////////////
	// set interactional context
	public void updateContext(PrimitiveInteraction enacted){
		
		// update timeline
		for (int i=0;i<SignatureList.length;i++){
			enactedEnsembles[1][i]=enactedEnsembles[0][i];
			enactedEnsembles[0][i]=0;
		}
		
		int inter=0; // used to define the enacted DFs
		
		// interactions
		if (enacted.getAction().getName().equals(">")){
			if (enacted.getPerception().getId()==1){
				enactedEnsembles[0][SignatureList.nbDF +1]=1;
			}
			else if (enacted.getPerception().getId()==2){
				enactedEnsembles[0][SignatureList.nbDF +2]=1;
				inter=2;
			}
			else{
				enactedEnsembles[0][SignatureList.nbDF   ]=1;
				inter=1;
			}
		}
		else if (enacted.getAction().getName().equals("v")){
			enactedEnsembles[0][SignatureList.nbDF +3]=1;
			inter=3;
		}
		else if (enacted.getAction().getName().equals("^")){
			enactedEnsembles[0][SignatureList.nbDF +4]=1;
			inter=4;
		}
		else if (enacted.getAction().getName().equals("\\")){
			enactedEnsembles[0][SignatureList.nbDF +5]=1;
			inter=5;
		}
		else if (enacted.getAction().getName().equals("/")){
			enactedEnsembles[0][SignatureList.nbDF +6]=1;
			inter=6;
		}
		enactedEnsembles[0][SignatureList.nbDF +7]=1;  // bias input
		
		// dynamic features
		if (inter>0){
			for (int i=0;i<agent.visual.color.length;i++){
				if (agent.visual.opticFlow[i]!=0 && agent.visual.opticFlow[i]<49){
					int col=0;
					if (agent.visual.color[i].equals(Environment.WALL1)) col=1;
					else if (agent.visual.color[i].equals(Environment.FISH1)) col=2;
					else if (agent.visual.color[i].equals(Environment.ALGA1)) col=3;
					
					if (col!=0){
	
						float theta=(float) Math.toRadians((i*5)-90);
						
						float x= (float) ( (agent.visual.opticFlow[i]/3)*Math.sin(theta))+(SignatureList.size1/2);
						float y= (float) ( (agent.visual.opticFlow[i]/3)*Math.cos(theta));
	
						if (x>=0 && x<SignatureList.size1 && y>=0 && y<SignatureList.size2){
							int n= ((int)x * SignatureList.size2*3 + (int)y*3 + (col-1)) + (inter-1)*SignatureList.size1*SignatureList.size2*3; 
							enactedEnsembles[0][n]=1f;
						}
					}
				}
			}
		}
	}
	
	
	///////////////////////////////////////////////////////////////////////
	// detect object instances
	public void detect(int o){
		
		System.out.println();
		System.out.println("////////////// object "+o+" //////////////");
		
		// generate a set of sequences of movements
		int[] sequence=new int[length+1];
		for (int i=0;i<length+1;i++){
			sequence[i]=-1;
		}
		sequence[0]=o;
		
		for (int n=0;n<SignatureList.length;n++){
			imageMap[o][n]=0;
		}
		
		boolean stop=false;
		while (!stop){
			
			/*for (int i=0;i<length;i++){
				System.out.print(sequence[i]+" ");
			}
			System.out.println();*/
			
			detectInstance(sequence);
			
			sequence[1]++;
			for (int i=1;i<length-1;i++){
				if (sequence[i]>length){
					sequence[i]=0;
					sequence[i+1]++;
				}
			}
			if (sequence[length-1]>5) stop=true;
		}
	}
	
	
	public void detectInstance(int[] seq){
		
		// fill initial signature
		maximums[0]=1;
		
		// get initial signature
		maps2[0]=agent.agnosticMemory.patternList[SignatureList.nbDF+seq[0]].clone();
		maximums[0]=maps2[0].getMax();
		/*for (int n=0;n<6;n++){
			for (int i=0;i<SignatureList.size1;i++){
				for (int j=0;j<SignatureList.size2;j++){
					for (int c=0;c<3;c++){
						maps[0][n][i][j][c]=agent.signatureList.get(SignatureList.nbDF+seq[0]).pattern[i*SignatureList.size2*3+j*3+n*SignatureList.size1*SignatureList.size2*3+c];
						if (Math.abs(maps[0][n][i][j][c])>maximums[0]) maximums[0]=Math.abs(maps[0][n][i][j][c]);
					}
				}
			}
		}
		// remove noise
		for (int n=0;n<6;n++){
			for (int i=0;i<SignatureList.size1;i++){
				for (int j=0;j<SignatureList.size2;j++){
					for (int c=0;c<3;c++){
						if (Math.abs(maps[0][n][i][j][c])<maximums[0]/3 && Math.abs(maps[0][n][i][j][c])<5) maps[0][n][i][j][c]=0;
					}
				}
			}
		}*/
		
		
		
		// compute prediction of the current signature image
		float predict=maps2[0].predict(agent.agnosticMemory.enactedEnsembles[0]);
		/*float predict=0;
		for (int n=0;n<6;n++){
			for (int i=0;i<SignatureList.size1;i++){
				for (int j=0;j<SignatureList.size2;j++){
					for (int c=0;c<3;c++){
						predict+= maps[0][n][i][j][c] 
						         *agent.agnosticMemory.enactedEnsembles[0][n*SignatureList.size1*SignatureList.size2*3 + i*SignatureList.size2*3 + j*3 + c];
					}
				}
			}
		}
		predict+=agent.signatureList.get(SignatureList.nbDF+seq[0]).pattern[SignatureList.length-1];
		predict= (float) ( 1 / (1+Math.exp(-predict)))*2-1;
		*/
		
		
		
		// generate moved signatures
		int a=1;
		while (a<5 && seq[a]!=-1){
		
			// reset map
			maps2[a].clear();
			/*for (int n=0;n<6;n++){
				for (int i=0;i<SignatureList.size1;i++){
					for (int j=0;j<SignatureList.size2;j++){
						for (int c=0;c<3;c++){
							maps[a][n][i][j][c]=0;
						}
					}
				}
			}*/
			
			// compute map
			for (int i=0;i<maps2[a-1].size();i++){
				if (maps2[a-1].getIndex(i)>=seq[a]*SignatureList.size1*SignatureList.size2*3 && maps2[a-1].getIndex(i)<(seq[a]+1)*SignatureList.size1*SignatureList.size2*3){
					System.out.println("+++ "+maps2[a-1].getIndex(i)+" ; "+(seq[a]+1)*SignatureList.size1*SignatureList.size2*3+" ; "+seq[a]);
					Simplified sig=agent.agnosticMemory.patternList[maps2[a-1].getIndex(i)];
					float max=sig.getMax();
					for (int j=0;j<sig.size();j++){
						if (sig.getIndex(j)<SignatureList.nbDF){
							if (Math.abs(sig.getWeight(j))>max/3 && Math.abs(sig.getWeight(j))>5){
								maps2[a].insert(sig.getIndex(j), maps2[a-1].getWeight(i) * sig.getWeight(j) /max );
							}
						}
					}
				}
				
			}
			maximums[a]=maps2[a].getMax();
			maps2[a].insert(SignatureList.length-1, maps2[a-1].getWeight(maps2[a-1].size()-1));
			
			/*maximums[0]=1;
			for (int i=0;i<SignatureList.size1;i++){
				for (int j=0;j<SignatureList.size2;j++){
					for (int c=0;c<3;c++){
						
						// get the signature
						Signature sig=agent.signatureList.get(i*SignatureList.size2*3+j*3+seq[a]*SignatureList.size2*SignatureList.size1*3+c);
						
						// find the maximum weight
						float max=0;
						for (int n2=0;n2<6;n2++){
							for (int i2=0;i2<SignatureList.size1;i2++){
								for (int j2=0;j2<SignatureList.size2;j2++){
									for (int c2=0;c2<3;c2++){
										if (Math.abs(sig.pattern[i2*SignatureList.size2*3+j2*3+n2*SignatureList.size2*SignatureList.size1*3+c2])>max)
											max=Math.abs(sig.pattern[i2*SignatureList.size2*3+j2*3+n2*SignatureList.size2*SignatureList.size1*3+c2]);
									}
								}
							}
						}
						
						// apply the signature
						for (int n2=0;n2<6;n2++){
							for (int i2=0;i2<SignatureList.size1;i2++){
								for (int j2=0;j2<SignatureList.size2;j2++){
									for (int c2=0;c2<3;c2++){
										if (Math.abs(sig.pattern[i2*SignatureList.size2*3+j2*3+n2*SignatureList.size2*SignatureList.size1*3+c2])>max/3 && Math.abs(sig.pattern[i2*SignatureList.size2*3+j2*3+n2*SignatureList.size2*SignatureList.size1*3+c2])>5){
											maps[a][n2][i2][j2][c2]+= maps[a-1][seq[a]][i][j][c] * sig.pattern[i2*SignatureList.size2*3+j2*3+n2*SignatureList.size2*SignatureList.size1*3+c2]/max;
											if (Math.abs(maps[a][n2][i2][j2][c2])>maximums[0]) maximums[0] = Math.abs(maps[a][n2][i2][j2][c2]);
										}
									}
								}
							}
						}
					}
				}
			}*/
			
			
			// compute prediction of the current signature image
			if (seq[a+1]==-1){
				
				predict=maps2[a].predict(agent.agnosticMemory.enactedEnsembles[0]);
				
				/*predict=0;
				for (int n=0;n<6;n++){
					for (int i=0;i<SignatureList.size1;i++){
						for (int j=0;j<SignatureList.size2;j++){
							for (int c=0;c<3;c++){
								predict+= maps[a][n][i][j][c] 
								         *agent.agnosticMemory.enactedEnsembles[0][n*SignatureList.size1*SignatureList.size2*3 + i*SignatureList.size2*3 + j*3 + c];
							}
						}
					}
				}
				predict+=agent.signatureList.get(SignatureList.nbDF+seq[0]).pattern[SignatureList.length-1];
				predict= (float) ( 1 / (1+Math.exp(-predict)))*2-1;
				*/
				boolean found=false;
				for (int i=1;i<length;i++){
					if (seq[i]==1) found=true;
				}
				
				if (predict>0 && !found){
					
					switch (seq[0]){
					case 0 : System.out.print(">0 ");break;
					case 1 : System.out.print(">X ");break;
					case 2 : System.out.print(">F ");break;
					case 3 : System.out.print("v  ");break;
					case 4 : System.out.print("^  ");break;
					case 5 : System.out.print("\\  ");break;
					case 6 : System.out.print("/  ");break;
					default : System.out.print("   ");break;
					}
					
					for (int i=1;i<=a;i++){
						switch (seq[i]){
						case 0 : System.out.print(">0 ");break;
						case 1 : System.out.print(">F ");break;
						case 2 : System.out.print("v  ");break;
						case 3 : System.out.print("^  ");break;
						case 4 : System.out.print("\\  ");break;
						case 5 : System.out.print("/  ");break;
						default : System.out.print("   ");break;
						}
					}
					System.out.println(" ; "+predict);
					
					if (predict>0.99){
						for (int i=0;i<maps2[a].size();i++){
							imageMap[seq[0]][maps2[a].getIndex(i)]=maps2[a].getWeight(i);
						}
						/*for (int n=0;n<6;n++){
							for (int x=0;x<SignatureList.size1;x++){
								for (int y=0;y<SignatureList.size2;y++){
									for (int c=0;c<3;c++){
										imageMap[seq[0]][n][x][y][c]+=predict * maps[a][n][x][y][c];
									}
								}
							}
						}*/
					}
				}
			}
			a++;
		}
	}
	
}
