/*
* Copyright (c) 2021 Xilinx, Inc.
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
* associated documentation files (the "Software"), to deal in the Software without restriction, including 
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
* copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to 
* the following conditions: 
* 
* The above copyright notice and this permission notice shall be included in all copies or substantial 
* portions of the Software. 
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* 
*
*
*/
import { PolymerElement, html } from "../../../node_modules/@polymer/polymer/polymer-element.js";
import { configObj, api_url, notificationRef } from "../../shared/constants.js";
import { clockTreeData, redirectToLogin, handleErrorResponse } from "../../shared/shared-script.js";

class TreeView extends PolymerElement {
  static get template() {
    return html`
    <style>
    :host {
        display: block;
    }
    .treeStructure {
        font-size: 14px;
        overflow-x: auto;
    }
    .treeStructure ul {
        position: relative;
        padding: 1em 0;
        white-space: nowrap;
        margin: 0 auto;
        text-align: center;
        overflow-x: hidden;
      }

      .treeStructure ul::after {
        content: '';
        display: table;
        clear: both;
      }

      .treeStructure li {
        vertical-align: top;
        text-align: center;
        list-style-type: none;
        position: relative;
        padding: 1em .5em 0 .5em;
      }

      .treeStructure li::before,
      .treeStructure li::after {
        content: '';
        position: absolute;
        top: 0;
        right: 50%;
        border-top: 1px solid #ccc;
        width: 50%;
        height: 1.1em;
      }

      .treeStructure li::after {
        right: auto;
        left: 50%;
        border-left: 1px solid #ccc;
      }

      .treeStructure li.inputRef::after,
      .treeStructure li.inputRef::before {
        display: none;
      }

      .treeStructure li.inputRef {
        padding-top: 0;
      }

      .treeStructure li:first-child::before,
      .treeStructure li:nth-last-child(2)::after {
        border: 0 none;
        border-radius: 0px !important;
      }

      .treeStructure li:nth-last-child(2)::before {
        border-right: 1px solid #ccc;
        border-radius: 0 5px 0 0;
        transform: translateX(1px);
      }

      .treeStructure li:first-child::after {
        border-radius: 5px 0 0 0;
      }

      .treeStructure ul ul::before {
        content: '';
        position: absolute;
        top: 1px;
        left: 50%;
        border-left: 1px solid #ccc;
        width: 0;
        height: 1em;
      }

      .bottomLine::after {
        content: '';
        position: absolute;
        bottom: -1em;
        left: 50%;
        border-left: 1px solid #ccc;
        width: 0;
        height: 1em;
      }

      .treeStructure .d-flex::before {
        height: 0 !important;
      }

      .treeStructure li a {
        border: 1px solid #ccc;
        padding: .5em .75em;
        text-decoration: none;
        display: inline-block;
        border-radius: 5px;
        color: #333;
        position: relative;
        top: 1px;
      }
      .freqValue {
        border-top: 1px solid;
      }
      .d-flex {
          display: flex;
          overflow-x: auto !important;
      }
      .inputRef {
          float: none !important;
          display: inline-block;
          text-align: left !important;
      }
      .inputRef::after,
      .inputRef::before {
          border: none !important;
      }
      .treePll::after,
      .treePll::before {
          border-top: none !important;
      }
      .d-block {
          display: block;
      }
      .d-inblock {
          display: inline-block;
      }
      paper-input {
          --paper-input-container: {
            width: 12em;
            padding: 0;
          }
          --paper-input-container-label: {
              font-weight: bold;
          }
      }
      .refreshBtn {
        padding: 4px 8px;
        background-color: #1E88E5;
        color: #fff;
        height: 30px;
        font-size: 14px;
        font-weight: bold;
      }
      .text-right {
        text-align: right;
      }
      .justify-center {
        justify-content: center;
      }
      .blkBgColor {
          background-color: #ddd;
          cursor: not-allowed;
      }
    </style>
    <div class="clockTree">
        <h2 class="text-right">
            <paper-button raised noink class="refreshBtn" on-tap="_refreshClock">Refresh</paper-button>
        </h2>
        <div hidden$={{!plldata.title}} class$="[[_computedClass(plldata.title)]]">
            <ul>
                <li class="inputRef">
                    <paper-input always-float-label disabled$={{plldata.npi_check}} on-blur="_onrefclkchanged" label="Ref Clock" value={{refclk}} auto-validate>
                        <div slot="suffix" style="margin-left:5px;font-size:12px;"> * {{multiplier}} &#247; {{divider}} MHz</div>
                    </paper-input>
                </li>
                <li class="treePll">
                    <a class="bottomLine">{{plldata.title}}
                        <div id='freqValue' class="freqValue">{{plldata.freq}}</div>
                    </a>
                    <ul class$="[[_computedClasses(plldata.title)]]">
                        <template is="dom-repeat" items="{{itemName}}" as='parent'>
                            <li>
                                <a>&#247; {{_itemByIndex(divisorBy, index)}}</a>
                                <ul>
                                    <template is="dom-repeat" items="{{_itemByIndex(itemName, index)}}" index-as="j" as='item'>
                                        <div>
                                            <a class$={{_computedBG(plldata.items,divisorBy,index,j)}}>{{item}}
                                                <div class="freqValue">{{_actualFreq(actFreq, index, j)}}</div>
                                            </a>
                                        </div>
                                    </template>
                                </ul>
                            </li>
                        </template>
                    </ul>
                </li>
            </ul>
        </div>
    </div>
    <iron-ajax id="clockrefdata" method="post" body={{refclkbody}} url=[[url]] handle-as="json" on-error="_handleErrorResponse" content-type="application/json" last-response="{{lastResponse}}"></iron-ajax>
    `;
  }

