/*
Copyright (c) 2010, Siemens Corporate Research a Division of Siemens Corporation 
All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/



import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;

import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import org.nema.dicom.wg23.ArrayOfObjectDescriptor;
import org.nema.dicom.wg23.ArrayOfObjectLocator;
import org.nema.dicom.wg23.ArrayOfUUID;
import org.nema.dicom.wg23.AvailableData;
import org.nema.dicom.wg23.ObjectDescriptor;
import org.nema.dicom.wg23.ObjectLocator;
import org.nema.dicom.wg23.Patient;
import org.nema.dicom.wg23.Rectangle;
import org.nema.dicom.wg23.Series;
import org.nema.dicom.wg23.State;
import org.nema.dicom.wg23.Study;
import org.nema.dicom.wg23.Uuid;

import com.pixelmed.dicom.Attribute;
import com.pixelmed.dicom.AttributeList;
import com.pixelmed.dicom.AttributeTag;
import com.pixelmed.dicom.DicomException;
import com.pixelmed.dicom.DicomInputStream;

import edu.wustl.xipApplication.application.ApplicationDataManager;
import edu.wustl.xipApplication.application.ApplicationDataManagerFactory;
import edu.wustl.xipApplication.application.ApplicationTerminator;
import edu.wustl.xipApplication.applicationGUI.ExceptionDialog;
import edu.wustl.xipApplication.application.WG23Application;
import edu.wustl.xipApplication.samples.ApplicationFrameTempl;
import edu.wustl.xipApplication.wg23.ApplicationImpl;
import edu.wustl.xipApplication.wg23.OutputAvailableEvent;
import edu.wustl.xipApplication.wg23.OutputAvailableListener;
import edu.wustl.xipApplication.wg23.WG23DataModel;
import edu.wustl.xipApplication.wg23.WG23DataModelImpl;
import edu.wustl.xipApplication.wg23.WG23Listener;

/**
 * @author Jie Zheng
 * Modified by Lawrence Tarbox to move Swing code onto the Swing Event Dispatch Thread.
 */
public class ImageAnnotation extends WG23Application implements WG23Listener, OutputAvailableListener{
	ImageAnnotationFrame frame = null;			
	
	State appCurrentState;
	ApplicationDataManager dataMgr;
	

