/**
 * widget layout manager
 * example：
    <layout>
        <row>
            <col width='6'>
                <control type='input'/>
            </col>
            <col width='6'>
                <control type='hotwords'/>
            </col>
        </row>
        <row>
            <col width='9'>
                <control type='results'/>
            </col>
            <col width='3'>
                <control type='aggregation'/>
                <control type='aggregation'/>
            </col>
        </row>
        <row>
            <col width='9'>
                <control type='pagination'/>
            </col>
        </row>
    </layout>
 */
export class WidgetLayout {

  private xml: Document;

  private constructor(xml: Document) {
    this.xml = xml
  }

  /**
   * parse layout xml string
   * @param xmlstring 
   * @returns 
   */
  static parse(xmlstring: string): WidgetLayout {
    const parser = new DOMParser()
    const xml = parser.parseFromString(xmlstring, 'text/xml')
    return new WidgetLayout(xml);
  }

  /**
   * turn to layout xml string
   * @returns 
   */
  toString(): string {
    var oSerializer = new XMLSerializer()
    var sXML = oSerializer.serializeToString(this.xml)
    return sXML
  }

  /**
   * check if exists a control named `ctrl_name`
   * @param ctrl_name 
   * @returns 
   */
  control(ctrl_name: string): any {
    return this.xml.querySelector(`control[type='${ctrl_name}']`);
  }

  /**
   * get all rows in layout
   * @returns 
   */
  rows(): any {
    return this.xml.getElementsByTagName("row");
  }

  /**
   * append node to position
   * @param node 
   * @param row 
   * @param col 
   * @returns 
   */
  append(node: any, row = 0, col = 0): WidgetLayout {
    if (this.control(node.type) != null)
      return this;
    //get position of row and col
    let theNode: any;
    let rowNodes = this.xml.getElementsByTagName('row')
    if (row >= 0 && row < rowNodes.length) {
      var rowNode = rowNodes[row]
      var colNodes = rowNode.getElementsByTagName('col')
      if (col >= 0 && col < colNodes.length) {
        theNode = colNodes[col]
      }
    }
    var elem = this.xml.createElement('control')
    elem.setAttribute('type', node.type)

    if (theNode) {
      //拖入设计器的某个元素之上
      var cols = theNode.parentNode?.getElementsByTagName('col')
      var lastWidth = parseInt(cols[cols.length - 1].getAttribute('width'))
      var totalWidth = 0
      for (var i = 0; i < cols.length; i++) {
        totalWidth += parseInt(cols[i].getAttribute('width'))
      }
      if (totalWidth + node.width > 12) {
        if (node.width == lastWidth) cols[cols.length - 1].appendChild(elem)
        else this.appendToNewRow(theNode, elem, node.width)
      } else {
        this.appendToNewCol(theNode, elem, node.width)
      }
    } else {
      //拖入设计器的空白处
      this.appendToNewRow(theNode, elem, node.width)
    }
    return this
  }

  /**
   * remove node from position
   * @param row 
   * @param col 
   * @param index 
   * @returns 
   */
  remove(row: number, col: number, index: number): WidgetLayout {
    let rowNodes = this.xml.getElementsByTagName('row')
    if (row < rowNodes.length) {
      var rowNode = rowNodes[row]
      var colNodes = rowNode.getElementsByTagName('col')
      if (col < colNodes.length) {
        colNodes[col].removeChild(colNodes[col].childNodes[index])
        //如果该列已经没有子节点了，则删除该列
        if (!colNodes[col].hasChildNodes()) {
          rowNode.removeChild(colNodes[col])
        }
        //如果该行已经没有子节点了，则删除该行
        if (!rowNode.hasChildNodes()) {
          rowNode.parentNode?.removeChild(rowNode)
        }
      }
    }
    return this;
  }

  /**
   * get control attribute with name `attr_name`
   * @param ctrl_name 
   * @param attr_name 
   * @returns 
   */
  getAttribute(ctrl_name: string, attr_name: string): any {
    var ctl = this.xml.querySelector(`control[type='${ctrl_name}']`);
    return ctl ? ctl.getAttribute(attr_name) : null;
  }

  /**
   * get attributes of control
   * @param ctrl_name 
   */
  getAttributes(ctrl_name: string = ""): any {
    let attrs = ctrl_name
      ? this.xml.querySelector(`control[type='${ctrl_name}']`)?.attributes
      : this.xml.querySelector('layout')?.attributes
    var props: {[key: string]:any} = {}
    if (attrs)
      for (var i = 0; i < attrs.length; i++) {
        var att = attrs.item(i)
        if (att) {
          if (att.value === 'true') props[att.name] = true
          else if (att.value === 'false') props[att.name] = false
          else if (att.value) props[att.name] = att.value
        }
      }
    return props
  }

  /**
   * set control attributes
   * @param {row}} ctrl_name
   * @param {attrs} attrs
   */
  setAttributes(ctrl_name: string, attrs: any) {
    let ctrl = this.xml.querySelector(`control[type='${ctrl_name}']`);
    if (ctrl) {
      for (let key in attrs) {
        ctrl.setAttribute(key, attrs[key]);
      }
    }
  }

  /**
   * set control attributes
   * @param {row}} row
   * @param {col} col
   * @param {index} index
   * @param {attrs} attrs
   */
  setAttributesByPos(row: number, col: number, index: number, attrs: any) {
    let rowNodes = this.xml.getElementsByTagName('row')
    if (row < rowNodes.length) {
      var rowNode = rowNodes[row]
      var colNodes = rowNode.getElementsByTagName('col')
      if (col < colNodes.length) {
        if (index < colNodes[col].childNodes.length) {
          var controls = colNodes[col].getElementsByTagName('control')
          if (index < controls.length) {
            var control = controls[index]
            for (var key in attrs) {
              control.setAttribute(key, attrs[key])
            }
          }
        }
      }
    }
  }

  private appendToNewRow(node: any, elem: any, width: number) {
    var row = this.xml.createElement('row')
    var col = this.xml.createElement('col')
    col.setAttribute('width', width + "")
    col.appendChild(elem)
    row.appendChild(col)
    this.xml.getElementsByTagName('layout')[0].appendChild(row)
  }

  private appendToNewCol(node: any, elem: any, width: number) {
    var col = this.xml.createElement('col')
    col.setAttribute('width', width + "")
    col.appendChild(elem)
    node.parentNode.appendChild(col)
  }

}