/*
 * Delivered to the U.S. Government in Accordance with Contract FA8650-10-C-7080.
 *
 * EXPORT CONTROLLED
 * ECCN#   EAR99
 * Warning: This document contains technical data which is controlled under the
 * Bureau of Industry & Security's Export Administration Regulations and may not be
 * exported or re-exported without authorization by the Bureau of Industry & Security.
 * Diversion contrary to US law is prohibited.
 *
 * (c) 2011 United Technologies Corp.
 * Developed with the sponsorship of the Defense Advanced Research Projects Agency (DARPA).
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this data,
 * including any software or models in source or binary form, as well as any drawings, 
 * specifications, and documentation (collectively "the Data"), to deal in the Data without 
 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Data, and to permit persons to whom the Data
 * is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Data.
 *
 * THE DATA IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS, SPONSORS, DEVELOPERS, CONTRIBUTORS, OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE DATA OR THE
 * USE OR OTHER DEALINGS IN THE DATA.
*/

package view;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Set;

import meta.rmi.ServiceResponse;

import org.jdom.Element;

import properties.ViewProperties;
import proxy.ArchitectureResult;
import proxy.ArchitectureList;
import proxy.clients.RepositoryProxy;
import exception.ControllerException;
import exception.ViewException;

/**
 * Controller for the application view
 */
public class ViewController {
	private Thread applicationView;
	
	private static final String  RepositoryHost = "localhost";
	
	/**
	 * Constructor
	 * 
	 * Starts by communicating with the repository proxy to get a list of 
	 * all of the architectures.  Once obtained, the constructor configures
	 * the threads that will be used to run both the application view as 
	 * well as the TSV application.
	 * 
	 * @param platform			The platform name
	 * @param level				The level
	 * @throws ViewException	Exceptions thrown by the view
	 * @throws ControllerException 
	 */
	public ViewController(String platform, String level) 
		throws ViewException, ControllerException {
		
		applicationView = null;
		
		// Get the architectures from the repository
		RepositoryProxy repository = new RepositoryProxy(RepositoryHost);
		ServiceResponse response   = repository.getAllArchitectures(platform, level);
		
		ArchitectureList arches = parseArchitectures(response);
		
		// Build the visualization file from the architectures
		VisualizationFile visFile = new VisualizationFile(arches);
		visFile.create(ViewProperties.getInstance()
                                 .get("TSVFilename"));
		
		// Configure the application view
		configureAppView(arches.getAllIds(), repository, platform, level);
	}
	
	/**
	 * Parse the architecture data
	 * @param response The GetAllArchitectures service response
	 * @return A table indexed by architecture ID of all of the architecture results
	 * @throws ViewException if a service response problem is detected
	 */
	private ArchitectureList parseArchitectures(ServiceResponse response) 
	throws ViewException {
		ArchitectureList archmap = new ArchitectureList();
		
		// Can only be a single composite argument element
		Element resp   		    = response.getXmlContent();
		List<?> architectures = resp.getChildren("composite_argument");
		int     n       	    = architectures.size();
		if (n != 1)
			throw new ViewException("Unexpected \"architectures\" data. %d " +
					                    "composite_argument values are present. Should be 1", n);
		
		// That composite argument must be of type architectures
		Element arches    = (Element) architectures.get(0);
		String  childtype = arches.getAttribute("type").getValue();
		if (! childtype.equals("architectures"))
			throw new ViewException("Unexpected \"architectures\" data. %s " +
                              "type found", childtype);
		
		// Cycle through each child (architecture) and parse it
		for (Object arch : arches.getContent()) {
			ArchitectureResult result = new ArchitectureResult((Element) arch);
			archmap.add(result);
		}
		
		return archmap;
	}
	
	/**
	 * Create the application view thread
	 * @param architectureList List of architectures that will be fed
	 * 						   into the application view
	 * @param repository       Proxy to the repository
	 * @param platform			The platform name
	 * @param level				The level
	 */
	private void configureAppView(final Set<Integer> architectureList,
			                          final RepositoryProxy repository,
			                          final String platform,
			                          final String level) {
		applicationView = new Thread("ApplicationView") {
			public void run(){
		      System.out.println("ApplicationView Running");
		      new ApplicationView(architectureList, repository, platform, level);
			}
		};
	}
	
	/**
	 * Launch the TSV process in the background
	 * @throws IOException if any error starting TSV
	 */
	private void startTsv() throws IOException {
		System.out.println("TSVView Running"); 
		 
		File workingDir = new File(ViewProperties.getInstance().get("TSVWorking"));
		ProcessBuilder pb = new ProcessBuilder(ViewProperties.getInstance()
				                                                 .get("TSVAppl"),
				                                   ViewProperties.getInstance()
				                                                 .get("TSVFilename"));
	 	pb.directory(workingDir);
		Process tsvproc = pb.start();
		drainOutput("TSV-stdout", tsvproc.getInputStream());
		drainOutput("TSV-stderr", tsvproc.getErrorStream());
	}
	
	/**
	 * Drain a process stream by writing the stream to stdout
	 * @param streamName  Name of the stream
	 * @param stream      The stream to drain
	 */
	private void drainOutput(String streamName, final InputStream stream) {
  	new Thread(streamName) {
			@Override
			public void run() {
	    	BufferedReader in = new BufferedReader(new InputStreamReader(stream));
	    	try {
	    		String line = null;
	    		while (( line = in.readLine()) != null)	    {
			        System.out.println(line);
	    		}
	    	} catch (IOException ioex) {
	    		System.out.println(ioex.getMessage());
	    		
	    	} finally {
	    		try { in.close(); } catch (IOException ioex) { }
	    	}
			}
  	}.start();
	}

	/**
	 * Start the views: the application view thread and the TSV background process
	 */
	public void startViews() throws ViewException {
		try {
			applicationView.start();
			startTsv();
			
		} catch (IOException e) {
			throw new ViewException(e.getMessage());
		}
	}
	
	/**
	 * The Meta View application
	 */
	public static void main(String[] args) {
		try {
			(new ViewController("engine","0"))
				.startViews();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