	public ImageAnnotation(URL hostURL, URL appURL) throws MalformedURLException {
		super(hostURL, appURL);				
		final ImageAnnotation mainApp = this;

		// Schedule a job for the event-dispatching thread:
		// creating and showing this application's GUI.
		try {
			SwingUtilities.invokeAndWait(new Runnable() {
			    public void run() {
					try {
						UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
						ImageAnnotationFrame frame = new ImageAnnotationFrame(mainApp);
				    	mainApp.setFrame(frame);
					} catch (ClassNotFoundException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (InstantiationException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (UnsupportedLookAndFeelException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
			    }
			});
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		
		/*Notify Host application was launched*/							
		dataMgr = ApplicationDataManagerFactory.getInstance();
		ApplicationImpl appImpl = new ApplicationImpl();
		appImpl.addWG23Listener(this);
		setAndDeployApplicationService(appImpl);		
		getClientToHost().notifyStateChanged(State.IDLE);		
		
		URL outUrl = new URL(getClientToHost().getOutputDir());		
		frame.setOutputDir(outUrl.getFile());
	}
	
	public void setFrame(ImageAnnotationFrame frameIn)
	{
		frame = frameIn;
	}
		
	public static void main(String[] args) {
		try {
			/*args = new String[4];
			args[0] = "--hostURL";
			args[1] = "http://localhost:8090/HostInterface";
			args[2] = "--applicationURL";
			args[3] = "http://localhost:8060/ApplicationInterface";*/	
			System.out.println("Number of parameters: " + args.length);
			for (int i = 0; i < args.length; i++){
				System.out.println(i + ". " + args[i]);
			}
			URL hostURL = null;
			URL applicationURL = null;
			for (int i = 0; i < args.length; i++){
				if (args[i].equalsIgnoreCase("--hostURL")){
					hostURL = new URL(args[i + 1]);
				}else if(args[i].equalsIgnoreCase("--applicationURL")){
					applicationURL = new URL(args[i + 1]);
				}					
			}									
			new ImageAnnotation(hostURL, applicationURL);										
		} catch (MalformedURLException e) {			
			e.printStackTrace();
		} catch (NullPointerException e){
			new ExceptionDialog("List of parameters is not valid!", 
					"Ensure: --hostURL url1 --applicationURL url2",
					"Launch Application Dialog");
			System.exit(0);
		}
	}
	
	public String getSceneGraphInput(List<ObjectLocator> objLocs){
		int size = objLocs.size();
		assert (size > 0);
		
		String filePath;				
		filePath = new File(objLocs.get(0).getUri()).getPath();
		String input = filePath.replace("file:\\", " ").trim();
		int index = input.lastIndexOf("\\");
		
		String folder = input.substring(0, index);
		
		System.out.println("DICOM Folder:" + folder);
		return folder;
	}
	
	public String getAIMInput(String inputFolder){
		assert (inputFolder.length() > 0);
	
		File dir = new File(inputFolder);
		FilenameFilter filter = new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return (name.lastIndexOf(".xml") != -1);
			}
		};
		
		String[] input = dir.list(filter);
		if (input.length > 0)
		{
			String file = inputFolder + "\\" + input[0];
			System.out.println("AIM File: " + file);
			return file;
		}
		
		return "";
	}
	
	public void setupImageData(List<ObjectLocator> objLocs){
		frame.emptyImageData();
		
		for (int i = 0; i < objLocs.size(); i++){
			String filePath = new File(objLocs.get(i).getUri()).getPath();
			String input = filePath.replace("file:\\", " ").trim();
			
			File dcmFile = new File(input);
			DicomInputStream segInput;

			try {
				segInput = new DicomInputStream(dcmFile);
				
				AttributeList tags = new AttributeList();
				tags.read(segInput);
				
				AttributeTag tag = new AttributeTag(0x8, 0x18);
				Attribute attrib = tags.get(tag);
				if (attrib != null){
					String strUID  = attrib.getSingleStringValueOrEmptyString();
					
					frame.setImageData(strUID, input);
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (DicomException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}		
		}
	}
	
	@Override
	public boolean bringToFront() {
		// Schedule a job for the event-dispatching thread:
		// bringing to front.
		SwingUtilities.invokeLater(new Runnable() {
		    public void run() {
				frame.setAlwaysOnTop(true);
				frame.setAlwaysOnTop(false);
		    }
		});
		return true;
		//return false;
	}

	@Override
	public void notifyDataAvailable(AvailableData availableData,
			boolean lastData) {
		ArrayOfUUID arrayUUIDs = new ArrayOfUUID();

		//Extract UUIDs for all dicom objects, only support the first patient/study/series
		List<Patient> patients = availableData.getPatients().getPatient();	
		if (patients.size() <= 0)
			return;
		
		int iPatient = 0;
		Patient patient = patients.get(iPatient);	

		List<Study> studies = patient.getStudies().getStudy();		
		if (studies.size() <= 0)
			return;
		
		int iStudy = 0;
		Study study = studies.get(iStudy);				
		List<Series> listOfSeries = study.getSeries().getSeries();
		
		if (listOfSeries.size() <= 0)
			return;
		
		int iSeries = 0;
		Series series = listOfSeries.get(iSeries);		
		
		List<Uuid> listUUIDs = arrayUUIDs.getUuid();
		ArrayOfObjectDescriptor descriptors = series.getObjectDescriptors();
		List<ObjectDescriptor> listDescriptors = descriptors.getObjectDescriptor();
		for(int m = 0;  m < listDescriptors.size(); m++){
			ObjectDescriptor desc = listDescriptors.get(m);
			listUUIDs.add(desc.getUuid());
		}
		
		ArrayOfObjectLocator objLocs = getClientToHost().getDataAsFile(arrayUUIDs, true);
		List<ObjectLocator> listObjLocs = objLocs.getObjectLocator();

		// Schedule a job for the event-dispatching thread:
		// loading inputs.
		final String inputs = getSceneGraphInput(listObjLocs);
		final String aimFile = getAIMInput(inputs);
		
		setupImageData(listObjLocs);
		
		SwingUtilities.invokeLater(new Runnable() {
		    public void run() {
		    	frame.setSceneGraphInputs(inputs);
		    	frame.loadAIMFile(aimFile);
		    }
		});
		
	}

	@Override
	public boolean setState(State newState) {
		if(State.valueOf(newState.toString()).equals(State.CANCELED)){
			getClientToHost().notifyStateChanged(State.CANCELED);
			getClientToHost().notifyStateChanged(State.IDLE);
		}else if(State.valueOf(newState.toString()).equals(State.EXIT)){
			getClientToHost().notifyStateChanged(State.EXIT);						
			//terminating endpoint and existing system is accomplished through ApplicationTerminator
			//and ApplicationScheduler. ApplicationSechduler is present to alow termination delay if needed (posible future use)
			ApplicationTerminator terminator = new ApplicationTerminator(getEndPoint());
			Thread t = new Thread(terminator);
			t.start();	
		}else{
			getClientToHost().notifyStateChanged(newState);
		}
		return true;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void outputAvailable(OutputAvailableEvent e) {
		List<File> output = (List<File>)e.getSource();
		WG23DataModel wg23DM = new WG23DataModelImpl(output);		
		dataMgr.setOutputData(wg23DM);
		AvailableData availableData = wg23DM.getAvailableData();		
		getClientToHost().notifyDataAvailable(availableData, true);	
	}
}
