class CCPExecutionForm extends HTMLElement{
  
  #boot;
  #rootdoc;
  #data;
  #method;
  #executionmonitor;
  
  #serviceurl; 
  
  constructor(){
    super()
    this.#boot = document.querySelector("d4s-boot-2")
    this.#serviceurl = this.getAttribute("serviceurl")
    this.#rootdoc = this.attachShadow({ "mode" : "open"})
    this.render()
    this.showMethod()
  }
  
  static get observedAttributes() { 
    return ["method"];
  }
  
  attributeChangedCallback(name, oldValue, newValue) {
    if((oldValue != newValue) && (name === "method")){
      this.#method = newValue
      this.loadMethod()
    }
  }
  
  render(){
    this.#rootdoc.innerHTML = `
      <div>
        <link rel="stylesheet" href="https://cdn.dev.d4science.org/ccp/css/common.css"></link>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
        <style>
          
        </style>
        <template id="EXECUTION_FORM_TEMPLATE">
          <div class="ccp-execution-form" name="execution_form">
            <h5 class="ccp-method-title"></h5>
            <p class="description font-italic font-weight-lighter"></p>
            <div name="plexiglass" class="ccp-invisible">
              <span name="status"></span>
              <span class="fas fa-spinner fa-spin"></span>
            </div>
            <form name="execution_form" class="d-flex flex-column gap-3" style="gap:5px">
              <div class="card">
                <div class="card-header">
                  <h5>Inputs</h5>
                </div>
                <div class="card-body ccp-inputs">
                  <div class="card-body">
                    <div class="form-group"></div>
                  </div>
                </div>        
              </div>
              <div class="card">
                <div class="card-header">
                  <h5>Outputs</h5>
                </div>
                <div class="card-body">
                  <div class="form-row ccp-outputs">
                    <div class="col form-group"></div>
                  </div>
                </div>
              </div>
              <!--div class="card">
                <div class="card-header">
                  <h5>Runtimes <span alt="refresh" title="refresh" style="cursor:pointer" name="refresh-runtimes" class="text-info">&#8634;</span></h5>
                </div>
                <div class="card-body">
                  <div class="ccp-runtimes">
                    <div class="form-row">
                      <select class="form-control">
                      </select>
                    </div>
                  </div>
                </div>
              </div-->
         <button id="execute_method_button" class="btn btn-info">Execute</button>
            </form>
          </div>    
        </template>
        <template id="EXECUTION_FORM_EMPTY_TEMPLATE">
          <div name="execution_form">
            <i style="padding:3rem">Drop a method here!</i>
          </div>
        </template>
        <div name="execution_form"></div>
      </div>
    `
  }
  
  loadMethod(){
    this.#boot.secureFetch(this.#serviceurl + "/processes/" + this.#method).then(
      (resp)=>{
        if(resp.status === 200){
          return resp.json()
        }else throw "Error retrieving process"
      }
    ).then(data=>{
      this.#data = data
      const rts = 
        this.#data.links
          .filter(l => l.rel === "compatibleWith")
          .map(l=>l.href.replace("runtimes/",""))
          .join(" ")
      return this.#boot.secureFetch(this.#serviceurl + "/infrastructures/runtimes?runtimes=" + rts)
      
    }).then(resp=>{
      this.#data.executable = resp.status === 200 
    }).then(()=>{
      this.showMethod()
    }).catch(err=>alert(err))
  }
  
  showEmpty(resp){
    BSS.apply(this.#empty_executionform_bss, this.#rootdoc)
  }
  
  showMethod(){
    if(this.#method == null) this.showEmpty();
    else{
      console.log(this.#data)
      BSS.apply(this.#executionform_bss, this.#rootdoc)
    }
  }
  
  sendExecutionRequest(){
    const url = this.#serviceurl + "/processes/" + this.#method + "/execution"
    const req = this.buildRequest()
    this.#boot.secureFetch(
      url, { method : "POST", body : JSON.stringify(req), headers : { "Content-Type" : "application/json"}}
    ).then(reply=>{
      if(reply.status !== 200 && reply.status !== 201){
        throw "Error while requesting resource"
      }
      return reply.json()
    }).then(data=>{
      if(data.status !== "accepted"){
        throw "Execution has not been accepted by server"
      }
      this.#executionmonitor = document.querySelector("d4s-ccp-executionmonitor")
      if(this.#executionmonitor){
        this.#executionmonitor.addExecution( { self : data.links[0].href, events : [data], jobID : data.jobID, method : this.#data.title})
      }
    }).catch(err => alert("Unable to call execute: " + err))
  }
  
  buildRequest(){
    let request = { inputs : {}, outputs : {}, response : "raw"}
    
    //fill inputs
    const inputs = this.getInputs()
    inputs.forEach(i=>{
      request.inputs[i.name] = i.value
    })
    
    //fill outputs
    const outputs = this.getOutputs()
    outputs.forEach(o=>{
      if(o.enabled) request.outputs[o.name] = { transmissionMode : "value" };
    })
   
    return request
  }
  
  getInputs(){
    return Array.prototype.slice.call(this.#rootdoc.querySelectorAll("d4s-ccp-input"))
  }
  
  getOutputs(){
    return Array.prototype.slice.call(this.#rootdoc.querySelectorAll("d4s-ccp-output"))
  }
  
  #empty_executionform_bss = {
    template : "#EXECUTION_FORM_EMPTY_TEMPLATE",
    target : "div[name=execution_form]",
    on_drop : ev=>{
      if(ev.dataTransfer && ev.dataTransfer.getData('text/plain+ccpmethod')){
        const id = ev.dataTransfer.getData('text/plain+ccpmethod')
        this.setAttribute("method", id);
        ev.preventDefault()
        ev.stopPropagation()
        ev.currentTarget.style.backgroundColor = "white"
      }
    },
    on_dragover : ev=>{
      ev.preventDefault()
    },
  }
  
  #executionform_bss = {
    template : "#EXECUTION_FORM_TEMPLATE",
    target : "div[name=execution_form]",
    in : ()=>this.#data,
    on_drop : ev=>{
      if(ev.dataTransfer && ev.dataTransfer.getData('text/plain+ccpmethod')){
        const id = ev.dataTransfer.getData('text/plain+ccpmethod');
        this.setAttribute("method", id);
        ev.preventDefault()
        ev.stopPropagation()
      }
    },
    on_dragover : ev=>{
      ev.preventDefault()
    },
    recurse : [
      {
        target: ".ccp-method-title",
        apply : (e,d)=>e.textContent = d.title + " (v. " + d.version + ")"
      },
      {
        target: "p.description",
        apply : (e,d)=>e.textContent = d.description
      },
      {
        target: "div.ccp-inputs",
        in : (e,d)=>d,
        recurse : [
          {
            "in" : (e,d)=>{ return Object.values(d.inputs) },
            target : "div",
            apply : (e,d)=>{
              e.innerHTML = `<d4s-ccp-input input='${JSON.stringify(d)}'></d4s-ccp-input>`
            }
          }
        ]
      },
      {
        target: "div.ccp-outputs",
        in : (e,d)=>d,
        recurse : [
          {
            "in" : (e,d)=>{ return Object.values(d.outputs) },
            target : "div",
            apply : (e,d)=>{
              e.innerHTML = `<d4s-ccp-output output='${JSON.stringify(d)}'></d4s-ccp-output>`
            }
          }
        ]
      },
      {
        target: "div.ccp-runtimes *[name=refresh-runtimes]",
        on_click : ev=>{
          BSS.apply(this.#executionform_bss)
        }
      },
      {
        target: "#execute_method_button",
        on_click : ev=>{
          ev.preventDefault()
          ev.stopPropagation()
          if(this.#data.executable) this.sendExecutionRequest();
          else alert("This method has no compatible runtimes available")
          return false;
        }
      },
    ]
  }
  
}

window.customElements.define('d4s-ccp-executionform', CCPExecutionForm);

class CCPExecutionEnvironmentOption extends HTMLOptionElement{
    
    #infrastructure;
    #runtime;
    
    constructor(runtime, infrastructure){
      super(`${runtime.name} [${infrastructure.name}]`, runtime["descriptor-id"])
      this.#runtime = runtime
      this.#infrastructure = infrastructure
      this.textContent = `${runtime.name} [${infrastructure.name}]`
      this.value = this.runtimeId
    }
    
    get infrastructureName(){
      return this.#infrastructure.name
    }
    
    get infrastructureId(){
      return this.#infrastructure.id
    }
    
    get runtimeName(){
      return this.#runtime.name
    }
    
    get runtimeId(){
      return this.#runtime["descriptor-id"]
    }
}

window.customElements.define('d4s-ccp-environment-option', CCPExecutionEnvironmentOption, {extends:'option'});
