import { EMPTY_STRING } from '../constants/constants.js';
// import { ____JsHtml } from '../html/JsHtml/JsHtml.js';
import { MelHtml } from '../html/JsHtml/MelHtml.js';
import { BnumEvent } from '../mel_events.js';
import { Point, Rectangle } from '../mel_maths.js';
import { Color, ColorFromVariable } from './color.js';
import { MelEnumerable } from './enum.js';

export { Toolbar };

class ToolbarIcon {
  constructor(icon) {
    this._icon = icon;
  }

  generate() {
    return MelHtml.start.span({ class: this._icon }).end();
  }

  static Create(icon) {
    return new ToolbarIcon(icon);
  }
}

class ToolbarMaterialIcon extends ToolbarIcon {
  constructor(icon) {
    super(icon);
  }

  generate() {
    return MelHtml.start.icon(this._icon).end();
  }

  static Create(icon) {
    return new ToolbarMaterialIcon(icon);
  }
}

/**
 * @class
 * @classdesc Element d'une toolbar
 * @todo Finir la doc
 */
class ToolbarItem {
  constructor(id) {
    this.id = id;
    this._parent = null;
    this._icon = null;
    this._text = EMPTY_STRING;
    this.$item = null;
    this.actions = new BnumEvent();
    this.ongenerate = new BnumEvent();
    this.attribs = {};

    Object.defineProperty(this, '$item', {
      get: () => {
        return this._get_item_jquery();
      },
    });
  }

  _get_item_jquery() {
    return this._parent.toolbar().find(`#toolbar-button-${this.id}`);
  }

  set_parent(toolbar) {
    this._parent = toolbar;
    return this;
  }

  set_text(text) {
    this._text = text;
    return this;
  }

  set_icon(icon) {
    this._icon = icon;
    return this;
  }

  add_attribs(key, value) {
    this.attribs[key] = value;
    return this;
  }

  add_action(callback) {
    this.actions.push(callback);
    return this;
  }

  close_modification() {
    return this._parent;
  }

  clone() {
    let clone = new ToolbarItem().set_icon(this._icon).set_text(this._text);
    clone.actions = this.actions;

    return clone;
  }

  generate(additionnal_attribs = {}) {
    for (const key in this.attribs) {
      if (Object.prototype.hasOwnProperty.call(this.attribs, key)) {
        additionnal_attribs[key] = this.attribs[key];
      }
    }

    return ToolbarItem.Generate(this, additionnal_attribs);
  }

  /**
   * @static
   * @param {ToolbarItem} item
   * @param {Object<string, string | number | boolean>} additionnal_attribs
   * @returns {____JsHtml}
   */
  static Generate(item, additionnal_attribs = {}) {
    //prettier-ignore
    let button = MelHtml.start
      .button({ class: 'toolbar-button', id: `toolbar-button-${item.id}` }).attr('onmouseenter', item._on_enter.bind(item)).attr('onmouseleave', item._on_leave.bind(item)).attr('onclick', item.actions.call.bind(item.actions))
        .add_child(item._icon.generate())
        .span({ class:'toolbar-text' }).css('display', (item._text ? EMPTY_STRING : 'none'))
          .text(item._text)
        .end()
      .end();

    for (const key in additionnal_attribs) {
      if (Object.prototype.hasOwnProperty.call(additionnal_attribs, key)) {
        const element = additionnal_attribs[key];

        switch (key) {
          case 'class':
            for (const c of element.split(' ')) {
              button.first().addClass(c);
            }
            break;

          case 'style':
            for (const s of element.split(';')) {
              const [k, v] = s.split('=');
              button.first().css(k, v);
            }
            break;

          default:
            button.first().attr(key, element);
            break;
        }
      }
    }

    button = item.ongenerate.call(button, item) || button;

    return button;
  }

  _on_enter(e) {
    // e = $(e.currentTarget);
    // this._style = e.css('color');
    // e.css('color', EMPTY_STRING);
  }

  _on_leave(e) {
    // $(e.currentTarget).css('color', this._style);
    // this._style = null;
  }
}

class MultipleButtonToolbarItem extends ToolbarItem {
  constructor(id) {
    super(id);
    /**
     * @type {Array<ToolbarItem>}
     */
    this._others_buttons = [];
  }

  add_button(button) {
    const id = this._others_buttons.length;
    button.id = `${this.id}-${id}`;
    this._others_buttons.push(button);

    return this;
  }

