package imseProc.graph;

import imseProc.core.IMSEProc;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import signals.aug.AUGSignal;
import signals.gmds.GMDSSignal;

import descriptors.aug.AUGSignalDesc;
import descriptors.gmds.GMDSSignalDesc;

import mds.AugMDSFetcher;

import algorithmrepository.Interpolation1D;
import algorithmrepository.LinearInterpolation1D;
import base.SignalFetcher;

public class ExtSignalsPlotter {
	
	public static class ExtSignalPlotInfo extends Series implements Comparable<ExtSignalPlotInfo>, Serializable {		
		private static final long serialVersionUID = 7499234410392846985L;
		
		public String name = "";
		public String path = "";
		public double scale = 1.0;
		public double offset = 0;
		/** axis slicing of multidimension arrays, index of all but last dimension */
		public int slice[];
		public boolean enable;
				
		@Override
		public int compareTo(ExtSignalPlotInfo o) {			
			return name.compareTo(o.name);
		}
	}
	
	private GraphUtilProcessor proc;
	private ArrayList<ExtSignalPlotInfo> sigList = new ArrayList<ExtSignalsPlotter.ExtSignalPlotInfo>();
	private boolean changed = false;
		
	public ExtSignalsPlotter(GraphUtilProcessor proc) {
		this.proc = proc;
		load("/default");
	}

	public boolean needsUpdate() {		
		return changed;
	}

	public List<Series> getSeriesList() {		
		Object o = proc.getConnectedSource().getSeriesMetaData("/aug/pulse");
		int pulse = (o != null && o instanceof Integer) ? (Integer)o : 0;
		
		ArrayList<Series> signalList = new ArrayList<Series>();
		
		for(ExtSignalPlotInfo sigInfo : sigList){
			if(!sigInfo.enable)
				continue;
			
			if(sigInfo.data == null || sigInfo.x == null){
					
				SignalFetcher amds = AugMDSFetcher.defaultInstance();
				try{	
					String fullPath = (sigInfo.path.startsWith("/aug") || sigInfo.path.startsWith("aug")) 
											? sigInfo.path 
											: "/aug/" + pulse + "/" + sigInfo.path;
					AUGSignal sig = (AUGSignal) amds.getSig(fullPath);
					
					sigInfo.x = (double[])sig.getTVectorAsType(double.class);
						
					//check entry array matches rank
					if(sigInfo.slice == null)
						sigInfo.slice = new int[0];
					if(sigInfo.slice.length != (sig.getRank()-1)){						
						sigInfo.slice = Arrays.copyOf(sigInfo.slice, sig.getRank()-1);
						System.err.println("Entry list "+sigInfo.slice+" (x,y,z...) doesn't match rank "+sig.getRank()+" of signal " + sig.getDescriptor() + ", resizing.");
					}
							
					//slice down to last dimension by given entry array
					Object o2 = sig.getDataAsType(double.class);
					for(int i=0; i < sigInfo.slice.length; i++){
						o2 = Array.get(o2, sigInfo.slice[i]);
					}
					sigInfo.data = (double[])o2;					
										
					if(sigInfo.scale != 1.0 || sigInfo.offset != 0.0){
						for(int i=0; i < sigInfo.data.length; i++)
							sigInfo.data[i] = sigInfo.data[i] * sigInfo.scale + sigInfo.offset; 
					}
					
				}catch(RuntimeException err){
					err.printStackTrace();
					sigInfo.enable = false;
				}
			}
			
			if(sigInfo.data != null)
				signalList.add(sigInfo);
			
		}
		
		proc.updateAllControllers();
		
		return signalList;
	}
	
	public void save(String name){
		try {
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			ObjectOutputStream oos;
			oos = new ObjectOutputStream(bos);
			oos.writeObject(sigList);			
			byte data[] = bos.toByteArray();
			
			GMDSSignalDesc sigDesc = new GMDSSignalDesc(0, IMSEProc.getMetaExp(proc.getConnectedSource()), "/signalLists/" + name);
			GMDSSignal sig = new GMDSSignal(sigDesc, data);
			sig.writeToCache(IMSEProc.globalGMDS().getCacheRoot());
			
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	public void load(String name){
		try{
			GMDSSignalDesc sigDesc = new GMDSSignalDesc(0, IMSEProc.getMetaExp(proc.getConnectedSource()), "/signalLists/" + name);
			GMDSSignal sig = (GMDSSignal) IMSEProc.globalGMDS().getSig(sigDesc);
			if(sig != null){
				byte data[] = (byte[])sig.getData();
				
				ByteArrayInputStream bis = new ByteArrayInputStream(data);
				ObjectInputStream ois = new ObjectInputStream(bis);
				sigList = (ArrayList<ExtSignalPlotInfo>)ois.readObject(); 
			}
		}catch(Exception e){
			e.printStackTrace();
		}
		
		proc.updateAllControllers();
	}
	
	public String[] loadList(){
		IMSEProc.globalGMDS().dumpTree("", 0, "", true);
		
		String sigsPaths[] = IMSEProc.globalGMDS().dumpTree(IMSEProc.getMetaExp(proc.getConnectedSource()), 0, "signalLists/", true);
		
		return sigsPaths;		
	}

	public void signalsListModified() {
		changed = true;			
	}

	public List<ExtSignalPlotInfo> getSignalsList() { return sigList; }
	
}
