#! /usr/bin/env python
#############################################################
#                                                           #
#   Author: Herve Menager                                   #
#   Organization:'Biological Software and Databases' Group, #
#                Institut Pasteur, Paris.                   #
#   Distributed under GPLv2 Licence. Please refer to the    #
#   COPYING.LIB document.                                   #
#                                                           #
#############################################################
from lxml import etree
from StringIO import StringIO
import urllib
 
import mb_cgi
from Mobyle.Workflow import Workflow, Task, Text, Parameter, Link, Type, Datatype, Biotype, InputValue,\
    Parser, parseTextOrHTML
from Mobyle.Registry import registry
from Mobyle.MobyleError import MobyleError

def process( self ):
    action = self.request.getfirst("action")
    if action in ["create","update"]:
        if action=="create":
            workflow = Workflow()
            self.workflow_id = self.session.addWorkflow(workflow)
        elif action=="update":
            self.workflow_id = int(self.request.getfirst('wf_id'))
            workflow = self.session.readWorkflow(self.workflow_id)
        if self.request.has_key('name'):
            name = self.request.getfirst('name')
            workflow.name = name
        if self.request.has_key('version'):
            version = self.request.getfirst('version')
            workflow.version = version
        if self.request.has_key('title'):
            title = self.request.getfirst('title')
            workflow.title = title
        if self.request.has_key('authors'):
            authors = self.request.getfirst('authors')
            workflow.authors = authors
        if self.request.has_key('description'):
            description = self.request.getfirst('description')
            workflow.description = parseTextOrHTML(description)
        if self.request.has_key('comment'):
            comment = self.request.getfirst('comment')
            workflow.comment = parseTextOrHTML(comment)
        self.session.saveWorkflow(workflow)
        self.xmltext = render_graphml(workflow)
    elif action=="get":
        self.workflow_id = int(self.request.getfirst('wf_id'))
        workflow = self.session.readWorkflow(self.workflow_id)
        self.xmltext = render_graphml(workflow)
    elif action=="list":
        ids = self.session.getWorkflows()
        self.xmltext = ids
    elif action=="get_url":
        self.workflow_id = int(self.request.getfirst('wf_id'))
        url = self.session.getWorkflowUrl(self.workflow_id)
        self.xmltext = url
    elif action=="create_task":
        try:
            self.workflow_id = int(self.request.getfirst('wf_id'))
            workflow = self.session.readWorkflow(self.workflow_id)
            service_pid = self.request.getfirst('service_name')
            service = get_service(service_pid)
            ids = [int(task.id) for task in workflow.tasks]
            if ids:
                self.task_id = max(ids)+1
            else:
                self.task_id = 1
            task = Task()
            task.id = self.task_id
            task.service = service_pid
            task.service_url = service.url
            workflow.tasks = workflow.tasks + [task]
            self.session.saveWorkflow(workflow)
            self.xmltext = render_task_xml(service.path, self.task_id,self.session.getWorkflowUrl(self.workflow_id))  
        except ServiceNotFoundError, snfe:
            self.xmltext = "<pre>%s</pre>" % snfe.message
    elif action=="fetch_task":
        self.workflow_id = int(self.request.getfirst('wf_id'))
        workflow = self.session.readWorkflow(self.workflow_id)
        self.task_id = int(self.request.getfirst('task_id'))
        task = [t for t in workflow.tasks if int(t.id)==self.task_id][0]
        service_pid = task.service
        service_path = get_service(service_pid).path
        self.xmltext = render_task_xml(service_path, self.task_id,self.session.getWorkflowUrl(self.workflow_id))  
    elif action=="save":
        self.workflow_id = int(self.request.getfirst('wf_id'))
        self.graphml = self.request.getfirst('graphml')
        self.mobylexml = transform_xml(StringIO(self.graphml),'graphml_to_wf.xsl',{'workflow_id':"'%s'" % self.workflow_id})
        workflow = Parser().XML(str(self.mobylexml))
        self.session.saveWorkflow(workflow)
        self.xmltext = self.mobylexml
    elif action=="fetch":
        self.workflow_id = int(self.request.getfirst('wf_id'))
        workflow = self.session.readWorkflow(self.workflow_id)
        self.xmltext = render_graphml(workflow)

