import ol_control_Control from 'ol/control/Control.js'
import ol_ext_element from '../util/element.js'

/** 
 * @classdesc
 * Application dialog
 * @extends {ol_control_Control}
 * @constructor
 * @fires show
 * @fires hide
 * @fires cancel
 * @fires button
 * @param {*} options
 *  @param {string} options.className
 *  @param {ol.Map} options.map the map to place the dialog inside
 *  @param {Element} options.target target to place the dialog
 *  @param {boolean} options.fullscreen view dialog fullscreen (same as options.target = document.body)
 *  @param {boolean} options.zoom add a zoom effect
 *  @param {boolean} options.closeBox add a close button
 *  @param {number} options.max if not null add a progress bar to the dialog, default null
 *  @param {boolean} options.hideOnClick close dialog when click
 *  @param {boolean} options.hideOnBack close dialog when click the background
 *  @param {boolean} options.closeOnSubmit Prevent closing the dialog on submit
 */
var ol_control_Dialog = class olcontrolDialog extends ol_control_Control {
  constructor(options) {
    options = options || {};
    if (options.fullscreen) options.target = document.body;
    var fullscreen = (options.target === document.body);

    var element = ol_ext_element.create(fullscreen ? 'DIALOG' : 'DIV', {
      className: ((options.className || '') + (options.zoom ? ' ol-zoom' : '') + ' ol-ext-dialog').trim()
    })
    super({
      element: element,
      target: options.target
    });

    if (fullscreen) {
      // Handle close (DIALOG)
      element.addEventListener('close', function(){
        this.hide();
      }.bind(this));
      // Prevent cancel (DIALOG on escape)
      document.addEventListener('keydown', function(e) {
        if (e.key !== 'Escape') return;
        if (!this.get('closeBox') && this.isOpen()) {
          e.preventDefault()
        }
      }.bind(this))
      // Cancel event
      element.addEventListener('cancel', function() {
        setTimeout(function() { this.dispatchEvent('cancel'); }.bind(this))
      }.bind(this));
    }

    // Constructor
    element.addEventListener('click', function (e) {
      if (this.get('hideOnBack') && e.target === element) this.close();
      if (this.get('hideOnClick')) this.close();
    }.bind(this));
    // form
    var form = ol_ext_element.create('FORM', {
      on: {
        submit: this._onButton('submit')
      },
      parent: element
    });
    // Title
    ol_ext_element.create('H2', {
      parent: form
    });
    // Close box
    ol_ext_element.create('DIV', {
      className: 'ol-closebox',
      click: this._onButton('cancel'),
      parent: form
    });
    // Content
    ol_ext_element.create('DIV', {
      className: 'ol-content',
      parent: form
    });
    // Progress
    this._progress = ol_ext_element.create('DIV', {
      style: { display: 'none' },
      parent: form
    });
    var bar = ol_ext_element.create('DIV', {
      className: 'ol-progress-bar',
      parent: this._progress
    });
    this._progressbar = ol_ext_element.create('DIV', {
      parent: bar
    });
    this._progressMessage = ol_ext_element.create('DIV', {
      className: 'ol-progress-message',
      parent: this._progress
    });
    // Buttons
    ol_ext_element.create('DIV', {
      className: 'ol-buttons',
      parent: form
    });

    this.set('closeBox', options.closeBox !== false);
    this.set('zoom', !!options.zoom);
    this.set('hideOnClick', !!options.hideOnClick);
    this.set('hideOnBack', !!options.hideOnBack);
    this.set('className', element.className);
    this.set('closeOnSubmit', options.closeOnSubmit);
    this.set('buttons', options.buttons);
    this.setContent(options);
  }
  /** Show a new dialog
   * @param { * | Element | string } options options or a content to show
   *  @param {Element | String} options.content dialog content
   *  @param {string} options.title title of the dialog
   *  @param {string} options.className dialog class name
   *  @param {number} options.autoclose a delay in ms before auto close
   *  @param {boolean} options.hideOnBack close dialog when click the background
   *  @param {number} options.max if not null add a progress bar to the dialog
   *  @param {number} options.progress set the progress bar value
   *  @param {Object} options.buttons a key/value list of button to show
   *  @param {function} [options.onButton] a function that takes the button id and a list of input by className
   */
  show(options) {
    if (options) {
      if (options instanceof Element || typeof (options) === 'string') {
        options = { content: options };
      }
      this.setContent(options);
    }
    if (this.element.showModal) this.element.showModal();
    this.element.classList.add('ol-visible');
    this.element.setAttribute('aria-hidden', false);
    var input = this.element.querySelector('input[type="text"],input[type="search"],input[type="number"]');
    if (input) {
      setTimeout(function() { input.focus(); })
    }
    this.dispatchEvent({ type: 'show' });
    if (options) {
      // Auto close
      if (options.autoclose) {
        var listener = setTimeout(function () { this.hide(); }.bind(this), options.autoclose);
        this.once('hide', function () {
          clearTimeout(listener);
        });
      }
      // hideOnBack
      if (options.hideOnBack) {
        // save value
        var value = this.get('hideOnBack');
        this.set('hideOnBack', true);
        this.once('hide', function () {
          this.set('hideOnBack', value);
        }.bind(this));
      }
    }
  }
  /** Open the dialog
   */
  open() {
    this.show();
  }
  /** Set the dialog content
   * @param {Element | String} content dialog content
   */
  setContentMessage(content) {
    if (content !== undefined) {
      var elt = this.getContentElement();
      if (content instanceof Element)
        ol_ext_element.setHTML(elt, '');
      ol_ext_element.setHTML(elt, content || '');
    }
  }
  /** Set the dialog title
   * @param {Element | String} content dialog content
   */
  setTitle(title) {
    var form = this.element.querySelector('form');
    form.querySelector('h2').innerText = title || '';
    if (title) {
      form.classList.add('ol-title');
    } else {
      form.classList.remove('ol-title');
    }
  }
  /** Set the dialog content
   * @param {*} options
   *  @param {Element | String} options.content dialog content
   *  @param {string} options.title title of the dialog
   *  @param {string} options.className dialog class name
   *  @param {number} options.max if not null add a progress bar to the dialog
   *  @param {number} options.progress set the progress bar value
   *  @param {Object} options.buttons a key/value list of button to show
   *  @param {function} [options.onButton] a function that takes the button id and a list of input by className
   */
  setContent(options) {
    if (!options)
      return;
    this.element.className = this.get('className');
    if (typeof (options) === 'string')
      options = { content: options };
    options = options || {};
    this.setProgress(false);
    if (options.max)
      this.setProgress(0, options.max);
    if (options.progress !== undefined)
      this.setProgress(options.progress);
    //this.element.className = 'ol-ext-dialog' + (this.get('zoom') ? ' ol-zoom' : '');
    if (this.get('zoom'))
      this.element.classList.add('ol-zoom');
    else
      this.element.classList.remove('ol-zoom');
    if (options.className) {
      options.className.split(' ').forEach(function (c) {
        this.element.classList.add(c);
      }.bind(this));
    }
    var form = this.element.querySelector('form');
    // Content
    if (options.content !== undefined) {
      if (options.content instanceof Element)
        ol_ext_element.setHTML(form.querySelector('.ol-content'), '');
      ol_ext_element.setHTML(form.querySelector('.ol-content'), options.content || '');
    }
    // Title
    this.setTitle(options.title);
    // Closebox
    if (options.closeBox || (this.get('closeBox') && options.closeBox !== false)) {
      form.classList.add('ol-closebox');
    } else {
      form.classList.remove('ol-closebox');
    }
    // Buttons
    var buttons = this.element.querySelector('.ol-buttons');
    buttons.innerHTML = '';
    var btn = options.buttons || this.get('buttons');
    if (btn) {
      form.classList.add('ol-button');
      for (var i in btn) {
        ol_ext_element.create('INPUT', {
          type: (i === 'submit' ? 'submit' : 'button'),
          value: btn[i],
          click: this._onButton(i, options.onButton),
          parent: buttons
        });
      }
    } else {
      form.classList.remove('ol-button');
    }
  }
  /** Get dialog content element
   * @returns {Element}
   */
  getContentElement() {
    return this.element.querySelector('form .ol-content');
  }
  /** Set progress
   * @param {number|boolean} val the progress value or false to hide the progressBar
   * @param {number} max
   * @param {string|element} message
   */
  setProgress(val, max, message) {
    if (val === false) {
      ol_ext_element.setStyle(this._progress, { display: 'none' });
      return;
    }
    if (max > 0) {
      this.set('max', Number(max));
    } else {
      max = this.get('max');
    }
    if (!max) {
      ol_ext_element.setStyle(this._progress, { display: 'none' });
    } else {
      var p = Math.round(val / max * 100);
      ol_ext_element.setStyle(this._progress, { display: '' });
      this._progressbar.className = p ? '' : 'notransition';
      ol_ext_element.setStyle(this._progressbar, { width: p + '%' });
    }
    this._progressMessage.innerHTML = '';
    ol_ext_element.setHTML(this._progressMessage, message || '');
  }
  /** Returns a function to do something on button click
   * @param {strnig} button button id
   * @param {function} callback
   * @returns {function}
   * @private
   */
  _onButton(button, callback) {
    // Dispatch a button event
    var fn = function (e) {
      e.preventDefault();
      if (button !== 'submit' || this.get('closeOnSubmit') !== false) this.hide();
      var inputs = this.getInputs();
      setTimeout(function() {
        this.dispatchEvent({ type: 'button', button: button, inputs: inputs });
        if (typeof (callback) === 'function') callback(button, inputs);
      }.bind(this))
    }.bind(this);
    return fn;
  }
  /** Get inputs, textarea an select of the dialog by classname
   * @return {Object} a {key:value} list of Elements by classname
   */
  getInputs() {
    var inputs = {};
    ['input', 'textarea', 'select'].forEach(function (type) {
      this.element.querySelectorAll('form ' + type).forEach(function (input) {
        if (input.className) {
          input.className.split(' ').forEach(function (n) {
            inputs[n] = input;
          });
        }
      });
    }.bind(this));
    return inputs;
  }
  /** Close the dialog
   */
  hide() {
    // Remove focus on dialog
    if (document.activeElement && document.activeElement !== document.body) {
      document.activeElement.blur();
    }
    // DIALOG element
    if (this.element.close) {
      this.element.close();
    }
    if (this.isOpen()) {
      this.element.classList.remove('ol-visible');
      this.element.setAttribute('aria-hidden', true)
      // Dispatch event when close
      setTimeout(function() {
        this.dispatchEvent({ type: 'hide' });
      }.bind(this))
    }
  }
  /** Close the dialog 
   */
  close() {
    this.hide();
  }
  /** The dialog is shown
   * @return {bool} true if a dialog is open
   */
  isOpen() {
    return (this.element.classList.contains('ol-visible'));
  }
}

export default ol_control_Dialog
