content: /**
   * ﻿Copyright (C) 2009 - 2019 52°North Initiative for Geospatial Open Source
   * Software GmbH
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 as published
   * by the Free Software Foundation.
   *
   * If the program is linked with libraries which are licensed under one of
   * the following licenses, the combination of the program with the linked
   * library is not considered a "derivative work" of the program:
   *
   *       • Apache License, version 2.0
   *       • Apache Software License, version 1.0
   *       • GNU Lesser General Public License, version 3
   *       • Mozilla Public License, versions 1.0, 1.1 and 2.0
   *       • Common Development and Distribution License (CDDL), version 1.0
   *
   * Therefore the distribution of the program linked with libraries licensed
   * under the aforementioned licenses, is permitted by the copyright holders
   * if the distribution is compliant with both the GNU General Public
   * License version 2 and the aforementioned licenses.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
   * Public License for more details.
   */
  package org.n52.wps.ags;
  
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.math.BigInteger;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.UUID;
  
  import net.opengis.wps.x100.InputDescriptionType;
  import net.opengis.wps.x100.OutputDescriptionType;
  import net.opengis.wps.x100.ProcessDescriptionType;
  import net.opengis.wps.x100.ProcessDescriptionsDocument;
  
  import org.apache.xmlbeans.XmlException;
  import org.apache.xmlbeans.XmlOptions;
  import org.n52.wps.ags.workspace.AGSWorkspace;
  import org.n52.wps.server.IAlgorithm;
  import org.n52.wps.server.IAlgorithmRepository;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.w3c.dom.Comment;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  
  /**
   * @author Matthias Mueller, TU Dresden; Christin Henzen, TU Dresden
   *
   */
  public class AGSProcessRepository implements IAlgorithmRepository {
  	
  	private static Logger LOGGER = LoggerFactory.getLogger(AGSProcessRepository.class);
  	
  	private Map<String, ProcessDescriptionType> registeredProcessDescriptions;
  	private Map<String, ToolParameter[]> registeredAlgorithmParameters;
  	
  	
  	public AGSProcessRepository() {
  		LOGGER.info("Initializing ArcGIS Server Repository ...");
  		
  		//initialize local variables
  		this.registeredProcessDescriptions = new HashMap<String, ProcessDescriptionType>();
  		this.registeredAlgorithmParameters = new HashMap<String, ToolParameter[]>();
  		
  		String describeProcessPathString = AGSProperties.getInstance().getProcessDescriptionDir();
  		LOGGER.info("Loading AGS process descriptions from: " + describeProcessPathString);
  		
  		File describeProcessPath = new File(describeProcessPathString);
  		ArrayList<File> describeProcessFiles = listAllFiles(describeProcessPath,false);
  		
  		for (File currentFile : describeProcessFiles){
  			addAlgorithm(currentFile);
  		}
  		
  	}
  	
  	
  	/**
  	 * Checks if a given processID exists 
  	 * @param processID
  	 * @return
  	*/
  	public boolean containsAlgorithm(String processID) {
  		return registeredProcessDescriptions.containsKey(processID);
  	}
  	
  	
  	/**
  	 * Returns an IAlgorithm through GenericAGSProcessDelegator
  	 * @param processID
  	 * @return
  	*/
  	public IAlgorithm getAlgorithm(String processID) {
  		if (!containsAlgorithm(processID)){
  			//TODO returning null should be better than throwing an unexpectable exception. Test if it breaks other parts
  			return null;
  		}
  		// create a unique directory for each instance
  		String randomDirName = AGSProperties.getInstance().getWorkspaceBase() + File.separator + UUID.randomUUID();
  		return new GenericAGSProcessDelegator(new File (randomDirName), processID, registeredAlgorithmParameters.get(processID), registeredProcessDescriptions.get(processID));
  	}
  	
  	
  	private void addAlgorithm(File processDescriptionFile){
  		
  		ProcessDescriptionType pd = this.loadProcessDescription(processDescriptionFile);
  		String processID = pd.getIdentifier().getStringValue();
  		LOGGER.debug("Registering: " + processID);
  		this.registeredProcessDescriptions.put(processID, pd);
  		ToolParameter[] params = this.loadParameters(pd);
  		this.registeredAlgorithmParameters.put(processID, params);
  		
  		if (!pd.validate()){
  			LOGGER.debug("ProcessDescription is not valid. Removing "  + processID + " from Repository.");
  			this.registeredProcessDescriptions.remove(processID);
  			this.registeredAlgorithmParameters.remove(processID);
  		}
  	}
  	
  	public Collection<String> getAlgorithmNames() {
  		return registeredProcessDescriptions.keySet();
  	}
  	
  	private ToolParameter[] loadParameters(ProcessDescriptionType pd){
  //		if(pd.getIdentifier().getStringValue().contains("buffer")){
  //			System.out.println("Buffer"); 
  //		}
  		InputDescriptionType[] inputArray = pd.getDataInputs().getInputArray();
  		OutputDescriptionType[] outputArray = pd.getProcessOutputs().getOutputArray();
  		
  		int paramCount = inputArray.length + outputArray.length;
  		
  		
  		//create parameter array
  		//LegacyParameter[] paramArray = new LegacyParameter[inputArray.length + outputArray.length];
  		ToolParameter[] paramArray = new ToolParameter[paramCount];
  		
  		
  		for (InputDescriptionType currentDesc : inputArray){
  			
  			NodeList nl = currentDesc.getDomNode().getChildNodes();
  			String pameterID = null;
  			
  			for (int i = 0; i < nl.getLength(); i++){
  				Node currentNode = nl.item(i);
  				if(currentNode instanceof Comment){
  					pameterID = ((Comment)currentNode).getNodeValue().replaceAll("\\s+", "");
  				}
  			}
  			
  			int index = Integer.parseInt(pameterID);
  			
  			String mimeType = null;
  			String schema = null;
  			String literalDataType = null;
  			String defaultCRS = null;
  			
  			String prefixString = null;
  			String suffixString = null;
  			String separatorString = " ";
  			String wpsInputID = currentDesc.getIdentifier().getStringValue();
  			String wpsOutputID = null;
  			
  			//complex data
  			if (currentDesc.isSetComplexData()){
  				mimeType = currentDesc.getComplexData().getDefault().getFormat().getMimeType();
  				schema = currentDesc.getComplexData().getDefault().getFormat().getSchema();
  			}
  			
  			//literal data
  			if (currentDesc.isSetLiteralData()){
  				if (currentDesc.getLiteralData().isSetDataType()){
  					literalDataType = currentDesc.getLiteralData().getDataType().getStringValue();
  				}
  				if (currentDesc.getLiteralData().isSetValuesReference()){
  					literalDataType = currentDesc.getLiteralData().getDataType().getReference();
  				}
  			}
  			
  			//BBox data
  			if (currentDesc.isSetBoundingBoxData()){
  				defaultCRS = currentDesc.getBoundingBoxData().toString();
  			}
  			
  			boolean isOptional = false;
  			if(currentDesc.getMinOccurs().equals(new BigInteger("0"))){
  				isOptional = true;
  			}
  			
  			paramArray[index] = new ToolParameter(wpsInputID, wpsOutputID, pameterID, schema, mimeType, literalDataType, defaultCRS, prefixString, suffixString, separatorString, isOptional);
  		}
  		
  		for (OutputDescriptionType currentDesc : outputArray){
  			
  			NodeList nl = currentDesc.getDomNode().getChildNodes();
  			String pameterID = null;
  			
  			for (int i = 0; i < nl.getLength(); i++){
  				Node currentNode = nl.item(i);
  				if(currentNode instanceof Comment){
  					pameterID = ((Comment)currentNode).getNodeValue().replaceAll("\\s+", "");
  				}
  			}
  			
  			int index = Integer.parseInt(pameterID);
  			
  			String mimeType = null;
  			String schema = null;
  			String literalDataType = null;
  			String defaultCRS = null;
  			
  			String prefixString = null;
  			String suffixString = null;
  			String separatorString = null;
  			String wpsInputID = null;
  			
  			// if parameter is already present
  			if(paramArray[index] != null){
  				wpsInputID = paramArray[index].wpsInputID;
  			}
  			
  			String wpsOutputID = currentDesc.getIdentifier().getStringValue();
  			
  			//complex data
  			if (currentDesc.isSetComplexOutput()){
  				mimeType = currentDesc.getComplexOutput().getDefault().getFormat().getMimeType();
  				schema = currentDesc.getComplexOutput().getDefault().getFormat().getSchema();
  			}
  			
  			//literal data
  			if (currentDesc.isSetLiteralOutput()){
  				if (currentDesc.getLiteralOutput().isSetDataType()){
  					literalDataType = currentDesc.getLiteralOutput().getDataType().getStringValue();
  				}
  			}
  			
  			//BBox data
  			if (currentDesc.isSetBoundingBoxOutput()){
  				defaultCRS = currentDesc.getBoundingBoxOutput().toString();
  			}
  			boolean isOptional = false;
  			
  			paramArray[index] = new ToolParameter(wpsInputID, wpsOutputID, pameterID, schema, mimeType, literalDataType, defaultCRS, prefixString, suffixString, separatorString,isOptional);
  		}
  		
  		
  		return paramArray;
  	}
  	
  	private ArrayList<File> listAllFiles(File rootDir, boolean includeDirNames) {
  		ArrayList<File> result = new ArrayList<File>();
  		try {
  			File[] fileList = rootDir.listFiles();
  			for (int i = 0; i < fileList.length; i++) {
  				if (fileList[i].isDirectory() == true) {
  					if (includeDirNames)
  						result.add(fileList[i]);
  					result.addAll(listAllFiles(fileList[i], includeDirNames));
  				} else{
  					result.add(fileList[i]);
  					LOGGER.info("Found AGS PD: " + fileList[i].getPath());
  				}
  			}
  		} catch (Exception e) {
  			e.printStackTrace();
  		}
  		return result;
  	}
  	
  
  	private ProcessDescriptionType loadProcessDescription(File describeProcessFile){
  		
  		try {
  			InputStream xmlDesc = new FileInputStream(describeProcessFile);
  			XmlOptions option = new XmlOptions();
  			option.setLoadTrimTextBuffer();
  			ProcessDescriptionsDocument doc = ProcessDescriptionsDocument.Factory.parse(xmlDesc, option);
  			if(doc.getProcessDescriptions().getProcessDescriptionArray().length == 0) {
  				LOGGER.warn("ProcessDescription is empty! " + describeProcessFile.getName());
  				return null;
  			}
  			
  			return doc.getProcessDescriptions().getProcessDescriptionArray(0);
  		}
  		catch(IOException e) {
  			LOGGER.warn("Could not initialize algorithm, parsing error! " + describeProcessFile.getName(), e);
  		}
  		catch(XmlException e) {
  			LOGGER.warn("Could not initialize algorithm, parsing error! " + describeProcessFile.getName(), e);
  		}
  		return null;
  	}
  	
  	@Override
  	public ProcessDescriptionType getProcessDescription(String processID) {
  		if(!registeredProcessDescriptions.containsKey(processID)){
  			registeredProcessDescriptions.put(processID, getAlgorithm(processID).getDescription());
  		}
  		return registeredProcessDescriptions.get(processID);
  	}
  
  
  	@Override
  	public void shutdown() {
  		AGSWorkspace.shutdown();
  	}
  	
  	
  }
  