/*
 * 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 proxy.clients;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import meta.rmi.Service;
import meta.rmi.ServiceResponse;

import org.jdom.Document;
import org.jdom.Element;

import proxy.ArchitectureProxyElement;
import proxy.ArgumentProxyElement;
import proxy.CompositeArgumentProxyElement;
import proxy.CompositeArgumentProxySource;
import proxy.FuelConsumptionProxy;
import proxy.ProxyElement;
import proxy.SimpleProxyElement;
import exception.ControllerException;

/**
 * A proxy to the repository service
 */
public class RepositoryProxy extends SOAProxy {
	private String  host;
	private Service service;

	/**
	 * Constructor
	 *
	 * @param host Where the repository service is running
	 */
	public RepositoryProxy(String host) {
		this.host    = host;
		this.service = null;
	}

	@Override
	public Service getService() throws MalformedURLException, RemoteException, NotBoundException {
		if (service == null)
			service = (Service) Naming.lookup(String.format("rmi://%s/repository", host));
		return service;
	}
	
	/**
	 * Store a platform definition. Executes Repository's StorePlatform
	 * @param doc A <platform> definition
	 * @return Service response from the repository
	 * @throws ControllerException if any problem saving the platform
	 */
	public ServiceResponse storePlatform(Document doc)
		throws ControllerException {

		// Have to be supplied with a <platform>
		List<ProxyElement> args     = new ArrayList<ProxyElement>();
		Element            platform = doc.getRootElement();
		if (! "platform".equalsIgnoreCase(platform.getName()))
			throw new ControllerException("Element \"%s\" is not a platform",
					                           platform.getName());
		
		// Platform and level arguments
		args.add(new ArgumentProxyElement("platform", "string",
				                               platform.getAttributeValue("name"),""));
		args.add(new ArgumentProxyElement("level", "integer",
                                       platform.getAttributeValue("level"),""));
		
		// Each composite argument is also added to the service request
		List<?> compositeArgs = platform.getChildren("composite_argument");
		for (Object ca : compositeArgs) {
			Element comparg = (Element) ca;
			args.add(new SimpleProxyElement(comparg));
		}

		return run("StorePlatform", args);
	}
	
	/**
	 * Store a mission definition. Executes Repository's StoreMission
	 * @param doc A <mission-segment> definition
	 * @return Service response from the repository
	 * @throws ControllerException if any problem saving the mission
	 */
	public ServiceResponse storeMission(Document doc)
		throws ControllerException {

		// Have to be supplied with a <mission-segment>
		List<ProxyElement> args    = new ArrayList<ProxyElement>();
		final Element      mission = doc.getRootElement();
		if (! "mission-segment".equalsIgnoreCase(mission.getName()))
			throw new ControllerException("Element \"%s\" is not a mission",
					                           mission.getName());
		
		CompositeArgumentProxySource source = new CompositeArgumentProxySource() {

			@SuppressWarnings("unchecked")
			@Override
			public List<Element> getElements() throws ControllerException {
				return mission.getChildren();
			}

			@Override
			public String getName() throws ControllerException {
				String name = mission.getAttributeValue("name");
				if (name == null)
					throw new ControllerException("Element \"%s\" is missing a name attribute",
						                             getType());
				return name;
			}

			@Override
			public String getType() throws ControllerException {
				return mission.getName();
			}
			
		};
		args.add(new CompositeArgumentProxyElement(source));
		return run("StoreMission", args);
	}
	
	/**
	 * Store an architecture. Executes Repository's StoreArchitectures
	 * @param platform  The platform name
	 * @param level     The current level
	 * @param arch      The architecture definition
	 * @return Service response from the repository
	 * @throws ControllerException if any problem saving the architecture
	 */
	public ServiceResponse storeArchitectures(final String platform,
			                                      String level,
			                                      final ArchitectureProxyElement arch)
		throws ControllerException {
		List<ProxyElement> args = new ArrayList<ProxyElement>(2);
		
		args.add(new ArgumentProxyElement("level", "integer", level, ""));
		
		CompositeArgumentProxySource source = new CompositeArgumentProxySource() {

			@Override
			public List<Element> getElements() throws ControllerException {
				List<Element> elements = new ArrayList<Element>(1);
				elements.add(arch.getElement());
				return elements;
			}

			@Override
			public String getName() throws ControllerException {
				return platform;
			}

			@Override
			public String getType() throws ControllerException {
				return "architectures";
			}
		};
		args.add(new CompositeArgumentProxyElement(source));
		return run("StoreArchitectures", args);
	}
	