  remove_button(id) {
    const index = this._others_buttons.findIndex((x) => x.id === id);

    this._others_buttons[index].attribs['removed'] = true;
    return this;
  }

  generate(main_button_additionnal_attribs = {}) {
    let base = super.generate(main_button_additionnal_attribs);

    if (this.attribs['data-solo']) return base;

    //prettier-ignore
    let entity = MelHtml.start.btn_group({ class: 'toolbar-button-group' })
                          .add_child(base)
                          .each((self, item) => {
                            return item.attribs.removed ? self : self.add_child(ToolbarItem.Generate(item, item.attribs));
                          }, ...this._others_buttons)
                        .end();

    base = null;

    return entity;
  }

  get(index) {
    return this._others_buttons[index];
  }

  *[Symbol.iterator]() {
    yield this;
    yield* this._others_buttons;
  }
}

class Toolbar {
  constructor(id) {
    let _shape = new Rectangle(Point.Zero(), '100%', '60px');
    let _color = new Color(0, 0, 0);

    this._icon_type = ToolbarIcon;
    this._$toolbar = null;
    this._items = [];
    this.x = null;
    this.y = null;
    this.width = null;
    this.heigth = null;
    this.color = EMPTY_STRING;
    this.id = null;

    Object.defineProperties(this, {
      id: {
        value: id,
        writable: false,
        enumerable: true,
        configurable: false,
      },
      x: {
        get: () => {
          return _shape.x;
        },
        set: (value) => {
          _shape.x = value;
          this._update_toolbar();
        },
      },
      y: {
        get: () => {
          return _shape.y;
        },
        set: (value) => {
          _shape.y = value;
          this._update_toolbar();
        },
      },
      width: {
        get: () => {
          return _shape.width;
        },
        set: (value) => {
          _shape.width = value;
          this._update_toolbar();
        },
      },
      heigth: {
        get: () => {
          return _shape.heigth;
        },
        set: (value) => {
          _shape.heigth = value;
          this._update_toolbar();
        },
      },
      color: {
        get: () => {
          return _color.toString();
        },
        set: (value) => {
          if (typeof value === 'string') _color = Color.fromHexa(value);
          else _color = value;
          this._update_toolbar();
        },
      },
    });
  }

  _update_toolbar() {
    if (this._$toolbar) {
      this._$toolbar.css({
        //'background-color': this.color,
        height: this.heigth,
        width: this.width,
        left: this.x,
        bottom: this.y,
      });
    }
  }

  set_icon_type(icon_type) {
    this._icon_type = icon_type;
    return this;
  }

  add_item(id, text, icon) {
    let item = new ToolbarItem(id)
      .set_parent(this)
      .set_text(text)
      .set_icon(this._icon_type.Create(icon));
    this._items.push(item);

    item = null;
    return this._items[this._items.length - 1];
  }

  add_mulitple_button_existing_item(id, array) {
    let item = new MultipleButtonToolbarItem(id);

    const first = array[0];
    item
      .set_parent(this)
      .set_text(first.text)
      .set_icon(this._icon_type.Create(first.icon));

    item = this._set_attribs_on_existing_item(first, item);

    let button;
    for (let index = 1; index < array.length; ++index) {
      const element = array[index];

      button = new ToolbarItem(id)
        .set_parent(this)
        .set_text(element.text)
        .set_icon(this._icon_type.Create(element.icon));

      item.add_button(this._set_attribs_on_existing_item(element, button));
    }

    this._items.push(item);
    item = null;

    return this._items[this._items.length - 1];
  }

  /**
   *
   * @param {*} existing_item
   * @returns {ToolbarItem}
   */
  add_existing_item(id, existing_item) {
    let item = this.add_item(id, existing_item.text, existing_item.icon);

    if (existing_item.action) item.add_action(existing_item.action);

    return this._set_attribs_on_existing_item(existing_item, item);
  }

  _set_attribs_on_existing_item(existing_item, item) {
    if (existing_item.data) {
      for (const key in existing_item.data) {
        if (Object.prototype.hasOwnProperty.call(existing_item.data, key)) {
          const element = existing_item.data[key];
          item.add_attribs(`data-${key}`, element);
        }
      }
    }

    return item;
  }

  remove_item(id) {
    this._items = this._items.filter((x) => x.id !== id);

    return this;
  }

