/*	Copyright (c) 2018 Jean-Marc VIGLINO, 
  released under the CeCILL-B license (French BSD license)
  (http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt).
*/
import ol_Feature from 'ol/Feature.js'
import ol_Overlay_Popup from './Popup.js'
import ol_ext_element from '../util/element.js'


/** Template attributes for popup
 * @typedef {Object} TemplateAttributes
 * @property {string} title
 * @property {function} format a function that takes an attribute and a feature and returns the formated attribute
 * @property {string} before string to instert before the attribute (prefix)
 * @property {string} after string to instert after the attribute (sudfix)
 * @property {boolean|function} visible boolean or a function (feature, value) that decides the visibility of a attribute entry
 */

/** Template 
 * @typedef {Object} Template
 * @property {string|function} title title of the popup, attribute name or a function that takes a feature and returns the title
 * @property {Object.<TemplateAttributes>} attributes a list of template attributes 
 */

/**
 * A popup element to be displayed on a feature.
 *
 * @constructor
 * @extends {ol_Overlay_Popup}
 * @fires show
 * @fires hide
 * @fires select
 * @fires attribute
 * @param {} options Extend Popup options 
 *  @param {String} options.popupClass the a class of the overlay to style the popup.
 *  @param {bool} options.closeBox popup has a close box, default false.
 *  @param {function|undefined} options.onclose: callback function when popup is closed
 *  @param {function|undefined} options.onshow callback function when popup is shown
 *  @param {Number|Array<number>} options.offsetBox an offset box
 *  @param {ol.OverlayPositioning | string | undefined} options.positioning 
 *    the 'auto' positioning var the popup choose its positioning to stay on the map.
 *  @param {Template|function} [options.template] A template with a list of properties to use in the popup or a function that takes a feature and returns a Template, default use all feature properties
 *  @param {ol.interaction.Select} options.select a select interaction to get features from
 *  @param {boolean} options.keepSelection keep original selection, otherwise set selection to the current popup feature and add a counter to change current feature, default false
 *  @param {boolean} options.canFix Enable popup to be fixed, default false
 *  @param {boolean} options.showImage display image url as image, default false
 *  @param {boolean} options.maxChar max char to display in a cell, default 200
 *  @api stable
 */
