/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const {Cu} = require("chrome");

const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm");
const EventEmitter = require("devtools/toolkit/event-emitter");
const {Services} = Cu.import("resource://gre/modules/Services.jsm");
const Strings = Services.strings.createBundle("chrome://browser/locale/devtools/webide.properties");

let ConfigView;

module.exports = ConfigView = function(window) {
  EventEmitter.decorate(this);
  this._doc = window.document;
  this._keys = [];
  return this;
};

ConfigView.prototype = {
  _renderByType: function(input, name, value, customType) {
    value = customType || typeof value;

    switch (value) {
      case "boolean":
        input.setAttribute("data-type", "boolean");
        input.setAttribute("type", "checkbox");
        break;
      case "number":
        input.setAttribute("data-type", "number");
        input.setAttribute("type", "number");
        break;
      case "object":
        input.setAttribute("data-type", "object");
        input.setAttribute("type", "text");
        break;
      default:
        input.setAttribute("data-type", "string");
        input.setAttribute("type", "text");
        break;
    }
    return input;
  },

  set front(front) {
    this._front = front;
  },

  set keys(keys) {
    this._keys = keys;
  },

  get keys() {
    return this._keys;
  },

  set kind(kind) {
    this._kind = kind;
  },

  set includeTypeName(include) {
    this._includeTypeName = include;
  },

  search: function(event) {
    if (event.target.value.length) {
      let stringMatch = new RegExp(event.target.value, "i");

      for (let i = 0; i < this._keys.length; i++) {
        let key = this._keys[i];
        let row = this._doc.getElementById("row-" + key);
        if (key.match(stringMatch)) {
          row.classList.remove("hide");
        } else if (row) {
          row.classList.add("hide");
        }
      }
    } else {
      var trs = this._doc.getElementById("device-fields").querySelectorAll("tr");

      for (let i = 0; i < trs.length; i++) {
        trs[i].classList.remove("hide");
      }
    }
  },

  generateDisplay: function(json) {
    let deviceItems = Object.keys(json);
    deviceItems.sort();
    this.keys = deviceItems;
    for (let i = 0; i < this.keys.length; i++) {
      let key = this.keys[i];
      this.generateField(key, json[key].value, json[key].hasUserValue);
    }
  },

  generateField: function(name, value, hasUserValue, customType, newRow) {
    let table = this._doc.querySelector("table");
    let sResetDefault = Strings.GetStringFromName("device_reset_default");

    if (this._keys.indexOf(name) === -1) {
      this._keys.push(name);
    }

    let input = this._doc.createElement("input");
    let tr = this._doc.createElement("tr");
    tr.setAttribute("id", "row-" + name);
    tr.classList.add("edit-row");
    let td = this._doc.createElement("td");
    td.classList.add("field-name");
    td.textContent = name;
    tr.appendChild(td);
    td = this._doc.createElement("td");
    input.classList.add("editable");
    input.setAttribute("id", name);
    input = this._renderByType(input, name, value, customType);

    if (customType === "boolean" || input.type === "checkbox") {
      input.checked = value;
    } else {
      if (typeof value === "object") {
        value = JSON.stringify(value);
      }
      input.value = value;
    }

    if (!(this._includeTypeName || isNaN(parseInt(value, 10)))) {
      input.type = "number";
    }

    td.appendChild(input);
    tr.appendChild(td);
    td = this._doc.createElement("td");
    td.setAttribute("id", "td-" + name);

    let button = this._doc.createElement("button");
    button.setAttribute("data-id", name);
    button.setAttribute("id", "btn-" + name);
    button.classList.add("reset");
    button.textContent = sResetDefault;
    td.appendChild(button);

    if (!hasUserValue) {
      button.classList.add("hide");
    }

    tr.appendChild(td);

    // If this is a new field, add it to the top of the table.
    if (newRow) {
      let existing = table.querySelector("#" + name);

      if (!existing) {
        table.insertBefore(tr, newRow);
      } else {
        existing.value = value;
      }
    } else {
      table.appendChild(tr);
    }
  },

  resetTable: function() {
    let table = this._doc.querySelector("table");
    let trs = table.querySelectorAll("tr:not(#add-custom-field)");

    for (var i = 0; i < trs.length; i++) {
      table.removeChild(trs[i]);
    }

    return table;
  },

  _getCallType: function(type, name) {
    let frontName = "get";

    if (this._includeTypeName) {
      frontName += type;
    }

    return this._front[frontName + this._kind](name);
  },

  _setCallType: function(type, name, value) {
    let frontName = "set";

    if (this._includeTypeName) {
      frontName += type;
    }

    return this._front[frontName + this._kind](name, value);
  },

  _saveByType: function(options) {
    let fieldName = options.id;
    let inputType = options.type;
    let value = options.value;
    let input = this._doc.getElementById(fieldName);

    switch(inputType) {
      case "boolean":
        this._setCallType("Bool", fieldName, input.checked);
        break;
      case "number":
        this._setCallType("Int", fieldName, value);
        break;
      case "object":
        try {
          value = JSON.parse(value);
        } catch(e) {}
        this._setCallType("Object", fieldName, value);
        break;
      default:
        this._setCallType("Char", fieldName, value);
        break;
    }
  },

  updateField: function(event) {
    if (event.target) {
      let inputType = event.target.getAttribute("data-type");
      let inputValue = event.target.checked || event.target.value;

      if (event.target.nodeName == "input" &&
          event.target.validity.valid &&
          event.target.classList.contains("editable")) {
        let id = event.target.id;
        if (inputType === "boolean") {
          if (event.target.checked) {
            inputValue = true;
          } else {
            inputValue = false;
          }
        }

        this._saveByType({
          id: id,
          type: inputType,
          value: inputValue
        });
        this._doc.getElementById("btn-" + id).classList.remove("hide");
      }
    }
  },

  _resetToDefault: function(name, input, button) {
    this._front["clearUser" + this._kind](name);
    let dataType = input.getAttribute("data-type");
    let tr = this._doc.getElementById("row-" + name);

    switch (dataType) {
      case "boolean":
        this._defaultField = this._getCallType("Bool", name);
        this._defaultField.then(boolean => {
          input.checked = boolean;
        }, () => {
          input.checked = false;
          tr.parentNode.removeChild(tr);
        });
        break;
      case "number":
        this._defaultField = this._getCallType("Int", name);
        this._defaultField.then(number => {
          input.value = number;
        }, () => {
          tr.parentNode.removeChild(tr);
        });
        break;
      case "object":
        this._defaultField = this._getCallType("Object", name);
        this._defaultField.then(object => {
          input.value = JSON.stringify(object);
        }, () => {
          tr.parentNode.removeChild(tr);
        });
        break;
      default:
        this._defaultField = this._getCallType("Char", name);
        this._defaultField.then(string => {
          input.value = string;
        }, () => {
          tr.parentNode.removeChild(tr);
        });
        break;
    }

    button.classList.add("hide");
  },

  checkReset: function(event) {
    if (event.target.classList.contains("reset")) {
      let btnId = event.target.getAttribute("data-id");
      let input = this._doc.getElementById(btnId);
      this._resetToDefault(btnId, input, event.target);
    }
  },

  updateFieldType: function() {
    let table = this._doc.querySelector("table");
    let customValueType = table.querySelector("#custom-value-type").value;
    let customTextEl = table.querySelector("#custom-value-text");
    let customText = customTextEl.value;

    if (customValueType.length === 0) {
      return false;
    }

    switch (customValueType) {
      case "boolean":
        customTextEl.type = "checkbox";
        customText = customTextEl.checked;
        break;
      case "number":
        customText = parseInt(customText, 10) || 0;
        customTextEl.type = "number";
        break;
      default:
        customTextEl.type = "text";
        break;
    }

    return customValueType;
  },

  clearNewFields: function() {
    let table = this._doc.querySelector("table");
    let customTextEl = table.querySelector("#custom-value-text");
    if (customTextEl.checked) {
      customTextEl.checked = false;
    } else {
      customTextEl.value = "";
    }

    this.updateFieldType();
  },

  updateNewField: function() {
    let table = this._doc.querySelector("table");
    let customValueType = this.updateFieldType();

    if (!customValueType) {
      return;
    }

    let customRow = table.querySelector("tr:nth-of-type(2)");
    let customTextEl = table.querySelector("#custom-value-text");
    let customTextNameEl = table.querySelector("#custom-value-name");

    if (customTextEl.validity.valid) {
      let customText = customTextEl.value;

      if (customValueType === "boolean") {
        customText = customTextEl.checked;
      }

      let customTextName = customTextNameEl.value.replace(/[^A-Za-z0-9\.\-_]/gi, "");
      this.generateField(customTextName, customText, true, customValueType, customRow);
      this._saveByType({
        id: customTextName,
        type: customValueType,
        value: customText
      });
      customTextNameEl.value = "";
      this.clearNewFields();
    }
  },

  checkNewFieldSubmit: function(event) {
    if (event.keyCode === 13) {
      this._doc.getElementById("custom-value").click();
    }
  }
};
