package version11_2_2_2.agnosticMemory;

import java.util.ArrayList;

import version11_2_2_2.Interface.InteractionList;

public class Backmove {

	//public ArrayList<ArrayList<Short>> sequences;
	//public ArrayList<Float> predictions;
	
	public ArrayList<Sequence> seqList;
	
	public Backmove(){
		//sequences=new ArrayList<ArrayList<Short>>();
		//predictions=new ArrayList<Float>();
		
		seqList=new ArrayList<Sequence>();
	}
	
	/*public void backmove(short id, ArrayList<Short> seq, float[] pattern, EnvironmentContext env){
		
		ArrayList<Short> seq_copy=new ArrayList<Short>();
		for (int i=0;i<seq.size();i++) seq_copy.add(seq.get(i));
		
		float[] pattern_copy=new float[InteractionList.length+1];
		
		if (env.interactionList[id].isChange()){
			seq_copy.add(id);
			sequences.add(seq_copy);
			
			// backmove pattern
			if (pattern==null){		// initialization
				for (int i=0;i<InteractionList.length;i++){
					if (env.interactionList[id].signature.signature[i]*env.interactionList[id].signature.getBias()<0
					&& Math.abs(env.interactionList[id].signature.signature[i])>0.5*env.interactionList[id].signature.maxPattern1){
						pattern_copy[i]=env.interactionList[id].signature.signature[i];
					}
					else pattern_copy[i]=0;
				}
				pattern_copy[InteractionList.length]=env.interactionList[id].signature.signature[InteractionList.length];
				
				predictions.add(prediction(pattern_copy, env.visual[env.interactionList[id].length-1]));
			}
			else{	// backmove
				
			}
		}
		else pattern_copy=pattern;

		if (seq_copy.size()<4){
			if (env.interactionList[id].previousDefaultTrue(env) && env.interactionList[id].signature.delta_s<1 && env.interactionList[id].signature.delta_f<1){
				for (int p=0;p<env.interactionList[id].predecessors.size();p++){
					int predecessor=env.interactionList[id].predecessors.get(p);
					if (env.interactionList[predecessor].signature.getBias()>=0 && env.interactionList[predecessor].previousDefaultTrue(env)){
						backmove((short)predecessor, seq_copy, pattern_copy, env);
					}
				}
			}
		}
		pattern=null; // remove the pattern
	}*/
	
	public void backmove(short id, EnvironmentContext env){
		
		// get the pattern
		float[] pattern=new float[InteractionList.length];
		float max=0;
		for (int i=0;i<InteractionList.length;i++){
			if (env.interactionList[id].signature.signature[i]*env.interactionList[id].signature.getBias()<0
			&& Math.abs(env.interactionList[id].signature.signature[i])>0.5*env.interactionList[id].signature.maxPattern1){
				pattern[i]=env.interactionList[id].signature.signature[i];
				if (Math.abs(pattern[i])>max) max=Math.abs(pattern[i]);
			}
			else pattern[i]=0;
		}
		// normalize pattern
		if (max>0.1){
			for (int i=0;i<InteractionList.length;i++){
				pattern[i]=pattern[i]/max;
			}
		}
		
		// generate the sequence
		seqList.add(new Sequence(id, null, pattern));
		pattern=null;
		
		// backmove the sequence
		if (env.interactionList[id].previousDefaultTrue(env) && env.interactionList[id].signature.delta_s<1 && env.interactionList[id].signature.delta_f<1){
			for (int p=0;p<env.interactionList[id].predecessors.size();p++){
				int predecessor=env.interactionList[id].predecessors.get(p);
				if (env.interactionList[predecessor].signature.getBias()>=0 && env.interactionList[predecessor].previousDefaultTrue(env)){
					backmove((short)predecessor, seqList.get(seqList.size()-1), env, (short)1);
				}
			}
		}
	}
	
	public void backmove(short id, Sequence pred, EnvironmentContext env, short level){
		
		Sequence current=null;
		
		if (env.interactionList[id].isChange()){
		
			// backmove pattern
			float[] pattern=new float[InteractionList.length];
			float max=0;
			for (int w=0;w<pred.patternVal.size();w++){
				for (int i=0;i<env.interactionList[id].secondarySignatures[pred.patternWeights.get(w)].nbWeights;i++){
					pattern[env.interactionList[id].secondarySignatures[pred.patternWeights.get(w)].weightList[i]]
					        +=env.interactionList[id].secondarySignatures[pred.patternWeights.get(w)].signature[i]*pred.patternVal.get(w);
					if (Math.abs(pattern[i])>max) max=Math.abs(pattern[i]);
				}
			}
			// normalize pattern
			if (max>0.1){
				for (int i=0;i<InteractionList.length;i++){
					pattern[i]=pattern[i]/max;
				}
			}
			// generate the sequence
			current=new Sequence(id, pred, pattern);
			seqList.add(current);
			pattern=null;
		}
		else current=pred;
		
		// backmove the sequence
		System.out.println(level+" ; "+current.getLength());
		if (level<2){
			if (env.interactionList[id].previousDefaultTrue(env) && env.interactionList[id].signature.delta_s<1 && env.interactionList[id].signature.delta_f<1){
				for (int p=0;p<env.interactionList[id].predecessors.size();p++){
					int predecessor=env.interactionList[id].predecessors.get(p);
					if (env.interactionList[predecessor].signature.getBias()>=0 && env.interactionList[predecessor].previousDefaultTrue(env)){
						backmove((short)predecessor, current, env, (short)(level+1));
					}
				}
			}
		}
	}
	
