class CCPInputWidgetController extends HTMLElement {
	
  #data = null;
  
  constructor(){
  	super()
  }
  
  connectedCallback(){
    this.#data = JSON.parse(this.getAttribute("input"))
    
    if(this.isChecklist()){
      
      const opts = this.#data.schema.enum.join(",")
      this.innerHTML += `<d4s-ccp-input-checklist readonly="${this.#data.schema.readOnly}" options="${opts}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-checklist>`
    
    } else if(this.isEnum()){
      
      const opts = this.#data.schema.enum.join(",")
      this.innerHTML += `<d4s-ccp-input-enum readonly="${this.#data.schema.readOnly}" options="${opts}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-enum>`    
    }else if(this.isCode()){
      this.innerHTML += `<d4s-ccp-input-textarea readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-textarea>`
    
    }else if(this.isDateTime()){
      const t = this.#data.schema.format.toLowerCase() === "datetime" ? 
        "datetime-local" : this.#data.schema.format.toLowerCase()
      this.innerHTML += `<d4s-ccp-input-simple type="${t}" readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-simple>`
    
    }else if(this.isFile()){
      this.innerHTML += `<d4s-ccp-input-file readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-file>`
    
    }else if(this.isRemoteFile()){
      this.innerHTML += `<d4s-ccp-input-remotefile readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-remotefile>`
    
    }else if(this.isSecret()){
      this.innerHTML += `<d4s-ccp-input-secret readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-secret>`
    
    }else if(this.isBoolean()){
      this.innerHTML += `<d4s-ccp-input-boolean readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-boolean>`
    
    }else if(this.isNumber()){
      this.innerHTML += `<d4s-ccp-input-simple type="number" readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-simple>`
    
    }else{
      this.innerHTML += `<d4s-ccp-input-simple readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-simple>`
    }
  }
  
  set value(value){
    this.firstElementChild.value = value  
  }
  
  get value(){
    return this.firstElementChild.value.length === 1 ? 
      this.firstElementChild.value[0] : 
      this.firstElementChild.value
  }
  
  get name(){
    return this.firstElementChild.name
  }
  
