############################################
#
# Author: Luca Cinquini
#
############################################

"""

Abstract
--------
The wps module of the OWSlib package provides client-side functionality for executing invocations to a remote Web Processing Server.


Disclaimer
----------
PLEASE NOTE: the owslib wps module should be considered in beta state: it has been tested versus only a handful of WPS services (deployed by the USGS, BADC and PML).
More extensive testing is needed and feedback is appreciated.


Usage
-----

The module can be used to execute three types of requests versus a remote WPS endpoint: 

a) "GetCapabilities" 
    - use the method wps.getcapabilities(xml=None)
    - the optional keyword argument "xml" may be used to avoid a real live request, and instead read the WPS capabilities document from a cached XML file
    
b) "DescribeProcess"
    - use the method wps.describeprocess(identifier, xml=None)
    - identifier is the process identifier, retrieved from the list obtained from a previous "GetCapabilities" invocation
    - the optional keyword argument "xml" may be used to avoid a real live request, and instead read the WPS process description document from a cached XML file
    
c) "Execute"
    - use the method wps.execute(identifier, inputs, output=None, request=None, response=None), 
      which submits the job to the remote WPS server and returns a WPSExecution object that can be used to periodically check the job status until completion 
      (or error)
    
    - the optional keyword argument "request" may be used to avoid re-building the request XML from input arguments, and instead submit a request from a 
      pre-made XML file
    
    - alternatively, an "Execute" request can be built from input arguments by supplying the "identifier", "inputs" and "output" arguments to the execute() method.
        - "identifier" is the mandatory process identifier
        - "inputs" is a dictionary of (key,value) pairs where:
            - key is a named input parameter
            - value is either a string, or any python object that supports a getXml() method
              In particular, a few classes are included in the package to support a FeatuteCollection input:
                  - "WFSFeatureCollection" can be used in conjunction with "WFSQuery" to define a FEATURE_COLLECTION retrieved from a live WFS server.
                  - "GMLMultiPolygonFeatureCollection" can be used to define one or more polygons of (latitude, longitude) points.
          - "output" is an optional output identifier to be included in the ResponseForm section of the request.
          
    - the optional keyword argument "response" mey be used to avoid submitting a real live request, and instead reading the WPS execution response document
      from a cached XML file (for debugging or testing purposes)
    - the convenience module function monitorExecution() can be used to periodically check the status of a remote running job, and eventually download the output
      either to a named file, or to a file specified by the server.
      
      
Examples
--------

The files examples/wps-usgs-script.py, examples/wps-pml-script-1.py and examples/wps-pml-script-2.py contain real-world usage examples 
that submits a "GetCapabilities", "DescribeProcess" and "Execute" requests to the live USGS and PML servers. To run: 
    cd examples
    python wps-usgs-script.py
    python wps-pml-script-1.py
    python wps-pml-script-2.py
    
The file wps-client.py contains a command-line client that can be used to submit a "GetCapabilities", "DescribeProcess" or "Execute"
request to an arbitratry WPS server. For example, you can run it as follows:
    cd examples
    To prints out usage and example invocations: wps-client -help
    To execute a (fake) WPS invocation: 
        wps-client.py -v -u http://cida.usgs.gov/climate/gdp/process/WebProcessingService -r GetCapabilities -x ../tests/USGSCapabilities.xml
    
The directory tests/ includes several doctest-style files wps_*.txt that show how to interactively submit a 
"GetCapabilities", "DescribeProcess" or "Execute" request, without making a live request but rather parsing the response of cached XML response documents. To run:
    cd tests
    python -m doctest wps_*.txt
    (or python -m doctest -v wps_*.txt for verbose output)

Also, the directory tests/ contains several examples of well-formed "Execute" requests:
    - The files wps_USGSExecuteRequest*.xml contain requests that can be submitted to the live USGS WPS service.
    - The files PMLExecuteRequest*.xml contain requests that can be submitted to the live PML WPS service.

"""

