/*
 * Copyright 2021, GFXFundamentals.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of GFXFundamentals. nor the names of his
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


(function(root, factory) {  // eslint-disable-line
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define([], function() {
      return factory.call(root);
    });
  } else {
    // Browser globals
    root.webglLessonsUI = factory.call(root);
  }
}(this, function() {
  'use strict';
  const gopt = getQueryParams();

  function setupSlider(selector, options) {
    var parent = document.querySelector(selector);
    if (!parent) {
      // like jquery don't fail on a bad selector
      return;
    }
    if (!options.name) {
      options.name = selector.substring(1);
    }
    return createSlider(parent, options); // eslint-disable-line
  }

  function createSlider(parent, options) {
    var precision = options.precision || 0;
    var min = options.min || 0;
    var step = options.step || 1;
    var value = options.value || 0;
    var max = options.max || 1;
    var fn = options.slide;
    var name = gopt["ui-" + options.name] || options.name;
    var uiPrecision = options.uiPrecision === undefined ? precision : options.uiPrecision;
    var uiMult = options.uiMult || 1;

    min /= step;
    max /= step;
    value /= step;

    parent.innerHTML = `
      <div class="gman-widget-outer">
        <div class="gman-widget-label">${name}</div>
        <div class="gman-widget-value"></div>
        <input class="gman-widget-slider" type="range" min="${min}" max="${max}" value="${value}" />
      </div>
    `;
    var valueElem = parent.querySelector(".gman-widget-value");
    var sliderElem = parent.querySelector(".gman-widget-slider");

    function updateValue(value) {
      valueElem.textContent = (value * step * uiMult).toFixed(uiPrecision);
    }

    updateValue(value);

    function handleChange(event) {
      var value = parseInt(event.target.value);
      updateValue(value);
      fn(event, { value: value * step });
    }

    sliderElem.addEventListener('input', handleChange);
    sliderElem.addEventListener('change', handleChange);

    return {
      elem: parent,
      updateValue: (v) => {
        v /= step;
        sliderElem.value = v;
        updateValue(v);
      },
    };
  }

  function makeSlider(options) {
    const div = document.createElement("div");
    return createSlider(div, options);
  }

  var widgetId = 0;
  function getWidgetId() {
    return "__widget_" + widgetId++;
  }

  function makeCheckbox(options) {
    const div = document.createElement("div");
    div.className = "gman-widget-outer";
    const label = document.createElement("label");
    const id = getWidgetId();
    label.setAttribute('for', id);
    label.textContent = gopt["ui-" + options.name] || options.name;
    label.className = "gman-checkbox-label";
    const input = document.createElement("input");
    input.type = "checkbox";
    input.checked = options.value;
    input.id = id;
    input.className = "gman-widget-checkbox";
    div.appendChild(label);
    div.appendChild(input);
    input.addEventListener('change', function(e) {
       options.change(e, {
         value: e.target.checked,
       });
    });

    return {
      elem: div,
      updateValue: function(v) {
        input.checked = !!v;
      },
    };
  }

  function makeOption(options) {
    const div = document.createElement("div");
    div.className = "gman-widget-outer";
    const label = document.createElement("label");
    const id = getWidgetId();
    label.setAttribute('for', id);
    label.textContent = gopt["ui-" + options.name] || options.name;
    label.className = "gman-widget-label";
    const selectElem = document.createElement("select");
    options.options.forEach((name, ndx) => {
      const opt = document.createElement("option");
      opt.textContent = gopt["ui-" + name] || name;
      opt.value = ndx;
      opt.selected = ndx === options.value;
      selectElem.appendChild(opt);
    });
    selectElem.className = "gman-widget-select";
    div.appendChild(label);
    div.appendChild(selectElem);
    selectElem.addEventListener('change', function(e) {
       options.change(e, {
         value: selectElem.selectedIndex,
       });
    });

    return {
      elem: div,
      updateValue: function(v) {
        selectElem.selectedIndex = v;
      },
    };
  }

  function noop() {
  }

  function genSlider(object, ui) {
    const changeFn = ui.change || noop;
    ui.name = ui.name || ui.key;
    ui.value = object[ui.key];
    ui.slide = ui.slide || function(event, uiInfo) {
      object[ui.key] = uiInfo.value;
      changeFn();
    };
    return makeSlider(ui);
  }

  function genCheckbox(object, ui) {
    const changeFn = ui.change || noop;
    ui.value = object[ui.key];
    ui.name = ui.name || ui.key;
    ui.change = function(event, uiInfo) {
      object[ui.key] = uiInfo.value;
      changeFn();
    };
    return makeCheckbox(ui);
  }

  function genOption(object, ui) {
    const changeFn = ui.change || noop;
    ui.value = object[ui.key];
    ui.name = ui.name || ui.key;
    ui.change = function(event, uiInfo) {
      object[ui.key] = uiInfo.value;
      changeFn();
    };
    return makeOption(ui);
  }

  const uiFuncs = {
    slider: genSlider,
    checkbox: genCheckbox,
    option: genOption,
  };

  function setupUI(parent, object, uiInfos) {
    const widgets = {};
    uiInfos.forEach(function(ui) {
      const widget = uiFuncs[ui.type](object, ui);
      parent.appendChild(widget.elem);
      widgets[ui.key] = widget;
    });
    return widgets;
  }

  function updateUI(widgets, data) {
    Object.keys(widgets).forEach(key => {
      const widget = widgets[key];
      widget.updateValue(data[key]);
    });
  }

  function getQueryParams() {
    var params = {};
    if (window.hackedParams) {
      Object.keys(window.hackedParams).forEach(function(key) {
        params[key] = window.hackedParams[key];
      });
    }
    if (window.location.search) {
      window.location.search.substring(1).split("&").forEach(function(pair) {
        var keyValue = pair.split("=").map(function(kv) {
          return decodeURIComponent(kv);
        });
        params[keyValue[0]] = keyValue[1];
      });
    }
    return params;
  }

  return {
    setupUI: setupUI,
    updateUI: updateUI,
    setupSlider: setupSlider,
    makeSlider: makeSlider,
    makeCheckbox: makeCheckbox,
  };

}));