	public void clear(){
		//sequences.clear();
		//predictions.clear();
		
		seqList.clear();
	}
	
	
	public float[][] getPatterns(ArrayList<Short> seq, EnvironmentContext env){
		// initialization
		float[][] pattern=new float[seq.size()][InteractionList.length];
		
		float max=0;
		
		for (int i=0;i<InteractionList.length;i++){
			if (env.interactionList[seq.get(0)].signature.signature[i]*env.interactionList[seq.get(0)].signature.getBias()<0
			&& Math.abs(env.interactionList[seq.get(0)].signature.signature[i])>0.5*env.interactionList[seq.get(0)].signature.maxPattern1){
				pattern[0][i]=env.interactionList[seq.get(0)].signature.signature[i];
				
				if (Math.abs(pattern[0][i])>max) max=Math.abs(pattern[0][i]);
			}
			else pattern[0][i]=0;
		}
		// normalize pattern
		if (max>0.1){
			for (int i=0;i<InteractionList.length;i++) pattern[0][i]=pattern[0][i]/max;
		}
		
		// backmove pattern
		for (int l=1;l<seq.size();l++){
			
			// projection
			for (int i=0;i<InteractionList.length;i++){
				if (pattern[l-1][i]!=0){
					for (int w=0;w<env.interactionList[seq.get(l)].secondarySignatures[i].nbWeights;w++){
						pattern[l][env.interactionList[seq.get(l)].secondarySignatures[i].weightList[w]]+=env.interactionList[seq.get(l)].secondarySignatures[i].signature[w]
							                                                                              *pattern[l-1][i];
					}
				}
			}
			
			// get max weight of the new pattern
			max=0;
			for (int i=0;i<InteractionList.length;i++){
				if (Math.abs(pattern[l][i])>max) max=Math.abs(pattern[l][i]);
			}
			// normalize pattern
			if (max>0.1){
				for (int i=0;i<InteractionList.length;i++){
					if (Math.abs(pattern[l][i])>0.5) pattern[l][i]=pattern[l][i]/max;
					else pattern[l][i]=0;
				}
			}
		}
		
		return pattern;
	}
	
	
	public float prediction(float[] pattern, float[] img){
		float result=0;
		for (int i=0;i<InteractionList.length;i++){
			result+=pattern[i]*img[i];
		}
		//bias
		result+=pattern[InteractionList.length];
		
		// activation function
		result= (float) ( 1 / (1+exp(-result)))*2-1;

		return result;
	}
	
	public ArrayList<Short> getSeqence(int i){
		return seqList.get(i).getSequence();
	}
	
	// fast exp function approximation
	public static double exp(double val) {
	    final long tmp = (long) (1512775 * val + (1072693248 - 60801));
	    return Double.longBitsToDouble(tmp << 32);
	}
	
	//////////////////////////////////////////////////////////////////////////////////////////
	//
	//////////////////////////////////////////////////////////////////////////////////////////
	private class Sequence{
		
		public short id;
		public Sequence predecessor;
		public ArrayList<Float> patternVal;
		public ArrayList<Short> patternWeights;
		
		public Sequence(short ident, Sequence pred, float[] pattern){
			id=ident;
			predecessor=pred;
			
			patternVal=new ArrayList<Float>();
			patternWeights=new ArrayList<Short>();
			
			for (short i=0;i<pattern.length;i++){
				if (pattern[i]!=0){
					patternVal.add(pattern[i]);
					patternWeights.add(i);
				}
			}
		}
		
		public ArrayList<Short> getSequence(){
			ArrayList<Short> seq=new ArrayList<Short>();
			seq.add(0,id);
			
			if (predecessor!=null){
				Sequence seq2=predecessor;
				
				while (seq2!=null){
					seq.add(0, seq2.id);
					seq2=seq2.predecessor;
				}
			}
			return seq;
		}
		
		public int getLength(){
			int ret=1;
			if (predecessor!=null){
				Sequence seq2=predecessor;
				
				while (seq2!=null){
					ret++;
					seq2=seq2.predecessor;
				}
			}
			return ret;
		}
	}
}