  generate($parent, $additionnal_attribs = {}, context = window) {
    //prettier-ignore
    let nav = MelHtml.start
      .nav({ id: this.id, class: 'mel-nav-toolbar' })
        .ul({ unstyled: true })
          .each((self, item) => {
            return self.li().add_child(item.generate()).end();
          }, ...this._items)
        .end()
      .end();

    if (Object.keys($additionnal_attribs).length) {
      for (const key in $additionnal_attribs) {
        if (Object.prototype.hasOwnProperty.call($additionnal_attribs, key)) {
          const attrib_value = $additionnal_attribs[key];

          switch (key) {
            case 'class':
              for (const attrib_class of attrib_value.split(' ')) {
                nav.first().addClass(attrib_class);
              }
              break;

            case 'id':
              break;

            default:
              nav.first().attr(key, attrib_value);
              break;
          }
        }
      }
    }

    this._$toolbar = nav.generate({ context }).appendTo($parent);

    this._update_toolbar();

    // const raw_color_a = Color.fromRGB(
    //   this._$toolbar.css('background-color'),
    // ).toHexa();
    // const raw_color_b = Color.fromRGB(
    //   this._$toolbar.find('button').first().css('color'),
    // ).toHexa();
    // const colorA = mel_metapage.Functions.colors.kMel_extractRGB(raw_color_a);
    // const colorB = mel_metapage.Functions.colors.kMel_extractRGB(raw_color_b);

    // const isAAA = mel_metapage.Functions.colors.kMel_LuminanceRatioAAA(
    //   colorA,
    //   colorB,
    // );

    // if (!isAAA) this._$toolbar.find('button').css('color', 'black');

    return this.toolbar();
  }

  toolbar() {
    return this._$toolbar;
  }

  /**
   *
   * @param {string} id
   * @returns {ToolbarItem}
   */
  get_button(id) {
    return MelEnumerable.from(this._items)
      .where((x) => x.id === id)
      .first();
  }

  destroy() {
    return this.toolbar().hide().remove();
  }

  /**
   * @yields {ToolbarItem}
   */
  *[Symbol.iterator]() {
    yield* this._items;
  }

  static Item({
    icon = EMPTY_STRING,
    text = EMPTY_STRING,
    action = null,
    on_generate = null,
  }) {
    return { icon, text, action, on_generate };
  }

  static MultipleItem(items) {
    return items;
  }

  static New(id) {
    return new Toolbar(id);
  }

  /**
   *
   * @param {string} config
   * @param {typeof Toolbar} ToolbarType
   * @returns
   */
  static FromConfig(config, ToolbarType = Toolbar) {
    const shape = this._ParseShape(config.toolbar.shape);
    let toolbar = new ToolbarType(config.toolbar.id).set_icon_type(
      config.toolbar.icon_type === 'material-symbol'
        ? ToolbarMaterialIcon
        : ToolbarIcon,
    );

    toolbar.x = shape.x;
    toolbar.y = shape.y;
    toolbar.width = shape.width;
    toolbar.heigth = shape.height;
    toolbar.color = config.toolbar.color
      ? config.toolbar.color.includes('var:')
        ? new ColorFromVariable(
            config.toolbar.color.replaceAll('var:', EMPTY_STRING),
          )
        : config.toolbar.color
      : new Color();

    for (const key in config.toolbar.buttons) {
      if (Object.prototype.hasOwnProperty.call(config.toolbar.buttons, key)) {
        const button_data = config.toolbar.buttons[key];

        if (Array.isArray(button_data))
          toolbar.add_mulitple_button_existing_item(key, button_data);
        else toolbar.add_existing_item(key, button_data);
      }
    }

    return toolbar;
  }

  static _ParseShape(shape) {
    if ((shape || false) && shape !== EMPTY_STRING) {
      let rectangle = new Rectangle(Point.Zero, '100%', '60px');
      shape = shape.split(':')[1];

      let prop;
      for (const element of shape.split(',')) {
        switch (element.replaceAll(' ', EMPTY_STRING)[0]) {
          case 'x':
          case 'y':
            prop = element[0];
            break;

          case 'w':
            prop = 'width';
            break;

          case 'h':
            prop = 'height';
            break;

          default:
            continue;
        }

        this._SetShapeValue(
          rectangle,
          prop,
          element.replace(' ', EMPTY_STRING).slice(1),
        );
      }

      shape = null;
      prop = null;

      return rectangle;
    } else return new Rectangle(Point.Zero, '100%', '60px');
  }

  static _SetShapeValue(shape, prop, val) {
    shape[prop] = val;
  }
}

Toolbar.IconsType = {
  basic: ToolbarIcon,
  material: ToolbarMaterialIcon,
};