var ol_Overlay_PopupFeature = class olOverlayPopupFeature extends ol_Overlay_Popup {
  constructor(options) {
    options = options || {};

    super(options);

    this.setTemplate(options.template);
    this.set('canFix', options.canFix);
    this.set('showImage', options.showImage);
    this.set('maxChar', options.maxChar || 200);
    this.set('keepSelection', options.keepSelection);

    // Bind with a select interaction
    if (options.select && (typeof options.select.on === 'function')) {
      this._select = options.select;
      options.select.on('select', function (e) {
        if (!this._noselect) {
          if (e.selected[0]) {
            this.show(e.mapBrowserEvent.coordinate, options.select.getFeatures().getArray(), e.selected[0]);
          } else {
            this.hide();
          }
        }
      }.bind(this));
    }
  }
  /** Set the template
   * @param {Template} [template] A template with a list of properties to use in the popup, default use all features properties
   */
  setTemplate(template) {
    if (!template) {
      template = function (f) {
        var prop = f.getProperties();
        delete prop[f.getGeometryName()];
        return {
          attributes: Object.keys(prop)
        };
      };
    }
    this._template = template;
    this._attributeObject(this._template);
  }
  /**
   * @private
   */
  _attributeObject(temp) {
    if (temp && temp.attributes instanceof Array) {
      var att = {};
      temp.attributes.forEach(function (a) {
        att[a] = true;
      });
      temp.attributes = att;
    }
    return temp.attributes;
  }
  /** Show the popup on the map
   * @param {ol.coordinate|undefined} coordinate Position of the popup
   * @param {ol.Feature|Array<ol.Feature>} features The features on the popup
   * @param {ol.Feature} current The current feature if keepSelection = true, otherwise get the first feature
   */
  show(coordinate, features, current) {
    if (coordinate instanceof ol_Feature
      || (coordinate instanceof Array && coordinate[0] instanceof ol_Feature)) {
      features = coordinate;
      coordinate = null;
    }
    if (!(features instanceof Array))
      features = [features];
    this._features = features.slice();
    if (!this._count)
      this._count = 1;

    // Calculate html upon feaures attributes
    this._count = 1;
    var f = this.get('keepSelection') ? current || features[0] : features[0];
    var html = this._getHtml(f);
    if (html) {
      if (!this.element.classList.contains('ol-fixed'))
        this.hide();
      if (!coordinate || features[0].getGeometry().getType() === 'Point') {
        coordinate = features[0].getGeometry().getFirstCoordinate();
      }
      super.show(coordinate, html);
    } else {
      this.hide();
    }
  }
  /**
   * @private
   */
  _getHtml(feature) {
    if (!feature)
      return '';
    var html = ol_ext_element.create('DIV', { className: 'ol-popupfeature' });
    if (this.get('canFix')) {
      ol_ext_element.create('I', { className: 'ol-fix', parent: html })
        .addEventListener('click', function () {
          this.element.classList.toggle('ol-fixed');
        }.bind(this));
    }
    var template = this._template;
    // calculate template
    if (typeof (template) === 'function') {
      template = template(feature, this._count, this._features.length);
    } else if (!template || !template.attributes) {
      template = template || {};
      template.attributes = {};
      for (var i in feature.getProperties())
        if (i != 'geometry') {
          template.attributes[i] = i;
        }
    }
    // Display title
    if (template.title) {
      var title;
      if (typeof template.title === 'function') {
        title = template.title(feature);
      } else {
        title = feature.get(template.title);
      }
      ol_ext_element.create('H1', { html: title, parent: html });
    }
    // Display properties in a table
    if (template.attributes) {
      var tr, table = ol_ext_element.create('TABLE', { parent: html });
      var atts = this._attributeObject(template);
      var featureAtts = feature.getProperties();
      Object.keys(atts).forEach(function(att) {
        if (featureAtts.hasOwnProperty(att)) {
          var a = atts[att];
          var content, val = featureAtts[att];
          // Get calculated value
          if (a && typeof (a.format) === 'function') {
            val = a.format(val, feature);
          }

          // Is entry visible?
          var visible = true;
          if (a && typeof (a.visible) === 'boolean') {
            visible = a.visible;
          } else if (a && typeof (a.visible) === 'function') {
            visible = a.visible(feature, val);
          }

          if (visible) {
            tr = ol_ext_element.create('TR', {
              click: function(e) {
                this.dispatchEvent({ type: 'attribute', feature: feature, attribute: att, originalEvent: e })
              }.bind(this),
              parent: table 
            });
            ol_ext_element.create('TD', { 
              className: 'ol-label',
              html: a ? a.title || att : att, 
              parent: tr 
            });

            // Show image or content
            if (this.get('showImage') && /(http(s?):)([/|.|\w|\s|-])*\.(?:jpg|gif|png)/.test(val)) {
              content = ol_ext_element.create('IMG', {
                src: val
              });
            } else {
              if (a) {
                content = (a.before || '') + val + (a.after || '');
              } else {
                content = '';
              }
              var maxc = this.get('maxChar') || 200;
              if (typeof (content) === 'string' && content.length > maxc) {
                content = content.substr(0, maxc) + '[...]';
              }
            }

            // Add value
            ol_ext_element.create('TD', {
              className: 'ol-value',
              html: content,
              parent: tr
            });
          }
        }
      }.bind(this))
    }
    // Zoom button
    ol_ext_element.create('BUTTON', { className: 'ol-zoombt', parent: html })
      .addEventListener('click', function () {
        if (feature.getGeometry().getType() === 'Point') {
          this.getMap().getView().animate({
            center: feature.getGeometry().getFirstCoordinate(),
            zoom: Math.max(this.getMap().getView().getZoom(), 18)
          });
        } else {
          var ext = feature.getGeometry().getExtent();
          this.getMap().getView().fit(ext, { duration: 1000 });
        }
      }.bind(this));

    // Counter
    if (!this.get('keepSelection') && this._features.length > 1) {
      var div = ol_ext_element.create('DIV', { className: 'ol-count', parent: html });
      ol_ext_element.create('DIV', {
        className: 'ol-prev',
        parent: div,
        click: function () {
          this._count--;
          if (this._count < 1)
            this._count = this._features.length;
          html = this._getHtml(this._features[this._count - 1]);
          setTimeout(function () {
            ol_Overlay_Popup.prototype.show.call(this, this.getPosition(), html);
          }.bind(this), 350);
        }.bind(this)
      });
      ol_ext_element.create('TEXT', { html: this._count + '/' + this._features.length, parent: div });
      ol_ext_element.create('DIV', {
        className: 'ol-next',
        parent: div,
        click: function () {
          this._count++;
          if (this._count > this._features.length)
            this._count = 1;
          html = this._getHtml(this._features[this._count - 1]);
          setTimeout(function () {
            ol_Overlay_Popup.prototype.show.call(this, this.getPosition(), html);
          }.bind(this), 350);
        }.bind(this)
      });
    }
    // Use select interaction
    if (this._select && !this.get('keepSelection')) {
      this._noselect = true;
      this._select.getFeatures().clear();
      this._select.getFeatures().push(feature);
      this._noselect = false;
    }
    this.dispatchEvent({ type: 'select', feature: feature, index: this._count });
    return html;
  }
  /** Fix the popup
   * @param {boolean} fix
   */
  setFix(fix) {
    if (fix)
      this.element.classList.add('ol-fixed');
    else
      this.element.classList.remove('ol-fixed');
  }
  /** Is a popup fixed
   * @return {boolean}
   */
  getFix() {
    return this.element.classList.contains('ol-fixed');
  }
}

/** Get a function to use as format to get local string for an attribute
 * if the attribute is a number: Number.toLocaleString()
 * if the attribute is a date: Date.toLocaleString()
 * otherwise the attibute itself
 * @param {string} locales string with a BCP 47 language tag, or an array of such strings
 * @param {*} options Number or Date toLocaleString options
 * @return {function} a function that takes an attribute and return the formated attribute
 */
var ol_Overlay_PopupFeature_localString = function (locales , options) {
  return function (a) {
    if (a && a.toLocaleString) {
      return a.toLocaleString(locales , options);
    } else {
      // Try to get a date from a string
      var date = new Date(a);
      if (isNaN(date)) return a;
      else return date.toLocaleString(locales , options);
    }
  };
};

export {ol_Overlay_PopupFeature_localString}
export default ol_Overlay_PopupFeature