"""
    elif object=='task':
        if action in ["create","update"]:
            if action=="create":
                self.workflow_id = int(self.request.getfirst('wf_id'))
                workflow = self.session.readWorkflow(self.workflow_id)
                ids = [int(task.id) for task in workflow.tasks]
                if ids:
                    id = max(ids)+1
                else:
                    id = 1
                task = Task()
                task.id = id
                workflow.tasks = workflow.tasks + [task]
            elif action=="update":
                self.workflow_id = int(self.request.getfirst('wf_id'))
                task_id = int(self.request.getfirst('task_id'))
                workflow = self.session.readWorkflow(self.workflow_id)
                task = [t for t in workflow.tasks if int(t.id)==task_id][0]
            if self.request.has_key('service_pid'):
                service_pid = self.request.getfirst('service_pid')
                task.service = service_pid
            if self.request.has_key('suspend'):
                suspend = bool(self.request.getfirst('suspend')=='true')            
                task.suspend = ("false","true")[suspend]
            if self.request.has_key('description'):
                description = self.request.getfirst('description')
                task.description = description
            # setting task parameter default values
            for key in self.request.keys():
                if key.startswith('parameter::'):
                    name = key.split('::')[1]
                    value = self.request.getfirst('parameter::%s' % name,'')
                    if value!='':
                        # set a value for a parameter
                        input_values = [iv for iv in task.input_values if iv.name==name]
                        if len(input_values)>0:
                            input_value=input_values[0]
                        else:
                            input_value=InputValue()
                            input_value.name=name
                            task.input_values = task.input_values + [input_value]
                        input_value.value=value
                    else:
                        # unset a value for a parameter
                        input_values = [iv for iv in task.input_values if iv.name!=name]
                        task.input_values = input_values
            # creating a link from or to the task along the way
            if self.request.has_key('from_parameter') or self.request.has_key('from_parameter'):
                ids = [int(link.id) for link in workflow.links]
                if ids:
                    id = max(ids)+1
                else:
                    id = 1
                link = Link()
                link.id = id
                workflow.links = workflow.links + [link]
                if self.request.has_key('from_parameter'):
                    from_parameter = self.request.getfirst('from_parameter')
                    link.from_parameter = from_parameter
                if self.request.has_key('from_task'):
                    from_task = self.request.getfirst('from_task')
                    if from_task=='self':
                        from_task = task.id
                    link.from_task = from_task
                if self.request.has_key('to_parameter'):
                    to_parameter = self.request.getfirst('to_parameter')
                    link.to_parameter = to_parameter
                if self.request.has_key('to_task'):
                    to_task = self.request.getfirst('to_task')
                    if to_task=='self':
                        to_task = task.id
                    link.to_task = to_task
            self.session.saveWorkflow(workflow) 
            self.xmltext = render_model(task)
        elif action=="get":
            self.workflow_id = int(self.request.getfirst('wf_id'))
            task_id = int(self.request.getfirst('task_id'))
            workflow = self.session.readWorkflow(self.workflow_id)
            task = [t for t in workflow.tasks if int(t.id)==task_id][0]
            self.xmltext = render_model(task)
        elif action=="delete":
            self.workflow_id = int(self.request.getfirst('wf_id'))
            task_id = int(self.request.getfirst('task_id'))
            workflow = self.session.readWorkflow(self.workflow_id)
            tasks = [t for t in workflow.tasks if int(t.id)!=task_id]
            workflow.tasks = tasks
            self.session.saveWorkflow(workflow)
            self.xmltext = render_graphml(workflow)
    elif object=='link':
        if action in ["create","update"]:
            if action=="create":
                self.workflow_id = int(self.request.getfirst('wf_id'))
                workflow = self.session.readWorkflow(self.workflow_id)
                ids = [int(link.id) for link in workflow.links]
                if ids:
                    id = max(ids)+1
                else:
                    id = 1
                link = Link()
                link.id = id
                workflow.links = workflow.links + [link]
            elif action=="update":
                self.workflow_id = int(self.request.getfirst('wf_id'))
                link_id = int(self.request.getfirst('link_id'))
                workflow = self.session.readWorkflow(self.workflow_id)
                link = [l for l in workflow.links if int(l.id)==link_id][0]
            if self.request.has_key('from_parameter'):
                from_parameter = self.request.getfirst('from_parameter')
                link.from_parameter = from_parameter
            if self.request.has_key('from_task'):
                from_task = self.request.getfirst('from_task')
                link.from_task = from_task
            if self.request.has_key('to_parameter'):
                to_parameter = self.request.getfirst('to_parameter')
                link.to_parameter = to_parameter
            if self.request.has_key('to_task'):
                to_task = self.request.getfirst('to_task')
                link.to_task = to_task
            self.session.saveWorkflow(workflow)
            self.xmltext = render_model(link)
        elif action=="get":
            self.workflow_id = int(self.request.getfirst('wf_id'))
            link_id = int(self.request.getfirst('link_id'))
            workflow = self.session.readWorkflow(self.workflow_id)
            link = [t for t in workflow.links if int(t.id)==link_id][0]
            self.xmltext = render_model(t)
        elif action=="delete":
            self.workflow_id = int(self.request.getfirst('wf_id'))
            link_id = int(self.request.getfirst('link_id'))
            workflow = self.session.readWorkflow(self.workflow_id)
            links = [l for l in workflow.links if int(l.id)!=link_id]
            workflow.links = links
            self.session.saveWorkflow(workflow)
            self.xmltext = render_graphml(workflow)
    elif object=='parameter':
        if action in ["create","update"]:
            if action=="create":
                self.workflow_id = int(self.request.getfirst('wf_id'))
                isout = self.request.getfirst('stream')=='output'
                workflow = self.session.readWorkflow(self.workflow_id)
                ids = [int(parameter.id) for parameter in workflow.parameters]
                if ids:
                    id = max(ids)+1
                else:
                    id = 1
                parameter = Parameter()
                parameter.id = id
                parameter.isout = isout
                workflow.parameters = workflow.parameters + [parameter]
            elif action=="update":
                self.workflow_id = int(self.request.getfirst('wf_id'))
                parameter_id = int(self.request.getfirst('parameter_id'))
                workflow = self.session.readWorkflow(self.workflow_id)
                parameter = [p for p in workflow.parameters if int(p.id)==parameter_id][0]
            if self.request.has_key('name'):
                name = self.request.getfirst('name')
                parameter.name = name
            if self.request.has_key('prompt'):
                prompt = self.request.getfirst('prompt')
                parameter.prompt = prompt
            if self.request.has_key('example'):
                example = self.request.getfirst('example')
                parameter.example = example
            if self.request.has_key('datatype_class'):
                datatype_class = self.request.getfirst('datatype_class')
                parameter.type = (Type(),parameter.type) [parameter.type!=None]
                parameter.type.datatype = (Datatype(),parameter.type.datatype) [parameter.type.datatype!=None]
                parameter.type.datatype.class_name = datatype_class
            if self.request.has_key('datatype_superclass'):
                datatype_superclass = self.request.getfirst('datatype_superclass')
                parameter.type = (Type(),parameter.type) [parameter.type!=None]
                parameter.type.datatype = (Datatype(),parameter.type.datatype) [parameter.type.datatype!=None]
                parameter.type.datatype.superclass_name = datatype_superclass
            if self.request.has_key('biotype'):
                biotypes = self.request.getlist('biotype')
                parameter.biotypes = [Biotype(b) for b in biotypes]
            if self.request.has_key('example'):
                example = self.request.getfirst('example')
                parameter.example = example
            self.session.saveWorkflow(workflow)
            self.xmltext = render_model(parameter)
        elif action=="get":
            self.workflow_id = int(self.request.getfirst('wf_id'))
            parameter_id = int(self.request.getfirst('parameter_id'))
            workflow = self.session.readWorkflow(self.workflow_id)
            parameter = [p for p in workflow.parameters if int(p.id)==parameter_id][0]
            self.xmltext = render_model(parameter)
        elif action=="delete":
            self.workflow_id = int(self.request.getfirst('wf_id'))
            parameter_id = int(self.request.getfirst('parameter_id'))
            workflow = self.session.readWorkflow(self.workflow_id)
            parameters = [l for l in workflow.parameters if int(l.id)!=parameter_id]
            workflow.parameters = parameters
            self.session.saveWorkflow(workflow)
            self.xmltext = render_graphml(workflow)
    else:
        self.xmltext = "error"
"""