  static get properties() {
    return {
      divisorBy: {
        type: String,
        value: []
      },
      itemName: {
        type: Array,
        value: []
      },
      actFreq: {
        type: String,
        value: []
      },
      url: {
        type: String,
        value: api_url + 'getclockdetails/'
      },
      plldata: {
        type: Object,
        notify: true,
        value: {},
        reflectToAttribute: true,
        observer: '_pllUpdated'
      },
      refclk: {
        type: String,
        notify: true,
        value: '',
        reflectToAttribute: true
      },
      refclkbody: {
        type: Object,
        value: {
          'refresh': false
        }
      },
      multiplier: {
        type: String,
        notify: true,
        value: '',
        reflectToAttribute: true
      },
      divider: {
        type: String,
        notify: true,
        value: '',
        reflectToAttribute: true
      }
    };
  }

  _itemByIndex(data, i) {
    return data[i];
  }

  _actualFreq(p, i, j) {
    if (p.length > 0 && p[i]) return p[i][j];
  }

  _computedClasses(title) {
    let classes = 'd-flex';
    if (title === 'APLL') classes += ' justify-center';
    return classes;
  }

  _computedBG(data, divs, i, j) {
    const key = this._itemByIndex(divs, i);

    if (data[key][j].Clock_Act == 0) return 'blkBgColor';
    return;
  }

  _onrefclkchanged() {
    const key = this.dataset.key.toUpperCase();
    let pkey = this.dataset.pkey;
    const refclk = configObj[`${pkey}clockdata`].refclk[key.toUpperCase()].refclk;
    if (this.refclk == refclk) return;

    if (this.refclk === '') {
      this.refclk = refclk;
      return;
    }

    const resStatus = confirm("Altering reference clock will reflect to all PLLs.\nAre you sure?");

    if (resStatus == false) {
      this.refclk = refclk;
      return;
    }

    if (this.refclk >= 27 && this.refclk <= 60) {
      document.querySelector("#pageloader").style.display = "block";
      const data = {
        'refclk': this.refclk,
        'refresh': false,
        "clock": this.dataset.pkey
      };
      this.refclkbody = data;

      this._commonCommands();
    } else {
      notificationRef.setAttribute('text', 'Ref clock value should be within the range 27-60MHz');
      notificationRef.open();
      this.refclk = refclk;
    }
  }

  _refreshClock() {
    let self = this;
    self.refclkbody = {
      'refresh': true,
      "clock": this.dataset.pkey
    };
    document.querySelector("#pageloader").style.display = "block";

    this._commonCommands();
  }

