import { getTokenConfig, setTokenConfig } from '../scripts/utils.js';

export default class TokenCustomConfig extends TokenConfig {
  constructor(object, options, imgSrc, imgName, callback, config) {
    let token;
    if (object instanceof Actor) {
      token = new TokenDocument(object.token, {
        actor: object,
      });
    } else {
      token = new TokenDocument(object.document, {
        actor: game.actors.get(object.actorId),
      });
    }
    super(token, options);
    this.imgSrc = imgSrc;
    this.imgName = imgName;
    this.callback = callback;
    this.config = config;
    if (this.config) {
      this.flags = this.config.flags;
      this.tv_script = this.config.tv_script;
    }
  }

  _getSubmitData(updateData = {}) {
    if (!this.form) throw new Error('The FormApplication subclass has no registered form element');
    const fd = new FormDataExtended(this.form, { editors: this.editors });
    let data = fd.object;
    if (updateData) data = foundry.utils.flattenObject(foundry.utils.mergeObject(data, updateData));

    // Clear detection modes array
    if (!('detectionModes.0.id' in data)) data.detectionModes = [];

    // Treat "None" as null for bar attributes
    data['bar1.attribute'] ||= null;
    data['bar2.attribute'] ||= null;
    return data;
  }

  async _updateObject(event, formData) {
    const filtered = {};

    const form = $(event.target).closest('form');

    form.find('.form-group').each(function (_) {
      const tva_checkbox = $(this).find('.tva-config-checkbox > input');
      if (tva_checkbox.length && tva_checkbox.is(':checked')) {
        $(this)
          .find('[name]')
          .each(function (_) {
            const name = $(this).attr('name');
            filtered[name] = formData[name];
          });
      }
    });

    if (this.tv_script) {
      filtered.tv_script = this.tv_script;
    }

    if (this.config) {
      let config = expandObject(filtered);
      config.flags = config.flags ? mergeObject(this.flags || {}, config.flags) : this.flags;
      if (this.callback) this.callback(config);
    } else {
      const saved = setTokenConfig(this.imgSrc, this.imgName, filtered);
      if (this.callback) this.callback(saved);
    }
  }

  applyCustomConfig() {
    const tokenConfig = flattenObject(this.config || getTokenConfig(this.imgSrc, this.imgName));
    const form = $(this.form);
    for (const key of Object.keys(tokenConfig)) {
      const el = form.find(`[name="${key}"]`);
      if (el.is(':checkbox')) {
        el.prop('checked', tokenConfig[key]);
      } else {
        el.val(tokenConfig[key]);
      }
      el.trigger('change');
    }
  }

  // *************
  // consider moving html injection to:
  // _replaceHTML | _injectHTML

  async activateListeners(html) {
    await super.activateListeners(html);

    // Disable image path controls
    $(html).find('.token-variants-image-select-button').prop('disabled', true);
    $(html).find('.file-picker').prop('disabled', true);
    $(html).find('.image').prop('disabled', true);

    // Remove 'Assign Token' button
    $(html).find('.assign-token').remove();

    // Add checkboxes to control inclusion of specific tabs in the custom config
    const tokenConfig = this.config || getTokenConfig(this.imgSrc, this.imgName);
    this.tv_script = tokenConfig.tv_script;

    $(html).on('change', '.tva-config-checkbox', this._onCheckboxChange);

    const processFormGroup = function (formGroup) {
      // Checkbox is not added for the Image Path group
      if (!$(formGroup).find('[name="img"]').length) {
        let savedField = false;
        if (tokenConfig) {
          const flatConfig = flattenObject(tokenConfig);
          $(formGroup)
            .find('[name]')
            .each(function (_) {
              const name = $(this).attr('name');
              if (name in flatConfig) {
                savedField = true;
              }
            });
        }

        const checkbox = $(
          `<div class="tva-config-checkbox"><input type="checkbox" data-dtype="Boolean" ${
            savedField ? 'checked=""' : ''
          }></div>`
        );
        if ($(formGroup).find('p.hint').length) {
          $(formGroup).find('p.hint').before(checkbox);
        } else {
          $(formGroup).append(checkbox);
        }
        checkbox.find('input').trigger('change');
      }
    };
    // Add checkboxes to each form-group to control highlighting and which fields will are to be saved
    $(html)
      .find('.form-group')
      .each(function (index) {
        processFormGroup(this);
      });

    // Add 'update' and 'remove' config buttons
    $(html).find('.sheet-footer > button').remove();
    $(html)
      .find('.sheet-footer')
      .append('<button type="submit" value="1"><i class="far fa-save"></i> Save Config</button>');
    if (tokenConfig) {
      $(html)
        .find('.sheet-footer')
        .append('<button type="button" class="remove-config"><i class="fas fa-trash"></i> Remove Config</button>');
      html.find('.remove-config').click(this._onRemoveConfig.bind(this));
    }

    // Pre-select image or appearance tab
    $(html).find('.tabs > .item[data-tab="image"] > i').trigger('click');
    $(html).find('.tabs > .item[data-tab="appearance"] > i').trigger('click');

    document.activeElement.blur(); // Hack fix for key UP/DOWN effects not registering after config has been opened

    // TokenConfig might be changed by some modules after activateListeners is processed
    // Look out for these updates and add checkboxes for any newly added form-groups
    const mutate = (mutations) => {
      mutations.forEach((mutation) => {
        mutation.addedNodes.forEach((node) => {
          if (node.nodeName === 'DIV' && node.className === 'form-group') {
            processFormGroup(node);
            this.applyCustomConfig();
          }
        });
      });
    };

    const observer = new MutationObserver(mutate);
    observer.observe(html[0], {
      characterData: false,
      attributes: false,
      childList: true,
      subtree: true,
    });

    // On any field being changed we want to automatically select the form-group to be included in the update
    $(html).on('change', 'input, select', onInputChange);
    $(html).on('click', 'button', onInputChange);

    this.applyCustomConfig();
  }

  async _onCheckboxChange(event) {
    const checkbox = $(event.target);
    checkbox.closest('.form-group').css({
      'outline-color': checkbox.is(':checked') ? 'green' : '#ffcc6e',
      'outline-width': '2px',
      'outline-style': 'dotted',
      'margin-bottom': '5px',
    });
    checkbox.closest('.tva-config-checkbox').css({
      'outline-color': checkbox.is(':checked') ? 'green' : '#ffcc6e',
      'outline-width': '2px',
      'outline-style': 'solid',
    });
  }

  async _onRemoveConfig(event) {
    if (this.config) {
      if (this.callback) this.callback({});
    } else {
      const saved = setTokenConfig(this.imgSrc, this.imgName, null);
      if (this.callback) this.callback(saved);
    }
    this.close();
  }

  get id() {
    return `token-custom-config-${this.object.id}`;
  }

  _getHeaderButtons() {
    const buttons = super._getHeaderButtons();
    return buttons;
  }
}

// Toggle checkbox if input has been detected inside it's form-group
async function onInputChange(event) {
  if (event.target.parentNode.className === 'tva-config-checkbox') return;
  $(event.target).closest('.form-group').find('.tva-config-checkbox input').prop('checked', true);
}