  isChecklist(){
    return this.isEnum() && (this.#data.schema.format === "checklist") 
  }
  
  isNumber(){
    return (this.#data.schema.type === "string") && 
      ("format" in this.#data.schema) && 
      (this.#data.schema.format != null) &&
      (this.#data.schema.format.toLowerCase() === "number") 
  }
  
  isBoolean(){
    return (this.#data.schema.type === "string") && 
      ("format" in this.#data.schema) && 
      (this.#data.schema.format != null) &&
      (this.#data.schema.format.toLowerCase() === "boolean") 
  }
  
  isEnum(){
    return (this.#data.schema.type === "string") && ("enum" in this.#data.schema) 
  }

  isSecret(){
    return (this.#data.schema.type === "string") && 
      ("format" in this.#data.schema) && 
      (this.#data.schema.format != null) &&
      (this.#data.schema.format.toLowerCase() === "secret")
  }
  
  isCode(){
    return (this.#data.schema.type === "string") && 
      ("format" in this.#data.schema) && 
      (this.#data.schema.format != null) && 
      (this.#data.schema.format.toLowerCase() === "code") 
  }

  isFile(){
    return (this.#data.schema.type === "string") && 
      ("format" in this.#data.schema) && 
      (this.#data.schema.format != null) && 
      (this.#data.schema.format.toLowerCase() === "file") 
  }
  
  isRemoteFile(){
    return (this.#data.schema.type === "string") && 
      ("format" in this.#data.schema) && 
      (this.#data.schema.format != null) && 
      (this.#data.schema.format.toLowerCase() === "remotefile") 
  }
  
  isDateTime(){
    return (this.#data.schema.type === "string") && 
      ("format" in this.#data.schema) &&
      (this.#data.schema.format != null) &&
      (["date", "time", "datetime"].indexOf(this.#data.schema.format.toLowerCase()) !== -1) 
  }
}
window.customElements.define('d4s-ccp-input', CCPInputWidgetController);

class CCPBaseInputWidgetController extends HTMLElement{
  #rootdoc = null;
  #minOccurs = 1;
  #maxOccurs = 1;
  #description = ""
  #title = ""
  #name = ""
  #default = null
  #options = null
  #value = null
  #readonly = false
  
  //useful to avoid having a custom element for every basic input type
  #type = null
  
  #count = 1
  
  constructor(){
    super()
    this.#rootdoc = this//this.attachShadow({ mode: "open" });
    this.#name = this.getAttribute("name")
    this.#title = this.getAttribute("title")
    this.#description = this.getAttribute("description")
    this.#default = this.getAttribute("default")
    this.#minOccurs = Number(this.getAttribute("minoccurs") ? this.getAttribute("minoccurs") : 1)
    this.#maxOccurs = Number(this.getAttribute("maxoccurs") ? this.getAttribute("maxoccurs") : 1)
    this.#readonly = this.getAttribute("readonly") === "true"
    
    // coalesce all basic input types
    this.#type = this.getAttribute("type")
    
    // Handle enum case
    this.#options = (this.getAttribute("options") ? this.getAttribute("options").split(",") : null)
     
    this.value = Array(Math.max(this.#minOccurs,1)).fill(this.#default)
  }
  
  setValue(v){
    this.#value = v
    this.render()
  }
  
  set value(v){
    const actual = Array.isArray(v) ? v : [v]
    if(actual.length < this.#minOccurs || actual.length > this.#maxOccurs){
      throw `Value with length ${v.length} does not respect bounds [${this.minOccurs},${this.maxOccurs}]`
    }
    this.#value = actual
    this.render()
  }
  
  get value(){
    return this.#value
  }
  
  get rootdoc(){
    return this.#rootdoc
  }
  
  get required(){
    return this.#minOccurs > 0
  }
  
  get readonly(){
    return this.#readonly
  }
  
  isIncrementable(){
    return this.#value.length < this.#maxOccurs
  }
  
  isDecrementable(){
    return this.#value.length > this.#minOccurs && this.#value.length > 1
  }
  
  get count(){
    return this.#count
  }
  
  get name(){
    return this.#name
  }
  
  get title(){
    return this.#title
  }
  
  get description(){
    return this.#description
  }
  
  get default(){
    return this.#default
  }
  
  get options(){
    return this.#options
  }
  
  set default(v){
    this.#default = v
  }
  
  get type(){
    return this.#type
  }
  
  renderPlusMinus(){
    this.rootdoc.querySelector("div[name=plusminus]").innerHTML = `
        ${this.isIncrementable() ? `<span name="plus" title="add one" class="badge text-success border-success btn">+</span>` : ``}
        ${this.isDecrementable() ? `<span name="minus" title="remove one" class="badge border border-danger  text-danger btn">-</span>` : ``}
    `
  }
  
  renderContent(){
    this.rootdoc.querySelector("div[name=content]").innerHTML = this.content()
  }
  
  render(){
    this.rootdoc.innerHTML = `
      <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet">
      <div name="root" class="my-3 ccp-input-widget form-field row">
        <label class="form-label">
          <div class="d-flex justify-content-between">
            <div>
              ${this.required ? `<span title="Required" class="p-1 text-danger">*</span>` : ``}
              ${this.title}
              <span class="badge text-primary" title="${this.#description}" alt="${this.#description}">?</span>
            </div>
            <div name="tools" class="d-flex" style="gap:2px">
              <div name="plusminus">
              </div>
            </div>
          </div>
          <div name="content">
          </div>
        </label>
     </div>        
    `
    
    this.renderPlusMinus()
    this.renderContent()
    
    this.#rootdoc.querySelector("div[name=root]").addEventListener("click", ev=>{
      const src = ev.target.getAttribute("name")
      if(src === "plus"){
        this.#value.push(this.#default)
        this.renderPlusMinus()
        this.renderContent()
      }else if(src === "minus"){
        this.#value.pop()
        this.renderPlusMinus()
        this.renderContent()
      }
    })
  }
}

class CCPSimpleInputWidgetController extends CCPBaseInputWidgetController{
    
  constructor(){
    super()
  }
  
  connectedCallback(){
    this.rootdoc.addEventListener("input", ev=>{
      if(ev.target.getAttribute("name") === this.name){
        const index = Number(ev.target.getAttribute("data-index"))
        this.value[index] = ev.target.value 
      }
    })
  }
    
  content(){
    if(this.value.length <= 1){
      return `<input data-index="0" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>`
    }
    var out = 
      this.value.map((c,i)=>{
        return `
          <input name="${this.name}" value="${c}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>
        `
      }).join("\n")
    return out
  }
}
window.customElements.define('d4s-ccp-input-simple', CCPSimpleInputWidgetController);

class CCPEnumInputWidgetController extends CCPBaseInputWidgetController{
  
  constructor(){
    super()
  }
  
  connectedCallback(){
    this.rootdoc.addEventListener("change", ev=>{
      if(ev.target.getAttribute("name") === this.name){
        const index = Number(ev.target.getAttribute("data-index"))
        this.value[index] = ev.target.value 
      }
    })
  }
  
  content(){
    const opts = this.options.map(o=>`<option value="${o}">${o}</option>`).join("\n")
    if(this.value.length <= 1){
      return `<select data-index="0" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'disabled' : ''} ${this.required ? 'required' : ''}>${opts}</select>`
    }
    var out = 
      this.value.map((c,i)=>{
        return `
          <select data-index="${i}" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'disabled' : ''} ${this.required ? 'required' : ''}>${opts}</select>
        `
      }).join("\n")
    return out
  }
}
window.customElements.define('d4s-ccp-input-enum', CCPEnumInputWidgetController);

class CCPChecklistInputWidgetController extends CCPBaseInputWidgetController{
  
  constructor(){
    super()
  }
   
  connectedCallback(){
    this.rootdoc.addEventListener("change", ev=>{
      if(ev.target.getAttribute("name") === this.name){
        const index = Number(ev.target.getAttribute("data-index"))
        
        const elems = Array.prototype.slice.call(ev.currentTarget.querySelectorAll(`input[name='${this.name}'][data-index='${index}']`))
        
        this.value[index] = elems.filter(e=>e.checked).map(e=>e.value).join(",")
      }
    })
  }
  
  buildOpts(index, selections){
    return this.options.map(o=>`
      <div class="form-check form-switch form-check-inline">
        <label>${o}</label>
        <input data-index="${index}" class="form-check-input" type="checkbox" name="${this.name}" value="${o}" ${this.readonly ? 'readonly' : ''} ${selections.indexOf(o) > -1 ? 'checked' : ''}/></div>
    `).join("\n")
  }
  
  content(){
    if(this.value.length === 1){
      const opts = this.buildOpts(0, this.value.length ? this.value[0].split(",") : [])
      return `
        <div class="my-2">
          ${opts}
        </div>
      `
    }
    var out = 
      this.value.map((c,i)=>{
        const opts = this.buildOpts(i, c.split(","))
        return `
          <div class="my-2">
            ${opts}
          </div>
        `
      }).join("\n")
    return out
  }
}
window.customElements.define('d4s-ccp-input-checklist', CCPChecklistInputWidgetController);

class CCPBooleanInputWidgetController extends CCPBaseInputWidgetController{
  
  constructor(){
    super()
  }
  
  connectedCallback(){
    this.value.forEach((v,i)=>{ this.value[i] = v === "" ? false : v})
    this.rootdoc.addEventListener("input", ev=>{
      if(ev.target.getAttribute("name") === this.name){
        const index = Number(ev.target.getAttribute("data-index"))
        this.value[index] = ev.target.checked
      }
    })
  }
  
  content(){
    if(this.value.length <= 1){
      return `
        <div class="my-2 form-check form-switch form-check-inline">
          <input data-index="0" value="${this.value.length ? this.value[0] : false}" class="my-2 form-check-input" type="checkbox" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.value[0] == "true" ? 'checked' : ''} value="${ this.value[0]}"/>
        </div>
      `
    }
    var out = 
      this.value.map((c,i)=>{
        return `
          <div class="my-2 form-check form-switch form-check-inline">
            <input data-index="${i}" value="${c}" class="my-2 form-check-input" type="checkbox" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${c == "true" ? 'checked' : ''} value="${this.value[i]}"/>
          </div>
        `
      }).join("\n")
    return out
  }
}
window.customElements.define('d4s-ccp-input-boolean', CCPBooleanInputWidgetController);

class CCPTextAreaWidgetController extends CCPBaseInputWidgetController{
    
  constructor(){
    super()
  }
  
  connectedCallback(){
    this.rootdoc.addEventListener("input", ev=>{
      if(ev.target.getAttribute("name") === this.name){
        const index = Number(ev.target.getAttribute("data-index"))
        this.value[index] = ev.target.value 
      }
    })
  }
  
  content(){
    if(this.value.length <= 1){
      return `<textarea data-index="0" class="my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" rows="5" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}>${this.value.length ? this.value[0] : ''}</textarea>`
    }
    var out = 
      this.value.map((c,i)=>{
        return `
          <textarea name="${this.name}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" rows="5" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}>${c}</textarea>
        `
      }).join("\n")
    return out
  }
}
window.customElements.define('d4s-ccp-input-textarea', CCPTextAreaWidgetController);

class CCPFileInputWidgetController extends CCPBaseInputWidgetController{
  
  constructor(){
    super()
  }
  
  connectedCallback(){
    this.rootdoc.addEventListener("change", ev=>{
      const tgt = ev.target
      if(tgt.getAttribute("name") === this.name){
        const index = Number(tgt.getAttribute("data-index"))
        const file = ev.target.files[0]
        if(file.size > 100*1024){
          alert("This input allows only small files (100K). Use references instead ")
          ev.stopPropagation()
          ev.preventDefault()
          tgt.value = null
          return false
        }
        const reader = new FileReader()
        reader.addEventListener('load', ev=>{
          let encoded = ev.target.result.toString().replace(/^data:(.*,)?/, '');
          if ((encoded.length % 4) > 0) {
            encoded += '='.repeat(4 - (encoded.length % 4));
          }
          this.value[index] = encoded
          this.querySelector("small[name=preview]").textContent = encoded.substr(0,5) + "..." + encoded.substr(encoded.length-5)
        })
        reader.readAsDataURL(file)
      }
    })
  }
  
  content(){
    if(this.value.length <= 1){
     const v = this.value.length ? this.value[0] : ''
     return `
        <input type="file" data-index="0" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required && !v ? 'required' : ''} value="${v}"/>
        <small name ="preview" class="form-text text-muted">${v.substr(0,5) + "..." + v.substr(v.length-5)}</small>
      `
    }
    var out = 
      this.value.map((c,i)=>{
        return `
          <input type="file" data-index="${i}" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''} value="${c}"/>
          <small name ="preview" class="form-text text-muted">${c.substr(0,5) + "..." + c.substr(s.length-5)}</small>
        `
      }).join("\n")
    return out
  }
}
window.customElements.define('d4s-ccp-input-file', CCPFileInputWidgetController);

class CCPRemoteFileInputWidgetController extends CCPBaseInputWidgetController{
  
  #iss = null;
  #addresses = {
    "https://accounts.dev.d4science.org/auth/realms/d4science" : "https://workspace-repository.dev.d4science.org/storagehub/workspace",
    "https://accounts.pre.d4science.org/auth/realms/d4science" : "https://pre.d4science.org/workspace",
    "https://accounts.d4science.org/auth/realms/d4science" : "https://api.d4science.org/workspace"
  };
  
  constructor(){
    super()
    this.#iss = document.querySelector("d4s-boot-2").loginToken.iss
  }
  
  get baseurl(){
    return this.#addresses[this.#iss]
  }
  
  connectedCallback(){
    this.rootdoc.addEventListener("input", ev=>{
      if(ev.target.getAttribute("name") === this.name){
        const index = Number(ev.target.getAttribute("data-index"))
        this.value[index] = ev.target.value 
      }
    })
    this.rootdoc.querySelector("div[name=tools]").innerHTML += `
      <svg name="trigger" style="width:24;height:24;fill:#007bff; cursor:pointer" viewBox="0 -960 960 960">
          <path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640H447l-80-80H160v480l96-320h684L837-217q-8 26-29.5 41.5T760-160H160Zm84-80h516l72-240H316l-72 240Zm0 0 72-240-72 240Zm-84-400v-80 80Z"/>
      </svg>
      <d4s-storage-tree 
        class="d-none position-absolute shadow border border-primary bg-light"
        style="left: 100%; margin: 0 0 0 -.5rem;min-width:350px; max-width:500px;z-index:1000; line-height:1.5rem;overflow-x:hidden;text-wrap:nowrap;text-overflow: ellipsis;min-height:5rem; max-height:10rem; padding:5px;"
        base-url="${this.baseurl}"
        file-download-enabled="true" 
        show-files="true"
        allow-drag="true"/>
    `
    const ws = this.rootdoc.querySelector("d4s-storage-tree")
    this.rootdoc.querySelector("svg[name=trigger]").addEventListener("click", ev=>{
      ws.classList.toggle("d-none")
    })
    
    this.addEventListener("dragover", ev=>{
      ev.preventDefault()
    })
    
    this.addEventListener("drop", ev=>{
      ev.stopPropagation()
      if(ev.target.getAttribute("name") == this.name && ev.target.getAttribute("data-index") != null){
        const index = Number(ev.target.getAttribute("data-index"))
        ev.target.value = ev.dataTransfer.getData("text/plain+downloadlink")
        this.value[index] = ev.target.value
      }
    })
    
    document.addEventListener("keydown", ev=>{
      if(ev.code == 'Escape' ) ws.classList.add("d-none");
    })
  }
    
  content(){
    if(this.value.length <= 1){
      return `
       <input data-index="0" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>`
    }
    var out = 
      this.value.map((c,i)=>{
        return `
          <input name="${this.name}" value="${c}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>
        `
      }).join("\n")
    return out
  }
}
window.customElements.define('d4s-ccp-input-remotefile', CCPRemoteFileInputWidgetController);

class CCPSecretInputWidgetController extends CCPBaseInputWidgetController{
    
  constructor(){
    super()
  }
  
  connectedCallback(){
    this.rootdoc.addEventListener("input", ev=>{
      if(ev.target.getAttribute("name") === this.name){
        const index = Number(ev.target.getAttribute("data-index"))
        this.value[index] = ev.target.value 
      }
    })
    
    this.rootdoc.addEventListener("click", ev=>{
      if(ev.target.getAttribute("name") === "password_toggle"){
        const index = Number(ev.target.getAttribute("data-index"))
        const field = this.rootdoc.querySelector(`input[data-index='${index}']`)
        if(field.type === "text") field.type="password";
        else field.type = "text";
      }
    })
  }
    
  content(){
    if(this.value.length <= 1){
      return `
        <div style="position:relative">
          <input data-index="0" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" type="password" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>
          <span data-index="0" class="btn" style="position:absolute; right:0; bottom:0" name="password_toggle">&#128065;</span>
        </div>
        `
    }
    var out = 
      this.value.map((c,i)=>{
        return `
          <div style="position:relative">
            <input name="${this.name}" value="${c}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="password" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>
            <span data-index="${i}" class="btn" style="position:absolute; right:0; bottom:0" name="password_toggle">&#128065;</span>
          </div>
        `
      }).join("\n")
    return out
  }
}
window.customElements.define('d4s-ccp-input-secret', CCPSecretInputWidgetController);