  _commonCommands() {
    let pkey = this.dataset.pkey;
    const session = JSON.parse(localStorage.getItem('storedUser'));
    this.$.clockrefdata.headers['uid'] = session.uid;
    this.$.clockrefdata.headers['version'] = session.version;
    let request = this.$.clockrefdata.generateRequest();
    request.completes.then(req => {
      if (req.response && req.response.status === 'Fail') {
        notificationRef.setAttribute('text', 'Please connect to board and try again');
        notificationRef.open();
        document.querySelector("#pageloader").style.display = "none";
        return;
      }

      if (req.response.error) {
        notificationRef.setAttribute('text', req.response.error);
        notificationRef.open();
        document.querySelector("#pageloader").style.display = "none";
        return;
      }

      const data = req.response.data.data;
      let ObjData = configObj[`${pkey}clockdata`];

      _.each(Object.keys(data), key => {
        ObjData[key] = data[key];
      });

      ObjData.refclk = req.response.data.refclk;
      const clkname = pkey;
      const refclockObj = req.response.data.refclk;

      for (let i = 0; i < ObjData.refObj.length; i++) {
        const datakey = Object.keys(ObjData.refObj[i]);
        let self = ObjData.refObj[i][datakey[0]];
        const PLL = ObjData[datakey[0].toUpperCase()];
        const ref_clk = ObjData.refclk[datakey[0].toUpperCase()].selected_pll;
        let pllupdated = configObj[datakey[0]]['plldata'];

        if (PLL) {
          pllupdated = clockTreeData(PLL, ref_clk, refclockObj, clkname);
          const items = pllupdated.items;
          self.plldata.freq = pllupdated.freq;
          self.plldata.items = items;
          self.$.freqValue.innerHTML = self.plldata.freq;

          if (items) {
            let clkname = [];
            let actualfreq = [];
            let divby = [];
            Object.entries(items).forEach(entry => {
              const [key, values] = entry;
              clkname.push(_.pluck(values, 'Clock_Name'));
              actualfreq.push(_.pluck(values, 'Actual_Frequency'));
              divby.push(key);
            });
            self.itemName = clkname;
            self.actFreq = actualfreq;
            self.divisorBy = divby;
          }

          self.refclk = ObjData.refclk[datakey[0].toUpperCase()].refclk;
        }
      }

      let row_data = [];
      Object.entries(data).forEach(entry => {
        const [key, values] = entry;
        values.forEach(value => {
          row_data.push(value);
        });
      });
      ObjData['row_data'] = row_data;

      ObjData.tableref._refreshValues();

      document.querySelector("#pageloader").style.display = "none";
    }, () => {
      if (request.status == 400) redirectToLogin();
    });
  }

  _pllUpdated(plldata) {
    if (plldata) this._pllValue(plldata.items);
  }

  _computedClass(title) {
    let classes = 'treeStructure';
    if (title === 'PPLL') classes += ' d-block';else classes += ' d-inblock';
    return classes;
  }

  _pllValue(items) {
    this.itemName = [];
    this.actFreq = [];
    this.divisorBy = [];

    if (items) {
      Object.entries(items).forEach(entry => {
        const [key, values] = entry;
        this.itemName.push(_.pluck(values, 'Clock_Name'));
        this.actFreq.push(_.pluck(values, 'Actual_Frequency'));
        this.divisorBy.push(key);
      });
      return this.itemName;
    }
  }

  _handleErrorResponse(req) {
    handleErrorResponse(req);
  }

  connectedCallback() {
    super.connectedCallback();
    const key = this.dataset.key;
    let pkey = this.dataset.pkey;
    let data = {};
    data[key] = this;
    configObj['clockname'] = pkey;
    if (pkey === 'ps') this.url = api_url + 'getclockdetails/';else this.url = api_url + 'getmeclockdetails/';
    let ObjData = configObj[`${pkey}clockdata`];
    ObjData['refObj'].push(data);

    if (configObj[key] && configObj[key].plldata) {
      this.plldata = configObj[key].plldata;
      this.refclk = ObjData.refclk[key.toUpperCase()].refclk;
      this.multiplier = ObjData.refclk[key.toUpperCase()].multiplier;
      this.divider = ObjData.refclk[key.toUpperCase()].divider;
    }
  }

}

customElements.define('custom-tree', TreeView);