mp = Parser()        

def render_model(e):
    return mp.tostring(e)

class CustomResolver(etree.Resolver):
    """
    CustomResolver is a Resolver for lxml that allows (among other things) to
    handle HTTPS protocol, which is not handled natively by lxml/libxml2
    """
    def resolve(self, url, id, context):
        return self.resolve_file(urllib.urlopen(url), context)


def transform_xml(xml,xslt,parameters={}):
    parser = etree.XMLParser(no_network = False)
    parser.resolvers.add(CustomResolver())
    xml = etree.parse(xml,parser)
    xslt_doc = etree.parse(xslt,parser)
    transform = etree.XSLT(xslt_doc)
    parser = etree.XMLParser( no_network = False )
    return transform(xml, **parameters)

def render_graphml(e):
    return transform_xml(StringIO(render_model(e)),'wf_to_graphml.xsl')

def render_task_xml(xml_path,task_id,workflow_url):
    return transform_xml(xml_path,'task_xml.xsl',{'task_id':"'%s'" % task_id,'workflow_url':"'%s'" % workflow_url})

class ServiceNotFoundError(MobyleError):
    def __init__(self,pid):
        self.pid = pid

    @property
    def message(self):
        return "Service %s cannot not be found on the server" % self.pid

def get_service(pid):
    def f(x): return x and x != '.'
    pid_dict = filter(f,pid.partition('.'))
    try:
        try:
            if len(pid_dict)==1:
                service = registry.serversByName['local'].programsByName[pid_dict[0]]    
            else:
                service = registry.serversByName[pid_dict[0]].programsByName[pid_dict[1]]
        except KeyError:
            if len(pid_dict)==1:
                service = registry.serversByName['local'].workflowsByName[pid_dict[0]]            
            else:
                service = registry.serversByName[pid_dict[0]].workflowsByName[pid_dict[1]]
    except KeyError:
        raise ServiceNotFoundError(pid)
    return service

if __name__ == "__main__":
    mb_cgi.XMLCGI(processFunction=process,useSession=True)