package imseProc.gui.swt;

import imseProc.core.IMSEProc;
import imseProc.core.ImagePipeController;
import imseProc.core.Img;
import imseProc.core.ImgPipe;
import imseProc.core.ImgSink;
import imseProc.core.ImgSource;

import java.util.LinkedList;
import java.util.List;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Scale;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;

public class InfoPanel {

	
	Group swtInfoGroup;
	Label swtImageInfoLabel;
	Combo swtSourceCombo;
	Combo swtNewSinkCombo;
	
	Group swtImgCfgComposite;
	Scale swtZoomScale;
	Scale swtColorLogScale;
	Spinner swtColorMinSpinner;
	Spinner swtColorMaxSpinner;
	Button relativeMinMax;
	Button swtCopyImageButton;
	Button swtCopySourceButton;
	Button swtCopyWindowButton;
	Button swtAcceptNewButton;
	Button swtGotoChangedButton;
	Button swtSaveImageButton;
	
	Tree pipeTree;
	
	private ImageWindow imgWin;
	
	public InfoPanel(ImageWindow imgWin, Composite parent, int style) {
		this.imgWin = imgWin;
		
		swtInfoGroup = new Group(parent, SWT.BORDER_SOLID);
        swtInfoGroup.setLayout(new GridLayout(2, false));
        swtInfoGroup.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
        
        swtInfoGroup.setText("Image Information");
        
        new Label(swtInfoGroup, 0).setText("Source:");
        
        swtSourceCombo = new Combo(swtInfoGroup, SWT.DROP_DOWN | SWT.READ_ONLY);        
        swtSourceCombo.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
        swtSourceCombo.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { changeSourceComboEvent(event); } });
                
        new Label(swtInfoGroup, 0).setText("Image:");
        swtImageInfoLabel = new Label(swtInfoGroup, 0);
        swtImageInfoLabel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1));
        
        new Label(swtInfoGroup, 0).setText("Add Sink:");
        swtNewSinkCombo = new Combo(swtInfoGroup, SWT.DROP_DOWN | SWT.READ_ONLY);        
        swtNewSinkCombo.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1));
        swtNewSinkCombo.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { newSinkComboEvent(event); } });
        
		swtImgCfgComposite = new Group(parent, SWT.BORDER_SOLID);
		
        swtImgCfgComposite.setLayout(new GridLayout(4, false));
        swtImgCfgComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
        
        swtImgCfgComposite.setText("View Properties");
        
        Label lZS = new Label(swtImgCfgComposite, 0); lZS.setText("Scale:");
                
        swtZoomScale = new Scale(swtImgCfgComposite, 0);
        swtZoomScale.setMinimum(0);
    	swtZoomScale.setMaximum(100);
    	swtZoomScale.setIncrement(1);
    	swtZoomScale.setSelection(50);
    	swtZoomScale.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
        swtZoomScale.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { zoomScaleEvent(event, false); } });
        
        Label lCLS = new Label(swtImgCfgComposite, 0);
        lCLS.setText("LogColor:");
        swtColorLogScale = new Scale(swtImgCfgComposite, 0);
        swtColorLogScale.setMinimum(0);
        swtColorLogScale.setMaximum(100);
        swtColorLogScale.setIncrement(1);
        swtColorLogScale.setSelection(50);
        swtColorLogScale.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
        swtColorLogScale.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { colorScaleEvent(event, false); } });
    
        Label lCMS = new Label(swtImgCfgComposite, 0);
        lCMS.setText("Min:");
        swtColorMinSpinner = new Spinner(swtImgCfgComposite, 0);
        swtColorMinSpinner.setValues(0, Integer.MIN_VALUE, Integer.MAX_VALUE, 3, 10000, 100000);
        swtColorMinSpinner.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
        swtColorMinSpinner.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { colorScaleEvent(event, false); } });
     
        Label lCXS = new Label(swtImgCfgComposite, 0);
        lCXS.setText("Max:");
        swtColorMaxSpinner = new Spinner(swtImgCfgComposite, 0);
        swtColorMaxSpinner.setValues(1000000, Integer.MIN_VALUE, Integer.MAX_VALUE, 3, 10000, 100000);
        swtColorMaxSpinner.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
        swtColorMaxSpinner.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { colorScaleEvent(event, false); } });
     
        relativeMinMax = new Button(swtImgCfgComposite, SWT.CHECK);
        relativeMinMax.setText("Relative Min/max (0-1000)");
        relativeMinMax.setSelection(true);
        relativeMinMax.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 4, 1));
        relativeMinMax.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { colorScaleEvent(event, false); } });
     
        Label lCP = new Label(swtImgCfgComposite, 0);
        lCP.setText("Copy:");
        lCP.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false, 1, 1));
                       
        swtCopySourceButton = new Button(swtImgCfgComposite, 0);
        swtCopySourceButton.setText("Source");
        swtCopySourceButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { copySourceButtonEvent(event);	}});
        swtCopySourceButton.setToolTipText("Make a new copy of the image source, at the same image index (Which may or may not be the same image instance).");
        swtCopySourceButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false));
       
        swtCopyWindowButton = new Button(swtImgCfgComposite, 0);
        swtCopyWindowButton.setText("Window");
        swtCopyWindowButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { copyWindowButtonEvent(event);	}});
        swtCopyWindowButton.setToolTipText("Make a copy of this window, viewing the same source");
        swtCopyWindowButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false));
        
        swtSaveImageButton = new Button(swtImgCfgComposite, 0);
        swtSaveImageButton.setText("Save Image");
        swtSaveImageButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { saveImageButtonEvent(event);	}});
        swtSaveImageButton.setToolTipText("Saves image to /tmp/imseImage.png");
        swtSaveImageButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false));
       
        Label lMT = new Label(swtImgCfgComposite, 0); lMT.setText("Move to:");
        
        swtAcceptNewButton = new Button(swtImgCfgComposite, SWT.CHECK); 
        swtAcceptNewButton.setText("New images");
        GridData gd1 = new GridData(); gd1.horizontalSpan = 1;
        swtAcceptNewButton.setLayoutData(gd1);        

        swtGotoChangedButton = new Button(swtImgCfgComposite, SWT.CHECK); 
        swtGotoChangedButton.setText("Changed images");
        GridData gd2 = new GridData(); gd2.horizontalSpan = 2;
        swtGotoChangedButton.setLayoutData(gd2);
        
        pipeTree = new Tree(parent, SWT.NONE);
        pipeTree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        pipeTree.addMouseListener(new MouseListener() {			
				@Override public void mouseUp(MouseEvent event) { }			
				@Override public void mouseDown(MouseEvent event) { treeMouseDownEvent(event); }			
				@Override public void mouseDoubleClick(MouseEvent event) { treeDoubleClickEvent(event);  }
			});
        
        Menu popupMenu = new Menu(pipeTree);
        
        MenuItem attachWinItem = new MenuItem(popupMenu, SWT.CASCADE);
        attachWinItem.setText("Attach Window");
        attachWinItem.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { menuAttachWinItemEvent(event); }});
         
        MenuItem refreshItem = new MenuItem(popupMenu, SWT.NONE);
        refreshItem.setText("Refresh");
        refreshItem.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { menuRefreshItemEvent(event); }});
        
        
        MenuItem destroyItem = new MenuItem(popupMenu, SWT.NONE);
        destroyItem.setText("Destroy");
        destroyItem.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { menuDeleteItemEvent(event); }});
         
        pipeTree.setMenu(popupMenu);

        repopulatePipeTree();
	}

	private void menuAttachWinItemEvent(Event event) {
		TreeItem item = pipeTree.getSelection()[0];
		Object o = item.getData();
		if(o instanceof ImgSource){
			new ImageWindow(swtInfoGroup.getDisplay(), (ImgSource)o, 0);
		}
	}
	
	private void menuDeleteItemEvent(Event event) {
		TreeItem item = pipeTree.getSelection()[0];
		Object o = item.getData();
		if(o instanceof ImgPipe){
			((ImgPipe)o).destroy();
		}
		repopulatePipeTree();
	}

	private void menuRefreshItemEvent(Event event) {
		repopulatePipeTree();
	}
	
	private void treeMouseDownEvent(MouseEvent event) {
		
	}
		
	private void treeDoubleClickEvent(MouseEvent event) {
		if(event.button > 1){
			repopulatePipeTree();
			return;
		}
		
		TreeItem item = pipeTree.getSelection()[0];
		
		Object o = item.getData();
		if(o instanceof ImageWindow){ //if clicked on window itself
			((ImageWindow)o).bringToFront();
		}else if(o instanceof ImgSource){
			//or clicked on a source, find the sink that is it's window (or all of them)
			boolean found = false;
			for(ImgSink sink : ((ImgSource)o).getConnectedSinks()){
				if(sink instanceof ImageWindow){
					((ImageWindow)sink).bringToFront();
					found = true;
				}
			}
			if(!found){
				new ImageWindow(swtInfoGroup.getDisplay(), (ImgSource)o, 0);
			}
		}
	}
	
	private void changeSourceComboEvent(Event event){
		String srcShortName = swtSourceCombo.getItem(swtSourceCombo.getSelectionIndex());
		
		LinkedList<ImgSource> srcList = IMSEProc.getSourceInstances();
        for(ImgSource src : srcList){
        	if(src != null && srcShortName.equals(src.toShortString())){
        		imgWin.setSource(src);	
        	}
        }
	}

	private void copySourceButtonEvent(Event event){
		if(imgWin.getConnectedSource() == null) return;
		
		Point p0 = imgWin.getScrollOrigin();
		ImgSource newSource = imgWin.getConnectedSource().clone();
		new ImageWindow(swtInfoGroup.getDisplay(), newSource, imgWin.getSelectedSourceIndex(), imgWin.getScale(), p0.x, p0.y);		
	}

	private void copyWindowButtonEvent(Event event){
		Point p0 = imgWin.getScrollOrigin();
		new ImageWindow(swtInfoGroup.getDisplay(), imgWin.getConnectedSource(), imgWin.getSelectedSourceIndex(), imgWin.getScale(), p0.x, p0.y);
	}
	
	private void saveImageButtonEvent(Event event){
		Image swtImage = imgWin.getSWTImage();
		if(swtImage == null){
			System.err.println("No image to save");
			return;
		}
		org.eclipse.swt.graphics.ImageLoader loader = new org.eclipse.swt.graphics.ImageLoader();
		loader.data = new ImageData[] { swtImage.getImageData() };
		loader.save("/tmp/imseImage.png", SWT.IMAGE_PNG);	       
	}
	   
      
	private void zoomScaleEvent(Event event, boolean centreOnMouse){
		imgWin.zoomScaleEvent(event, centreOnMouse);		
	}
	
	
	private void colorScaleEvent(Event event, boolean centreOnMouse){
		imgWin.colorScaleEvent(event, centreOnMouse);		
	}
	
	private void newSinkComboEvent(Event event){
		String sinkTypeName = swtNewSinkCombo.getItem(swtNewSinkCombo.getSelectionIndex());
		
		if(sinkTypeName != null){
			List<Class> sinkTypes = IMSEProc.getSinkClasses();
			for(Class sinkType : sinkTypes){
	        	if(sinkTypeName.equals(sinkType.getSimpleName())){
	        		
					try {
						ImgSink sink = (ImgSink)sinkType.newInstance();
						sink.setSource(imgWin.getConnectedSource());
						sink.setSelectedSourceIndex(imgWin.getSelectedSourceIndex());
						imgWin.addSinkController(sink);
						
						//if the sink is also a source (i.e. a full pipe), create a new image window with
						// it selected
						if(sink instanceof ImgSource)
							new ImageWindow(swtInfoGroup.getDisplay(), (ImgSource)sink, imgWin.getSelectedSourceIndex());

					} catch (InstantiationException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
	        	}
	        }
		}
        
		imgWin.updateInfoPanel(); //refresh the list (and hence clear the selection) 
		repopulatePipeTree();
	}

	void repopulatePipeTree() {
		pipeTree.setRedraw(false);
		pipeTree.removeAll();
		
		ImgSource src = imgWin.getConnectedSource();
		if(src == null)
			return;
			
		//find root source
		while(src instanceof ImgSink && ((ImgSink)src).getConnectedSource() != null)
			src = ((ImgSink)src).getConnectedSource();
		
		TreeItem rootSourceItem = new TreeItem(pipeTree, SWT.NONE);
		rootSourceItem.setText(src.toString());
		rootSourceItem.setData(src);
		
		populateTree(rootSourceItem, src);
		
		expandTree(rootSourceItem);
		pipeTree.setRedraw(true);
		pipeTree.redraw();
	}
	
	private void populateTree(TreeItem srcItem, ImgSource src){
		
		for(ImgSink sink : src.getConnectedSinks()){
			
			TreeItem nextItem = new TreeItem(srcItem, SWT.NONE);
			nextItem.setText(sink.toString());
			nextItem.setData(sink);
			if(sink == imgWin){
				//nextItem.setBackground(swtInfoGroup.getDisplay().getSystemColor(SWT.COLOR_GRAY));
				nextItem.setForeground(swtInfoGroup.getDisplay().getSystemColor(SWT.COLOR_BLUE));
			}else if(sink instanceof ImageWindow){
				nextItem.setForeground(swtInfoGroup.getDisplay().getSystemColor(SWT.COLOR_DARK_BLUE));				
			}			
			if(sink instanceof ImgSource){ //also a source, so need to recurse
				nextItem.setForeground(swtInfoGroup.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN));	
				populateTree(nextItem, (ImgSource)sink);
			}
			
		}
	}
	
	private void expandTree(TreeItem item0){
		item0.setExpanded(true);
		for(TreeItem item : item0.getItems())
			expandTree(item);		
	}
}