from __future__ import (absolute_import, division, print_function)

from owscapable.etree import etree
from owscapable.ows import DEFAULT_OWS_NAMESPACE, ServiceIdentification, ServiceProvider, OperationsMetadata
from time import sleep
from owscapable.util import (testXMLValue, build_get_url, dump, getTypedValue, 
                  getNamespace, element_to_string, nspath, openURL, nspath_eval, log)
from xml.dom.minidom import parseString
from owscapable.namespaces import Namespaces

# namespace definition
n = Namespaces()

# These static namespaces are DEPRECIATED.  Please don't use them.
# No great way of printing a message since there are at the file level
WPS_DEFAULT_NAMESPACE = n.get_namespace("wps")
WFS_NAMESPACE = n.get_namespace("wfs")
OGC_NAMESPACE = n.get_namespace("ogc")
GML_NAMESPACE = n.get_namespace("gml")
DRAW_NAMESPACE = n.get_namespace("draw")

GML_SCHEMA_LOCATION = "http://schemas.opengis.net/gml/3.1.1/base/feature.xsd"
DRAW_SCHEMA_LOCATION = 'http://cida.usgs.gov/climate/derivative/xsd/draw.xsd'
WPS_DEFAULT_SCHEMA_LOCATION = 'http://schemas.opengis.net/wps/1.0.0/wpsExecute_request.xsd'
WPS_DEFAULT_VERSION = '1.0.0'

def get_namespaces():
    ns = n.get_namespaces(["ogc","wfs","wps","gml","xsi","xlink"])
    ns[None]  = n.get_namespace("wps")
    ns["ows"] = DEFAULT_OWS_NAMESPACE
    return ns
namespaces = get_namespaces()

class IWebProcessingService():
    """
    Abstract interface for an OGC Web Processing Service (WPS).
    """
    
    url = property("""URL for the remote WPS server (string).""")
    
    def getcapabilities(**kw):
        """
        Makes a GetCapabilities request to the remote WPS server,
        returns an XML document wrapped in a python file-like object.
        """
    
    def describeprocess(**kw):
        """
        Makes a DescribeProcess request to the remote WPS server,
        returns a Process object containing all the process metadata.
        """
        
    def execute(**kw):
        """
        Submits an Execute request to the remote WPS server,
        returns a WPSExecution object, which can be used to monitor the status of the job, and ultimately retrieve the result.
        """

class IComplexData():
    """
    Abstract interface representing complex input object for a WPS request.
    """
    
    def getXml(self):
        """
        Method that returns the object data as an XML snippet, 
        to be inserted into the WPS request document sent to the server.
        """ 
    
class WebProcessingService(object):
    """
    Class that contains client-side functionality for invoking an OGC Web Processing Service (WPS).
    
    Implements IWebProcessingService.
    """
    
    def __init__(self, url, version=WPS_DEFAULT_VERSION, username=None, password=None, verbose=False, skip_caps=False):
        """
        Initialization method resets the object status.
        By default it will execute a GetCapabilities invocation to the remote service, 
        which can be skipped by using skip_caps=True.
        """
        
        # fields passed in from object initializer
        self.url = url
        self.username = username
        self.password = password
        self.version = version
        self.verbose = verbose
                
        # fields populated by method invocations
        self._capabilities = None
        self.identification = None
        self.provider = None
        self.operations=[]
        self.processes=[]

        if not skip_caps:
            self.getcapabilities()
        
    def getcapabilities(self, xml=None):
        """
        Method that requests a capabilities document from the remote WPS server and populates this object's metadata.
        keyword argument xml: local XML GetCapabilities document, prevents actual HTTP invocation.
        """
        
        # read capabilities document
        reader = WPSCapabilitiesReader(version=self.version, verbose=self.verbose)
        if xml:
            # read from stored XML file
            self._capabilities = reader.readFromString(xml)