class CCPMethodEditorController extends HTMLElement{

  #boot;
  #rootdoc;

  #serviceurl;
  #infrastructures;
  #locked = false;
  
  #isupdate = false;
  #cloneornew_dialog = null;
  #dragging_method = null;
  
  #tmp_inputs = []
  #tmp_outputs = []
  #current = null;
  #method_template = {
    id : "",
    title : "New Method",
    description : "New empty method",
    version : "1.0.0",
    jobControlOptions : "async-execute",
    metadata :  [],
    inputs : {},
    outputs : {},
    additionalParameters : {
      parameters : [
        {
          name : "deploy-script",
          value : []
        },
        {
          name : "execute-script",
          value : []
        },
        {
          name : "undeploy-script",
          value : []
        }
      ]
    },
    links : []
  }
  
  #scripts = `
    <link rel="canonical" href="https://getbootstrap.com/docs/5.0/components/modal/">
  `
  
  #style = ` 
    <link rel="stylesheet" href="https://cdn.dev.d4science.org/ccp/css/common.css"></link>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">

    <style>
      .ccp-method-editor {
        position: relative;
      }
      
      .ccp-option {
        background-color:#eeffff;
        color:#0099CC; 
        border:solid 1px #0099CC;
        display: inline-flex;
        flex-direction: row;
        justify-content: space-between;
        align-items: center;
        gap: 1rem;
        font-size: 1rem;
      }
      
      .ccp-option > .btn {
        padding: 0;
      }
      
      ul.author_list, ul.context_list{
        list-style: none;
        display: flex;
        flex-direction: row;
        gap:2px;
        font-size: small;
        font-weight: 300;
      }
      li.author_list_item{
        display: inline-block;
        padding: 0.25em 0.4em;
        line-height: 1;
        text-align: center;
        white-space: nowrap;
        vertical-align: baseline;
        border-radius: 0.25rem;
        background-color: #eeffff;
        color: #9900CC;
        border: solid 1px #9900CC;
      }
      li.context_list_item{
        display: inline-block;
        padding: 0.25em 0.4em;
        line-height: 1;
        text-align: center;
        white-space: nowrap;
        vertical-align: baseline;
        border-radius: 0.25rem;
        background-color: #eeffff;
        color: #0099CC;
        border: solid 1px #0099CC;
      }
      li.category_list_item{
        display: inline-block;
        padding: 0.25em 0.4em;
        line-height: 1;
        text-align: center;
        white-space: nowrap;
        vertical-align: baseline;
        border-radius: 0.25rem;
        background-color: #eeffff;
        color: #0099CC;
        border: solid 1px #0099CC;
      }
    </style>
  `
  #erase_icon = `
    <svg viewBox="0 0 24 24">
          <path d="M16.24,3.56L21.19,8.5C21.97,9.29 21.97,10.55 21.19,11.34L12,20.53C10.44,22.09 7.91,22.09 6.34,20.53L2.81,17C2.03,16.21 2.03,14.95 2.81,14.16L13.41,3.56C14.2,2.78 15.46,2.78 16.24,3.56M4.22,15.58L7.76,19.11C8.54,19.9 9.8,19.9 10.59,19.11L14.12,15.58L9.17,10.63L4.22,15.58Z" />
  `
  
  #plus_icon = `
    <svg viewBox="0 0 24 24">
      <path d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />
    </svg> 
  `
  #disc_icon = `
    <svg viewBox="0 0 24 24">
      <path d="M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z" />
    </svg> 
  `
  #output_icon = `
    <svg viewBox="0 0 24 24">
      <path d="M23,12L19,8V11H10V13H19V16M1,18V6C1,4.89 1.9,4 3,4H15A2,2 0 0,1 17,6V9H15V6H3V18H15V15H17V18A2,2 0 0,1 15,20H3A2,2 0 0,1 1,18Z" />
    </svg> 
  `
  #delete_icon = `
    <svg viewBox="0 0 24 24">
      <path d="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z" />
    </svg>
  `
  
  #annotation_input_icon = `
    <svg viewBox="0 -960 960 960">
      <path d="M450-234h60v-129h130v-60H510v-130h-60v130H320v60h130v129ZM220-80q-24 0-42-18t-18-42v-680q0-24 18-42t42-18h361l219 219v521q0 24-18 42t-42 18H220Zm331-554v-186H220v680h520v-494H551ZM220-820v186-186 680-680Z"/>
    </svg>
  `
   
  constructor(){
    super();
    this.#boot = document.querySelector("d4s-boot-2")
    this.#rootdoc = this.attachShadow({ "mode" : "open"})
    this.#serviceurl = this.getAttribute("serviceurl")
    this.initMethod()
    this.fetchInfrastructures()
    this.connectNewEditRequest()
  }

  connectedCallback(){

  }
  
  connectNewEditRequest(){
    document.addEventListener("neweditrequest", ev=>{ 
      this.cloneOrEditMethod(ev.detail)
    })
  }

  isInfrastructureRegistered(infra){
    return (this.#infrastructures.filter(i=>i.id === infra.id)).length > 0
  }

  fetchInfrastructures(){
    this.#boot.secureFetch(this.#serviceurl + "/infrastructures").
      then(resp=>{
        if(resp.status !== 200) throw "Unable to fetch infrastructures";
        return resp.json()
      }).then(data=>{
        this.#infrastructures = data
        this.render()
      }).catch(err=>{
        alert(err)
      })
  }

  saveMethod(){
    if(this.#locked) return;
    if(this.#current != null){
      this.adoptTemporaries()
      const text = `Please confirm ${this.#isupdate ? "updating" : "creation"} of ${this.#current.title} version ${this.#current.version}`
      if(window.confirm(text)){
        this.lockRender()
        const url = this.#serviceurl + "/methods"
        const args = { 
          body : JSON.stringify(this.#current), 
          method : this.#isupdate ? "PUT" : "POST", 
          headers : {"Content-type" : "application/json"}
        }
        this.#boot.secureFetch(url, args).then(
          (resp)=>{
            if(resp.ok){
              return resp.text()
            }else throw "Error saving process: " + resp.status
        }).then(data=>{
          if(!this.#isupdate){ 
            this.#current = JSON.parse(data)
            this.#isupdate = true
            this.#tmp_inputs = Object.keys(this.#current.inputs).map(k=>this.#current.inputs[k])
            this.#tmp_outputs = Object.keys(this.#current.outputs).map(k=>this.#current.outputs[k])
          }
          this.unlockRender()
        }).catch(err=>{
          alert(err)
          this.unlockRender()
        })
      }
    }
  }
  
  deleteMethod(){
    if(this.#locked) return;
    if(this.#current != null){
      const text = `Please confirm deletion of ${this.#current.title} version ${this.#current.version}`
      if(window.confirm(text)){
        this.lockRender()
        const url = this.#serviceurl + "/methods/" + this.#current.id
        const args = { 
          method : "DELETE"
        }
        this.#boot.secureFetch(url, args).then(
          (resp)=>{
            if(resp.status === 404 || resp.status === 204){
              return null
            }else throw "Error deleting method: " + resp.status
        }).then(data=>{
          this.#isupdate = false
          this.initMethod()
          this.unlockRender()
        }).catch(err=>{
          alert(err)
          this.unlockRender()
        })
      }
    }
  }
  
  initMethod(){
    this.#current = JSON.parse(JSON.stringify(this.#method_template))
    this.#current.id = Math.abs((Math.random() * 10e11)|0)
    this.#current.metadata = []
    this.#tmp_inputs = [
      {
        id : "ccpimage",
        title : "Runtime",
        description : "The image of the runtime to use for method execution. This depends on the infrastructure specific protocol for interacting with registries.",
        minOccurs : 1,
        maxOccurs : 1,
        schema : {
          type : "string",
          format : "url",
          contentMediaType : "text/plain",
          default : "",
          readonly : true,
        }
      }
    ]
    this.#tmp_outputs = []
  }
  
  resetMethod(){
    this.initMethod()
    this.render()
  }
  
  cloneOrEditMethod(method){
    const subject = this.#boot.subject
    const matchingauthors = method.metadata ? method.metadata.filter(md=>md.role === "author" && md.href.endsWith("/" + subject)).length : 0
    if(matchingauthors === 0) this.cloneMethod(method.id);
    else{
      this.#dragging_method = method.id
      this.#cloneornew_dialog.style.display = "block"
      this.#cloneornew_dialog.classList.add("show")
    }
  }
  
  cloneMethod(method){
    if(this.#locked) return;
    this.lockRender()
    this.#boot.secureFetch(this.#serviceurl + "/methods/" + method + "/clone").then(
      (resp)=>{
        if(resp.status === 200){
          return resp.json()
        }else throw "Error retrieving process: " + resp.status
      }
    ).then(data=>{
      this.#current = data
      this.#isupdate = false
      this.#tmp_inputs = Object.keys(this.#current.inputs).map(k=>this.#current.inputs[k])
      this.#tmp_outputs = Object.keys(this.#current.outputs).map(k=>this.#current.outputs[k])
      this.unlockRender()
    }).catch(err=>{
      this.unlockRender()
    })
  }
  
  editMethod(method){
    if(this.#locked) return;
    this.lockRender()
    this.#boot.secureFetch(this.#serviceurl + "/methods/" + method + "/updatable").then(
      (resp)=>{
        if(resp.status === 200){
          return resp.json()
        }else throw "Error retrieving process: " + resp.status
      }
    ).then(data=>{
      this.#current = data
      this.#isupdate = true
      this.#tmp_inputs = Object.keys(this.#current.inputs).map(k=>this.#current.inputs[k])
      this.#tmp_outputs = Object.keys(this.#current.outputs).map(k=>this.#current.outputs[k])
      this.unlockRender()
    }).catch(err=>{
      this.unlockRender()
    })
  }
  
  adoptTemporaries(){
    this.#current.inputs = {}
    this.#tmp_inputs.forEach(t=>{
      this.#current.inputs[t.id] = t
    })
    this.#current.outputs = {}
    this.#tmp_outputs.forEach(t=>{
      this.#current.outputs[t.id] = t
    })
  }

  getInputAt(i){
    return this.#tmp_inputs[i]
  }
  
  deleteTmpInputAt(i){
    this.#tmp_inputs.splice(i,1)
  }
  
  deleteTmpOutputAt(i){
    this.#tmp_outputs.splice(i,1)
  }
 
  lockRender(){
    this.#locked = true
    this.#rootdoc.querySelector(".plexiglass").classList.toggle("d-none")
  }
  
  unlockRender(){
    this.#rootdoc.querySelector(".plexiglass").classList.toggle("d-none")
    this.render()
    this.#locked = false
    if(this.parentElement) this.parentElement.scrollIntoViewIfNeeded();
  }
 
  render(){
    this.#rootdoc.innerHTML = `
      <!-- Modal -->
      <div class="modal fade" style="background-color:rgba(0,0,0,0.3)" id="cloneornew" tabindex="-1" role="dialog">
        <div class="modal-dialog" role="document">
          <div class="modal-content shadow-lg border-primary">
            <div class="modal-body">
              Choose whether you want to clone this method or edit it.
            </div>
            <div class="modal-footer">
              <button name="clone" type="button" class="btn btn-info">Clone</button>
              <button name="edit" type="button" class="btn btn-primary">Edit</button>
            </div>
          </div>
        </div>
      </div>
      <div class="ccp-method-editor">
        <div class="d-none plexiglass">
          <svg class="spinner" viewBox="0 0 66 66">
             <circle class="path" fill="none" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle>
          </svg>
        </div>
        ${this.#style}
        <div class="ccp-method-editor-form">
          <div class="card">
            <div class="card-header">
              <div class="ccp-toolbar-header">
                <div>
                  <span name="header" class="mr-2">${this.#current.title}</span>        
                </div>
                <div class="ccp-toolbar-right">
                  ${this.renderSaveButton()}
                  ${this.renderResetButton()}
                  ${ this.#isupdate ?  this.renderDeleteButton() : "" }
                </div>
              </div>
            </div>
            <div class="card-body">
              <div class="mb-3 row">
                ${this.renderAuthors()}
              </div>
              <!-- div class="mb-3 row">
                ${this.renderContexts()}
              </div-->
              <div class="mb-3 row">
                <div class="col">
                  <label class="form-label">Title</label>
                  <input name="title" class="form-control" type="text" required="required" value="${this.#current.title}"/>
                </div>
                <div class="col">
                  <label class="form-label">Version</label>
                  <input name="version" class="form-control" type="text" required="required" value="${this.#current.version}"/>
                </div>
              </div>
              <div class="mb-3">
                <label class="form-label">Description</label>
                <input name="description" class="form-control" type="text" required="required" value="${this.#current.description}"/>
              </div>
              <div class="row">
                <div class="col mb-3">
                  <label class="form-label">Keywords</label>
                  <input name="keyword-input" class="form-control" type="text"/>
                  <div name="keyword-list" class="form-text">
                    ${this.renderKeywords()}
                  </div>
                </div>
                <div class="col mb-3">
                  <label class="form-label">Categories</label>
                  <input list="categoryhints" name="category-input" class="form-control" type="text"/>
                  ${this.renderCategoryHints()}
                  <div name="category-list" class="form-text">
                    ${this.renderCategories()}
                  </div>
                </div>
              </div>
              <div name="infrastructures" class="mb-3">
                <label class="form-label">Compatible Infrastrucures</label>
                <select name="infrastructure-input" class="form-control">
                  ${this.renderInfrastructureOptions()}
                </select>
                <div name="infrastructure-list" class="form-text">
                  ${this.renderInfrastructures()}
                </div>
              </div>
            </div>
          </div>
          <details class="card">
            <summary class="card-header">
              <div class="ccp-toolbar-header">
                <div>
                  <span class="mr-2">Inputs</span>
                </div>
                <div class="ccp-toolbar-right">
                  ${this.renderStandardInputButtons()}
                  ${this.renderPlusButton("add-input")}    
                </div>
              </div>
            </summary>
            <div class="card-body" name="input-list">
            </div>
          </details>
          <details class="card">
            <summary class="card-header" name="output-buttons">
              <div class="ccp-toolbar-header">
                <div>
                  <span class="mr-2">Outputs</span>
                </div>
                <div class="ccp-toolbar-right">
                  ${this.renderStandardOutputButtons()}
                  ${this.renderPlusButton("add-output")}    
                </div>
              </div>
            </summary>
            <div class="card-body" name="output-list">
              <d4s-ccp-output-editor></d4s-ccp-output-editor>
            </div>
          </div>
          <details class="card" name="script-list">
            <summary class="card-header">
              <div class="ccp-toolbar-header">
                <div>
                  <span>Scripts</span>
                  <select name="script-selector" style="border:none; padding:.3rem">
                    <option value="deploy-script">Deploy</option>
                    <option value="execute-script">Execute</option>
                    <option value="undeploy-script">Undeploy</option>
                  </select>
                </div>
              </div>
            </summary>
            <div class="card-body" name="input_container">
              ${this.renderScripts()}
            </div>
          </div>
        </div>
      </div>
    `
    this.renderInputs()
    this.renderOutputs()
    
    this.#cloneornew_dialog = this.#rootdoc.querySelector("#cloneornew")
    
    this.#rootdoc.querySelector("#cloneornew button[name=clone]").addEventListener("click", ev=>{
      this.#cloneornew_dialog.classList.remove("show")
      this.#cloneornew_dialog.style.display = "none"
      this.cloneMethod(this.#dragging_method)
      this.#dragging_method = null
    })
    
    this.#rootdoc.querySelector("#cloneornew button[name=edit]").addEventListener("click", ev=>{
      this.#cloneornew_dialog.classList.remove("show")
      this.#cloneornew_dialog.style.display = "none"
      this.editMethod(this.#dragging_method)
      this.#dragging_method = null
    })
    
    this.#rootdoc.querySelector("input[name=title]").addEventListener("input", ev=>{
      this.#current.title = ev.currentTarget.value
      this.#rootdoc.querySelector("span[name=header]").innerText = this.#current.title
    })
    
    this.#rootdoc.querySelector("input[name=version]").addEventListener("input", ev=>{
      this.#current.version = ev.currentTarget.value
    })
    
    this.#rootdoc.querySelector("input[name=description]").addEventListener("input", ev=>{
      this.#current.description = ev.currentTarget.value
    })
    
    this.#rootdoc.addEventListener("drop", ev=>{
      if(ev.dataTransfer && ev.dataTransfer.getData('text/plain+ccpmethod')){
        const method = JSON.parse(ev.dataTransfer.getData('application/json+ccpmethod'))
        ev.stopImmediatePropagation()
        ev.preventDefault()
        ev.stopPropagation()
        this.cloneOrEditMethod(method)
      }
    })
    
    this.#rootdoc.addEventListener("dragover", ev=>{
      ev.preventDefault()
    })
    
    this.#rootdoc.querySelector("button[name=reset]").addEventListener("click", ev=>{
      if(window.confirm("All unsaved data will be lost. Proceed?")){
        this.resetMethod() 
      }
      ev.preventDefault()
      ev.stopPropagation()
    })
   
    this.#rootdoc.querySelector("button[name=save]").addEventListener("click", ev=>{
      ev.preventDefault()
      ev.stopPropagation()
      this.saveMethod()     
    })
    
    if(this.#isupdate){
      this.#rootdoc.querySelector("button[name=delete]").addEventListener("click", ev=>{
        ev.preventDefault()
        ev.stopPropagation()
        this.deleteMethod()
      })  
    }
       
    this.#rootdoc.querySelector("input[name=keyword-input]").addEventListener("keypress", ev=>{
      if(ev.key === "Enter" || ev.which === 13){
        ev.preventDefault()
        ev.stopPropagation()
        const val = ev.target.value
        ev.target.value = null
        if(!this.#current.keywords){
          this.#current.keywords = [val]
        }else{
          this.#current.keywords.push(val)
        }
        this.reRenderKeywords()
      }
    })
    
    this.#rootdoc.querySelector("div[name=keyword-list]").addEventListener("click", ev=>{
      ev.preventDefault()
      ev.stopPropagation()
      if(ev.target.getAttribute('name') === "delete-keyword"){
        const index = ev.target.getAttribute("data-index")
        this.#current.keywords.splice(index, 1)
        this.reRenderKeywords()
        this.#rootdoc.querySelector("input[name=keyword-input]").focus()
      }
    })
    
    this.#rootdoc.querySelector("input[name=category-input]").addEventListener("keypress", ev=>{
      if(ev.key === "Enter" || ev.which === 13){
        ev.preventDefault()
        ev.stopPropagation()
        const val = ev.target.value
        ev.target.value = null
        if(this.#current.metadata.filter(md=>md.role === "category" && md.title === val).length === 0){
          this.#current.metadata.push({ role : "category", title : val})
          this.reRenderCategories()
        }
      }
    })
    
    this.#rootdoc.querySelector("div[name=category-list]").addEventListener("click", ev=>{
      ev.preventDefault()
      ev.stopPropagation()
      if(ev.target.getAttribute('name') === "delete-category"){
        const val = ev.target.parentElement.title
        this.#current.metadata = this.#current.metadata.filter(md=>md.role !== "category" || md.title !== val)
        this.reRenderCategories()
        this.#rootdoc.querySelector("input[name=category-input]").focus()
      }
    })
    
    this.#rootdoc.querySelector("div[name=infrastructures]").addEventListener("change", ev=>{
      if(ev.target.getAttribute("name") === "infrastructure-input" && ev.target.value){
        ev.preventDefault()
        ev.stopPropagation()
        const id = ev.target.value
        const display = ev.target.options[ev.target.selectedIndex].text
        let link = {
          rel : "compatibleWith", title : display, href : "infrastructures/" + id
        }
        if(!this.#current.links){
          this.#current.links = [link]
        }else{
          this.#current.links.push(link)
        }
        this.reRenderInfrastructures(ev.currentTarget)
      }
    })
    
    this.#rootdoc.querySelector("div[name=infrastructure-list]").addEventListener("click", ev=>{
      ev.preventDefault()
      ev.stopPropagation()
      if(ev.target.getAttribute('name') === "delete-infrastructure"){
        const index = ev.target.getAttribute("data-index")
        this.#current.links.splice(index, 1)
        this.reRenderInfrastructures()
      }
    })
    
    this.#rootdoc.querySelector("button[name=add-input]").addEventListener("click", ev=>{
      ev.preventDefault()
      ev.stopPropagation()
      this.#tmp_inputs.push(
        {
          id : "new_input",
          title : "New input",
          description : "A new input field",
          minOccurs : 1,
          maxOccurs : 1,
          schema : {
            type : "string",
            format : null,
            contentMediaType : "text/plain",
            default : ""
          }
        }
      )
      this.renderInputs()
    })
    
    this.#rootdoc.querySelector("button[name=add-ccpannotation]").addEventListener("click", ev=>{
      ev.preventDefault()
      ev.stopPropagation()
      this.#tmp_inputs.push(
        {
          id : "ccpnote",
          title : "Annotations for execution",
          description : "The value of this parameter will be associated as annotation to the execution",
          minOccurs : 1,
          maxOccurs : 1,
          schema : {
            type : "string",
            format : null,
            contentMediaType : "text/plain",
            default : ""
          }
        }
      )
      this.renderInputs()
    })
    
    this.#rootdoc.querySelector("div[name=input-list]").addEventListener("click", ev=>{
      const evname = ev.target.getAttribute('name')
      if(evname === "delete-input"){
        ev.preventDefault()
        ev.stopPropagation()
        const index = Number(ev.target.getAttribute("data-index"))
        console.log("deleting input at index", index)
        this.deleteTmpInputAt(index)
        this.renderInputs()
      } 
    })
    
    this.#rootdoc.querySelector("button[name=add-output]").addEventListener("click", ev=>{
      ev.preventDefault()
      ev.stopPropagation()
      this.#tmp_outputs.push(
        {
          id : "new_output",
          title : "New ouput",
          description : "A new output field",
          minOccurs : 1,
          maxOccurs : 1,
          metadata : [
            {
              "role" : "file",
              "title" : "newoutput.txt",
              "href" : "newoutput.txt"
            }
          ],
          schema : {
            type : "string",
            contentMediaType : "text/plain"
          }
        }
      )
      this.renderOutputs()
    })
    
    this.#rootdoc.querySelector("button[name=add-stdout]").addEventListener("click", ev=>{
      ev.preventDefault()
      ev.stopPropagation()
      this.#tmp_outputs.push(
        {
          id : "stdout",
          title : "Standard output",
          description : "Standard output channel",
          minOccurs : 1,
          maxOccurs : 1,
          metadata : [
            {
              "role" : "file",
              "title" : "stdout",
              "href" : "stdout"
            }
          ],
          schema : {
            type : "string",
            contentMediaType : "text/plain"
          }
        }
      )
      this.renderOutputs()
    })
    
    this.#rootdoc.querySelector("button[name=add-stderr]").addEventListener("click", ev=>{
      ev.preventDefault()
      ev.stopPropagation()
      this.#tmp_outputs.push(
        {
          id : "stderr",
          title : "Standard error",
          description : "Standard error channel",
          minOccurs : 1,
          maxOccurs : 1,
          metadata : [
            {
              "role" : "file",
              "title" : "stderr",
              "href" : "stderr"
            }
          ],
          schema : {
            type : "string",
            contentMediaType : "text/plain"
          }
        }
      )
      this.renderOutputs()
    })
    
    this.#rootdoc.querySelector("div[name=output-list]").addEventListener("click", ev=>{
      const evname = ev.target.getAttribute('name')
      if(evname === "delete-output"){
        ev.preventDefault()
        ev.stopPropagation()
        const index = Number(ev.target.getAttribute("data-index"))
        console.log("deleting output at index", index)
        this.deleteTmpOutputAt(index)
        this.renderOutputs()
      } 
    })
    
    this.#rootdoc.querySelector("select[name=script-selector]").addEventListener("change", ev=>{
      ev.preventDefault()
      ev.stopPropagation()
      const scriptname = ev.target.value
      const areas = Array.prototype.slice.call(this.#rootdoc.querySelectorAll("textarea.script-area"))
      areas.forEach(a=>{ 
        if(a.getAttribute("name") === scriptname) a.classList.remove("d-none");
        else a.classList.add("d-none")
      })       
    })
    
    this.#rootdoc.querySelector("details[name=script-list]").addEventListener("input", ev=>{
      ev.preventDefault()
      ev.stopPropagation()
      const scriptname = ev.target.getAttribute("name")
      const script = this.#current.additionalParameters.parameters.filter(p=>p.name === scriptname)[0]
      if(script) script.value = ev.target.value.split(/\r?\n/);
    })
  }
  
  renderAuthors(){
    return `
      <ul class="author_list">
      ${ this.#current.metadata.
          filter(md=>md.role === "author").
          map(a=>`<li class="badge bg-warning text-dark" title="Author: ${a.title}">${a.title}</li>`).join() }
      </ul>
    `
  }
  
  renderCategoryHints(){
    const ml = document.querySelector("d4s-ccp-methodlist")
    const cats = ml.getCategoryHints()
    if(cats.indexOf("Uncategorised") === -1) cats.push("Uncategorised")
    return `
      <datalist id="categoryhints">
        ${cats.map(c=>`<option value="${c}">${c}</option>`)}
      </datalist>
    `
  }
  
  renderContexts(){
    return `
      <ul class="context_list">
      ${ this.#current.metadata.
          filter(md=>md.role === "context").
          map(c=>`<li class="context_list_item" title="${c.title}">${c.title}</li>`).join("") }
      </ul>
    `
  }
  
  renderSaveButton(){
    return `
      <button title="Save" name="save" class="btn btn-primary ccp-toolbar-button">
        ${this.#disc_icon}  
      </button>
    `
  }
  
  renderDeleteButton(){
    return `
      <button title="Delete" name="delete" class="btn btn-danger ccp-toolbar-button">
        ${this.#delete_icon}  
      </button>
    `
  }
  
  renderResetButton(){
    return `
      <button name="reset" title="Reset" class="btn btn-primary ccp-toolbar-button">
        <svg viewBox="0 0 24 24">
          ${this.#erase_icon} 
        </svg>
      </button>
    `    
  }
  
  renderPlusButton(name){
    return `
      <button name="${name}" title="Add" name="reset" class="btn btn-primary ccp-toolbar-button">
        ${this.#plus_icon}   
      </button>
    `
  }
  
  renderStandardInputButtons(){
    return `
      <button name="add-ccpannotation" title="Add annotation input" name="reset" class="btn btn-info ccp-toolbar-button">
        ${this.#annotation_input_icon}  
      </button>
    `
  }
  
  renderStandardOutputButtons(){
    return `
      <button name="add-stdout" title="Add stdout" class="btn btn-success ccp-toolbar-button">
        ${this.#output_icon}  
      </button>
      <button name="add-stderr" title="Add stderr" class="btn btn-danger ccp-toolbar-button">
        ${this.#output_icon}
      </button>
    `
  }
  
  reRenderKeywords(){
    this.#rootdoc.querySelector("div[name=keyword-list]").innerHTML = this.renderKeywords()
  }
  
  renderKeywords(){
    if(this.#current.keywords){
      return this.#current.keywords.map((k,i) => {
        return `
          <div class="ccp-option badge ccp-keyword" title="${k}" alt="${k}">
            <span>${k}</span>
            <span class="btn text-danger ccp-toolbar-button" name="delete-keyword" data-index="${i}">x</span>
          </div>
        `
      }).join("\n")
    }else{
      return ""
    }
  }
  
  reRenderCategories(){
    this.#rootdoc.querySelector("div[name=category-list]").innerHTML = this.renderCategories()
  }
  
  renderCategories(){
    if(this.#current.metadata){
      return this.#current.metadata.filter(md=>md.role === "category").map((k,i) => {
        return `
          <div class="ccp-option badge ccp-category" title="${k.title}" alt="${k.title}">
            <span>${k.title}</span>
            <span class="btn text-danger ccp-toolbar-button" name="delete-category" data-index="${i}">x</span>
          </div>
        `
      }).join("\n")
    }else{
      return ""
    }
  } 
 
  reRenderInfrastructures(){
    this.#rootdoc.querySelector("select[name=infrastructure-input]").innerHTML = this.renderInfrastructureOptions()
    this.#rootdoc.querySelector("div[name=infrastructure-list]").innerHTML = this.renderInfrastructures()
  }
 
  renderInfrastructureOptions(){
    const selectedinfras = this.#current.links.filter(l=>l.rel === "compatibleWith").map(i=>i.href.split("/")[1])
    const available = this.#infrastructures.filter(i=>{ return selectedinfras.indexOf(i.id) === -1 }) 
    return `
      <option></option>
      ${
        available.map(infra=>{
          return `
            <option value="${infra.id}" title="${infra.description}">${infra.name}</option>
          `
        }).join("\n")
      } 
    `
  }
  
  renderInfrastructures(){
    return this.#current.links.filter(l=>l.rel === "compatibleWith").map((l, i)=>{
      return `
        <div class="ccp-option badge ccp-keyword" title="${l.title}" alt="${l.title}">
          <span>${l.title}</span>
          <span class="btn text-danger ccp-toolbar-button" name="delete-infrastructure" data-index="${i}">x</span>
        </div>
      `
    }).join("\n")
  }
   
  renderInputs(){
    const parent = this.#rootdoc.querySelector("div[name=input-list]")
    parent.innerHTML = "" 
    return this.#tmp_inputs.map((inp, i)=>{
      const c = document.createElement("d4s-ccp-input-editor");
      parent.appendChild(c)
      c.render(inp, i)    
    })
  }
  
  renderOutputs(){
    const parent = this.#rootdoc.querySelector("div[name=output-list]")
    parent.innerHTML = "" 
    return this.#tmp_outputs.map((outp, i)=>{
      const c = document.createElement("d4s-ccp-output-editor");
      parent.appendChild(c)
      c.render(outp, i)    
    })
  }
  
  renderScripts(){
    const val = 'deploy-script'
    return this.#current.additionalParameters.parameters.map(
       (script, i) => {
         let code = script.value.length ? script.value.join("\r\n") : ""
         return `
           <textarea rows="5" class="script-area form-control ${script.name === val ? '' : 'd-none'}" name="${script.name}">${code}</textarea>
         `
       }
    ).join("\n")
  }
}

window.customElements.define('d4s-ccp-methodeditor', CCPMethodEditorController);
