import React from 'react';
import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent';
import { wrapCtxMenu } from '../utils/UtilityFunctions';
import { canMount } from '../utils/SlotFunctions';
import { Equalizer } from '../components/SvgIcons';
import cn from 'classnames';
const browser = require('detect-browser');

/**
 * Abstract Slot Section
 */
export default class SlotSection extends TranslatedComponent {

  static propTypes = {
    ship: PropTypes.object.isRequired,
    onChange: PropTypes.func.isRequired,
    onCargoChange: PropTypes.func.isRequired,
    onFuelChange: PropTypes.func.isRequired,
    code: PropTypes.string.isRequired,
    togglePwr: PropTypes.func
  };

  /**
   * Constructor
   * @param  {Object} props   React Component properties
   * @param  {Object} context React Component context
   * @param  {string} sectionId   Section DOM Id
   * @param  {string} sectionName Section name
   */
  constructor(props, context, sectionId, sectionName) {
    super(props);
    this.sectionId = sectionId;
    this.sectionName = sectionName;

    this._getSlots = this._getSlots.bind(this);
    this._selectModule = this._selectModule.bind(this);
    this._getSectionMenu = this._getSectionMenu.bind(this);
    this._contextMenu = this._contextMenu.bind(this);
    this._drop = this._drop.bind(this);
    this._dragOverNone = this._dragOverNone.bind(this);
    this._close = this._close.bind(this);
    this.state = {};
  }

  // Must be implemented by subclasses:
  //  _getSlots()
  //  _getSectionMenu()
  //  _contextMenu()

  /**
   * Open a menu
   * @param  {string} menu    Menu name
   * @param  {SyntheticEvent} event Event
   */
  _openMenu(menu, event) {
    event.preventDefault();
    event.stopPropagation();
    if (this.props.currentMenu === menu) {
      menu = null;
    }
    this.context.openMenu(menu);
  }

  /**
   * Mount/Use the specified module in the slot
   * @param  {Object} slot Slot
   * @param  {Object} m    Selected module
   */
  _selectModule(slot, m) {
    this.props.ship.use(slot, m, false);
    this.props.onChange();
    this._close();
  }

  /**
   * Slot Drag Handler
   * @param  {object} originSlot Origin slot model
   * @param  {Event} e           Drag Event
   */
  _drag(originSlot, e) {
    if (!browser || (browser.name !== 'edge' && browser.name !== 'ie')) {
      e.dataTransfer.setData('text/html', e.currentTarget);
    }
    e.dataTransfer.effectAllowed = 'copyMove';
    this.setState({ originSlot, copy: e.getModifierState('Alt') });
    this._close();
  }

  /**
   * Slot Drag Over Handler
   * @param  {object} targetSlot Potential drop target
   * @param  {Event} e           Drag Event
   */
  _dragOverSlot(targetSlot, e) {
    e.preventDefault();
    e.stopPropagation();
    let os = this.state.originSlot;
    if (os) {
      // Show correct icon
      const effect = this.state.copy ? 'copy' : 'move';
      if (!browser || (browser.name !== 'edge' && browser.name !== 'ie')) {
        e.dataTransfer.dropEffect = os != targetSlot && canMount(this.props.ship, targetSlot, os.m.grp, os.m.class) ? effect : 'none';
      }
      this.setState({ targetSlot });
    } else {
      if (!browser || (browser.name !== 'edge' && browser.name !== 'ie')) {
        e.dataTransfer.dropEffect = 'none';
      }
    }
  }

  /**
   * Drag over non-droppable target/element
   * @param  {Event} e   Drag Event
   */
  _dragOverNone(e) {
    e.preventDefault();
    if (!browser || (browser.name !== 'edge' && browser.name !== 'ie')) {
      e.dataTransfer.dropEffect = 'none';
    }
    this.setState({ targetSlot: null });
  }

  /**
   * Slot drop handler. If the target is eligible swap the origin and target modules.
   * If the target slot's current module cannot be mounted in the origin slot then
   * the origin slot will be empty.
   */
  _drop() {
    let { originSlot, targetSlot, copy } = this.state;
    let m = originSlot.m;

    if (targetSlot && originSlot != targetSlot) {
      if (copy) {
        // We want to copy the module in to the target slot
        if (targetSlot && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
          const mCopy = m.clone();
          this.props.ship.use(targetSlot, mCopy, false);
          // Copy power info
          targetSlot.enabled = originSlot.enabled;
          targetSlot.priority = originSlot.priority;
          this.props.onChange();
        }
      } else {
        // Store power info
        const originEnabled = targetSlot.enabled;
        const originPriority = targetSlot.priority;
        const targetEnabled = originSlot.enabled;
        const targetPriority = originSlot.priority;
        // We want to move the module in to the target slot, and swap back any module that was originally in the target slot
        if (targetSlot && m && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
          // Swap modules if possible
          if (targetSlot.m && canMount(this.props.ship, originSlot, targetSlot.m.grp, targetSlot.m.class)) {
            this.props.ship.use(originSlot, targetSlot.m, true);
            this.props.ship.use(targetSlot, m);
            // Swap power
            originSlot.enabled = originEnabled;
            originSlot.priority = originPriority;
            targetSlot.enabled = targetEnabled;
            targetSlot.priority = targetPriority;
          } else { // Otherwise empty the origin slot
            // Store power
            const targetEnabled = originSlot.enabled;
            this.props.ship.use(originSlot, null, true);  // Empty but prevent summary update
            this.props.ship.use(targetSlot, m);
            originSlot.enabled = 0;
            originSlot.priority = 0;
            targetSlot.enabled = targetEnabled;
            targetSlot.priority = targetPriority;
          }
          this.props.onChange();
        }
      }
    }
    this.setState({ originSlot: null, targetSlot: null, copy: null });
  }

  /**
   * Determine drop eligibilty CSS class
   * @param  {Object} slot       Current slot
   * @param  {Object} originSlot Origin slot
   * @param  {Object} targetSlot Target slot
   * @return {string} CSS Class name
   */
  _dropClass(slot, originSlot, targetSlot) {
    if (!originSlot) {
      return null;
    }
    if (slot === originSlot) {
      if (targetSlot && targetSlot.m && !canMount(this.props.ship, originSlot, targetSlot.m.grp, targetSlot.m.class)) {
        return 'dropEmpty'; // Origin slot will be emptied
      }
      return null;
    }
    if (originSlot.m && canMount(this.props.ship, slot, originSlot.m.grp, originSlot.m.class)) { // Eligble drop slot
      if (slot === targetSlot) {
        return 'drop';  // Can drop
      }
      return 'eligible';  // Potential drop slot
    }

    return 'ineligible';  // Cannot be dropped / invalid drop slot
  }

  /**
   * Close current menu
   */
  _close() {
    if (this.props.currentMenu) {
      this.context.closeMenu();
    }
  }

  /**
   * Render the slot section
   * @return {React.Component} Slot section
   */
  render() {
    let translate = this.context.language.translate;
    let sectionMenuOpened = this.props.currentMenu === this.sectionName;
    let open = this._openMenu.bind(this, this.sectionName);
    let ctx = wrapCtxMenu(this._contextMenu);

    return (
      <div id={this.sectionId} className={'group'}  onDragLeave={this._dragOverNone}>
        <div className={cn('section-menu', { selected: sectionMenuOpened })} onClick={open} onContextMenu={ctx}>
          <h1>{translate(this.sectionName)} <Equalizer/></h1>
          {sectionMenuOpened ? this._getSectionMenu(translate, this.props.ship) : null }
        </div>
        {this._getSlots()}
      </div>
    );
  }
}