	/**
	 * Store a result. Executes Repository's StoreResult
	 * @param result The result to store
	 * @param level  The current level
	 * @param arch   The architecture producing the response
	 * @return Service response from the repository
	 * @throws ControllerException if any problem saving the result
	 */
	public ServiceResponse storeResult(final FuelConsumptionProxy result,
			                               String level,
			 			                         ArchitectureProxyElement arch)
		throws ControllerException {
		List<ProxyElement> args = new ArrayList<ProxyElement>(3);
		
		CompositeArgumentProxySource source = new CompositeArgumentProxySource() {

			@Override
			public List<Element> getElements() throws ControllerException {
				List<ProxyElement> proxies  = result.getElements();
				List<Element>      elements = new ArrayList<Element>(proxies.size());
				for (ProxyElement proxy : proxies) {
					elements.add(proxy.getElement());
				}
				return elements;
			}

			@Override
			public String getName() throws ControllerException {
				return result.getPlatform();
			}

			@Override
			public String getType() throws ControllerException {
				return "FuelConsumption";
			}
		};
		args.add(new CompositeArgumentProxyElement(source));
		args.add(new ArgumentProxyElement("level",        "int",    level,        ""));
		args.add(new ArgumentProxyElement("architecture", "string", arch.getId(), ""));

		return run("StoreResult", args);
	}

	
	/**
	 * Store marked architectures. Executes Repository's StoreMarkedArchitectures
	 * @param markedArchs  Set of marked architecture id's
	 * @param platform     Current platform
	 * @param level        Current level
	 * @return Service response from the repository
	 * @throws ControllerException if any problem saving the marked arch's
	 */
	public ServiceResponse storeMarkedArch(final Set<String> markedArchs,
			                                   final String platform,
			 			                             String level)
		throws ControllerException {
		List<ProxyElement> args = new ArrayList<ProxyElement>(markedArchs.size()+1);
		
		CompositeArgumentProxySource source = new CompositeArgumentProxySource() {

			@Override
			public List<Element> getElements() throws ControllerException {
				int            n       = markedArchs.size();
				ProxyElement[] proxies = new ProxyElement[n];
				int i = 0;
				for (String ma : markedArchs) {
					proxies[i++] = new ArgumentProxyElement(ma, "boolean",
							                                    Boolean.TRUE.toString(), "");
				}
				List<Element> elements = new ArrayList<Element>(n);
				for (ProxyElement proxy : proxies) {
					elements.add(proxy.getElement());
				}
				return elements;
			}

			@Override
			public String getName() throws ControllerException {
				return platform;
			}

			@Override
			public String getType() throws ControllerException {
				return "marked-architectures";
			}
		};
		args.add(new CompositeArgumentProxyElement(source));
		args.add(new ArgumentProxyElement("level", "int", level, ""));

		return run("StoreMarkedArchitectures", args);
	}

	/**
	 * Get the platform definition. Executes Repository's GetPlatform
	 * @param platform  The desired platform
	 * @param level     The run level
	 * @return          Service response from the repository
	 * @throws ControllerException if any problem getting the platform
	 */
	public ServiceResponse getPlatform(String platform, String level)
		throws ControllerException {
		List<ProxyElement> args = new ArrayList<ProxyElement>(2);
		args.add(new ArgumentProxyElement("platform", "string", platform, ""));
		args.add(new ArgumentProxyElement("level",    "integer",    level,    ""));
		
		return run("GetPlatform", args);
	}

	/**
	 * Get a result. Executes Repository's GetResult
	 * @param platform  The desired platform
	 * @param level     The run level
	 * @param id        The architecture (and result) id
	 * @return          Service response from the repository
	 * @throws ControllerException if any problem getting the platform
	 */
	public ServiceResponse getResult(String platform, String level, String id)
		throws ControllerException {
		List<ProxyElement> args = new ArrayList<ProxyElement>(2);
		args.add(new ArgumentProxyElement("platform", "string", platform, ""));
		args.add(new ArgumentProxyElement("level",    "integer",    level,    ""));
		args.add(new ArgumentProxyElement("id",       "string", id,       ""));
		
		return run("GetResult", args);
	}
	
	/**
	 * Get all of the currently stored architectures in the repository. 
	 * Executes Repository's GetAllArchitectures
	 * @param platform  The desired platform
	 * @param level     The run level
	 * @return          Service response from the repository
	 * @throws ControllerException if any problem getting the architectures
	 */
	public ServiceResponse getAllArchitectures(String platform, String level)
		throws ControllerException {
		List<ProxyElement> args = new ArrayList<ProxyElement>(2);
		args.add(new ArgumentProxyElement("mode",     "string", "all-in-one", ""));
		args.add(new ArgumentProxyElement("platform", "string",  platform,    ""));
		args.add(new ArgumentProxyElement("level",    "integer", level,       ""));
		
		return run("GetArchitectures", args);
	}

	/**
	 * Get the mission. Executes Repository's GetMission
	 * @param mission   The desired mission name
	 * @return          Service response from the repository. This is a 
	 *                  composite argument of type "mission-segment"
	 * @throws ControllerException if any problem getting the mission
	 */
	public ServiceResponse getMission(String mission)
		throws ControllerException {
		List<ProxyElement> args = new ArrayList<ProxyElement>(1);
		args.add(new ArgumentProxyElement("mission", "string", mission, ""));
		
		return run("GetMission", args);
	}

}
