import React from 'react';
// import Perf from 'react-addons-perf';
import { Ships } from 'coriolis-data/dist';
import cn from 'classnames';
import Page from './Page';
import Router from '../Router';
import Persist from '../stores/Persist';
import * as Utils from '../utils/UtilityFunctions';
import Ship from '../shipyard/Ship';
import * as _ from 'lodash';
import { toDetailedBuild } from '../shipyard/Serializer';
import { outfitURL } from '../utils/UrlGenerators';
import {
  FloppyDisk,
  Bin,
  Switch,
  Download,
  Reload,
  LinkIcon,
  ShoppingIcon,
  MatIcon,
  OrbisIcon
} from '../components/SvgIcons';
import LZString from 'lz-string';
import ShipSummaryTable from '../components/ShipSummaryTable';
import StandardSlotSection from '../components/StandardSlotSection';
import HardpointSlotSection from '../components/HardpointSlotSection';
import InternalSlotSection from '../components/InternalSlotSection';
import UtilitySlotSection from '../components/UtilitySlotSection';
import Pips from '../components/Pips';
import Boost from '../components/Boost';
import Fuel from '../components/Fuel';
import Cargo from '../components/Cargo';
import ShipPicker from '../components/ShipPicker';
import EngagementRange from '../components/EngagementRange';
import OutfittingSubpages from '../components/OutfittingSubpages';
import ModalExport from '../components/ModalExport';
import ModalPermalink from '../components/ModalPermalink';
import ModalShoppingList from '../components/ModalShoppingList';
import ModalOrbis from '../components/ModalOrbis';

/**
 * Document Title Generator
 * @param  {String} shipName  Ship Name
 * @param  {String} buildName Build Name
 * @return {String}           Document title
 */
function getTitle(shipName, buildName) {
  return buildName ? buildName : shipName;
}

/**
 * The Outfitting Page
 */
export default class OutfittingPage extends Page {

  /**
   * Constructor
   * @param  {Object} props   React Component properties
   * @param  {Object} context React Component context
   */
  constructor(props, context) {
    super(props, context);
    // window.Perf = Perf;
    this.state = this._initState(props, context);
    this._keyDown = this._keyDown.bind(this);
    this._exportBuild = this._exportBuild.bind(this);
    this._pipsUpdated = this._pipsUpdated.bind(this);
    this._boostUpdated = this._boostUpdated.bind(this);
    this._cargoUpdated = this._cargoUpdated.bind(this);
    this._fuelUpdated = this._fuelUpdated.bind(this);
    this._opponentUpdated = this._opponentUpdated.bind(this);
    this._engagementRangeUpdated = this._engagementRangeUpdated.bind(this);
    this._sectionMenuRefs = {};
  }

  /**
   * [Re]Create initial state from context
   * @param  {Object} props    React component properties
   * @param  {context} context React component context
   * @return {Object}          New state object
   */
  _initState(props, context) {
    let params = context.route.params;
    let shipId = params.ship;
    let code = params.code;
    let buildName = params.bn;
    let data = Ships[shipId];   // Retrieve the basic ship properties, slots and defaults
    let savedCode = Persist.getBuild(shipId, buildName);
    if (!data) {
      return { error: { message: 'Ship not found: ' + shipId } };
    }
    let ship = new Ship(shipId, data.properties, data.slots);          // Create a new Ship instance
    if (code) {
      ship.buildFrom(code);  // Populate modules from serialized 'code' URL param
    } else {
      ship.buildWith(data.defaults);  // Populate with default components
    }

    this._getTitle = getTitle.bind(this, data.properties.name);

    // Obtain ship control from code
    const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange } = this._obtainControlFromCode(ship, code);
    return {
      error: null,
      title: this._getTitle(buildName),
      costTab: Persist.getCostTab() || 'costs',
      buildName,
      newBuildName: buildName,
      shipId,
      ship,
      code,
      savedCode,
      sys,
      eng,
      wep,
      boost,
      fuel,
      cargo,
      opponent,
      opponentBuild,
      opponentSys,
      opponentEng,
      opponentWep,
      engagementRange
    };
  }

  /**
   * Handle build name change and update state
   * @param  {SyntheticEvent} event React Event
   */
  _buildNameChange(event) {
    let stateChanges = {
      newBuildName: event.target.value
    };

    if (Persist.hasBuild(this.state.shipId, stateChanges.newBuildName)) {
      stateChanges.savedCode = Persist.getBuild(this.state.shipId, stateChanges.newBuildName);
    } else {
      stateChanges.savedCode = null;
    }

    this.setState(stateChanges);
  }

  /**
   * Update the control part of the route
   */
  _updateRouteOnControlChange() {
    const { ship, shipId, buildName } = this.state;
    const code = this._fullCode(ship);
    this._updateRoute(shipId, buildName, code);
    this.setState({ code });
  }

  /**
   * Provide a full code for this ship, including any additions due to the outfitting page
   * @param   {Object}  ship  the ship
   * @param   {number}  fuel  the fuel carried by the ship (if different from that in state)
   * @param   {number}  cargo the cargo carried by the ship (if different from that in state)
   * @returns {string}        the code for this ship
   */
  _fullCode(ship, fuel, cargo) {
    return `${ship.toString()}.${LZString.compressToBase64(this._controlCode(fuel, cargo))}`;
  }

  /**
   * Obtain the control information from the build code
   * @param   {Object} ship    The ship
   * @param   {string} code    The build code
   * @returns {Object}         The control information
   */
  _obtainControlFromCode(ship, code) {
    // Defaults
    let sys = 2;
    let eng = 2;
    let wep = 2;
    let boost = false;
    let fuel = ship.fuelCapacity;
    let cargo = ship.cargoCapacity;
    let opponent = new Ship('eagle', Ships['eagle'].properties, Ships['eagle'].slots).buildWith(Ships['eagle'].defaults);
    let opponentSys = 2;
    let opponentEng = 2;
    let opponentWep = 2;
    let opponentBuild;
    let engagementRange = 1000;

    // Obtain updates from code, if available
    if (code) {
      const parts = code.split('.');
      if (parts.length >= 5) {
        // We have control information in the code
        const control = LZString.decompressFromBase64(Utils.fromUrlSafe(parts[4])).split('/');
        sys = parseFloat(control[0]);
        eng = parseFloat(control[1]);
        wep = parseFloat(control[2]);
        boost = control[3] == 1 ? true : false;
        fuel = parseFloat(control[4]);
        cargo = parseInt(control[5]);
        if (control[6]) {
          const shipId = control[6];
          opponent = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots);
          if (control[7] && Persist.getBuild(shipId, control[7])) {
            // Ship is a particular build
            const opponentCode = Persist.getBuild(shipId, control[7]);
            opponent.buildFrom(opponentCode);
            opponentBuild = control[7];
            if (opponentBuild) {
              // Obtain opponent's sys/eng/wep pips from their code
              const opponentParts = opponentCode.split('.');
              if (opponentParts.length >= 5) {
                const opponentControl = LZString.decompressFromBase64(Utils.fromUrlSafe(opponentParts[4])).split('/');
                opponentSys = parseFloat(opponentControl[0]);
                opponentEng = parseFloat(opponentControl[1]);
                opponentWep = parseFloat(opponentControl[2]);
              }
            }
          } else {
            // Ship is a stock build
            opponent.buildWith(Ships[shipId].defaults);
          }
        }
        engagementRange = parseInt(control[8]);
      }
    }

    return { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange };
  }

  /**
   * Triggered when pips have been updated
   * @param {number} sys    SYS pips
   * @param {number} eng    ENG pips
   * @param {number} wep    WEP pips
   */
  _pipsUpdated(sys, eng, wep) {
    this.setState({ sys, eng, wep }, () =>  this._updateRouteOnControlChange());
  }

  /**
   * Triggered when boost has been updated
   * @param {boolean} boost true if boosting
   */
  _boostUpdated(boost) {
    this.setState({ boost }, () =>  this._updateRouteOnControlChange());
  }

  /**
   * Triggered when fuel has been updated
   * @param {number} fuel the amount of fuel, in T
   */
  _fuelUpdated(fuel) {
    this.setState({ fuel }, () =>  this._updateRouteOnControlChange());
  }

  /**
   * Triggered when cargo has been updated
   * @param {number} cargo the amount of cargo, in T
   */
  _cargoUpdated(cargo) {
    this.setState({ cargo }, () =>  this._updateRouteOnControlChange());
  }

  /**
   * Triggered when engagement range has been updated
   * @param {number} engagementRange the engagement range, in m
   */
  _engagementRangeUpdated(engagementRange) {
    this.setState({ engagementRange }, () =>  this._updateRouteOnControlChange());
  }

  /**
   * Triggered when target ship has been updated
   * @param {string} opponent       the opponent's ship model
   * @param {string} opponentBuild  the name of the opponent's build
   */
  _opponentUpdated(opponent, opponentBuild) {
    const opponentShip  = new Ship(opponent, Ships[opponent].properties, Ships[opponent].slots);
    let opponentSys = this.state.opponentSys;
    let opponentEng = this.state.opponentEng;
    let opponentWep = this.state.opponentWep;
    if (opponentBuild && Persist.getBuild(opponent, opponentBuild)) {
      // Ship is a particular build
      opponentShip.buildFrom(Persist.getBuild(opponent, opponentBuild));
      // Set pips for opponent
      const opponentParts = Persist.getBuild(opponent, opponentBuild).split('.');
      if (opponentParts.length >= 5) {
        const opponentControl = LZString.decompressFromBase64(Utils.fromUrlSafe(opponentParts[4])).split('/');
        opponentSys = parseFloat(opponentControl[0]);
        opponentEng = parseFloat(opponentControl[1]);
        opponentWep = parseFloat(opponentControl[2]);
      }
    } else {
      // Ship is a stock build
      opponentShip.buildWith(Ships[opponent].defaults);
      opponentSys = 2;
      opponentEng = 2;
      opponentWep = 2;
    }

    this.setState({ opponent: opponentShip, opponentBuild, opponentSys, opponentEng, opponentWep }, () =>  this._updateRouteOnControlChange());
  }

  /**
   * Set the control code for this outfitting page
   * @param   {number}  fuel  the fuel carried by the ship (if different from that in state)
   * @param   {number}  cargo the cargo carried by the ship (if different from that in state)
   * @returns {string}        The control code
   */
  _controlCode(fuel, cargo) {
    const { sys, eng, wep, boost, opponent, opponentBuild, engagementRange } = this.state;
    const code = `${sys}/${eng}/${wep}/${boost ? 1 : 0}/${fuel || this.state.fuel}/${cargo || this.state.cargo}/${opponent.id}/${opponentBuild ? opponentBuild : ''}/${engagementRange}`;
    return code;
  }

  /**
   * Save the current build
   */
  _saveBuild() {
    const { ship, buildName, newBuildName, shipId } = this.state;

    // If this is a stock ship the code won't be set, so ensure that we have it
    const code =  this.state.code || ship.toString();

    Persist.saveBuild(shipId, newBuildName, code);
    this._updateRoute(shipId, newBuildName, code);

    let opponent, opponentBuild, opponentSys, opponentEng, opponentWep;
    if (shipId === this.state.opponent.id && buildName === this.state.opponentBuild) {
      // This is a save of our current opponent build; update it
      opponentBuild = newBuildName;
      opponent  = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots).buildFrom(code);
      opponentSys = this.state.sys;
      opponentEng = this.state.eng;
      opponentWep = this.state.wep;
    } else {
      opponentBuild = this.state.opponentBuild;
      opponent  = this.state.opponent;
      opponentSys = this.state.opponentSys;
      opponentEng = this.state.opponentEng;
      opponentWep = this.state.opponentWep;
    }
    this.setState({ buildName: newBuildName, code, savedCode: code, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, title: this._getTitle(newBuildName) });
  }

  /**
   * Rename the current build
   */
  _renameBuild() {
    const { code, buildName, newBuildName, shipId, ship } = this.state;
    if (buildName != newBuildName && newBuildName.length) {
      Persist.deleteBuild(shipId, buildName);
      Persist.saveBuild(shipId, newBuildName, code);
      this._updateRoute(shipId, newBuildName, code);
      this.setState({ buildName: newBuildName, code, savedCode: code, opponentBuild: newBuildName });
    }
  }

  /**
   * Reload build from last save
   */
  _reloadBuild() {
    this.setState({ code: this.state.savedCode }, () => this._codeUpdated());
  }

  /**
   * Reset build to Stock/Factory defaults
   */
  _resetBuild() {
    const { ship, shipId, buildName } = this.state;
    // Rebuild ship
    ship.buildWith(Ships[shipId].defaults);
    // Reset controls
    const code = ship.toString();
    const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code);
    // Update state, and refresh the ship
    this.setState({
      sys,
      eng,
      wep,
      boost,
      fuel,
      cargo,
      opponent,
      opponentBuild,
      engagementRange
    }, () => this._updateRoute(shipId, buildName, code));
  }

  /**
   * Delete the build
   */
  _deleteBuild() {
    const { shipId, buildName } = this.state;
    Persist.deleteBuild(shipId, buildName);

    let opponentBuild;
    if (shipId === this.state.opponent.id && buildName === this.state.opponentBuild) {
      // Our current opponent has been deleted; revert to stock
      opponentBuild = null;
    } else {
      opponentBuild = this.state.opponentBuild;
    }
    Router.go(outfitURL(this.state.shipId));

    this.setState({ opponentBuild });
  }

  /**
   * Serialized and show the export modal
   */
  _exportBuild() {
    let translate = this.context.language.translate;
    let { buildName, ship } = this.state;
    this.context.showModal(<ModalExport
      title={(buildName || ship.name) + ' ' + translate('export')}
      description={translate('PHRASE_EXPORT_DESC')}
      data={toDetailedBuild(buildName, ship, ship.toString())}
    />);
  }

  /**
   * Called when the code for the ship has been updated, to synchronise the rest of the data
   */
  _codeUpdated() {
    const { code, ship, shipId, buildName } = this.state;

    // Rebuild ship from the code
    this.state.ship.buildFrom(code);

    // Obtain controls from the code
    const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code);
    // Update state, and refresh the route when complete
    this.setState({
      sys,
      eng,
      wep,
      boost,
      fuel,
      cargo,
      opponent,
      opponentBuild,
      engagementRange
    }, () => this._updateRoute(shipId, buildName, code));
  }

  /**
   * Called when the ship has been updated, to set the code and then update accordingly
   */
  _shipUpdated() {
    let { ship, shipId, buildName, cargo, fuel } = this.state;
    if (cargo > ship.cargoCapacity) {
      cargo = ship.cargoCapacity;
    }
    if (fuel > ship.fuelCapacity) {
      fuel = ship.fuelCapacity;
    }
    const code = this._fullCode(ship, fuel, cargo);
    // Only update the state if this really has been updated
    if (this.state.code != code || this.state.cargo != cargo || this.state.fuel != fuel) {
      this.setState({ code, cargo, fuel }, () => this._updateRoute(shipId, buildName, code));
    }
  }

  /**
   * Update the current route based on build
   * @param  {string} shipId    Ship Id
   * @param  {string} buildName Current build name
   * @param  {string} code      Serialized ship 'code'
   */
  _updateRoute(shipId, buildName, code) {
    Router.replace(outfitURL(shipId, code, buildName));
  }

  /**
   * Update state based on context changes
   * @param  {Object} nextProps   Incoming/Next properties
   * @param  {Object} nextContext Incoming/Next conext
   */
  componentWillReceiveProps(nextProps, nextContext) {
    if (this.context.route !== nextContext.route) {  // Only reinit state if the route has changed
      this.setState(this._initState(nextProps, nextContext));
    }
  }

  /**
   * Add listeners when about to mount
   */
  componentWillMount() {
    document.addEventListener('keydown', this._keyDown);
  }

  /**
   * Remove listeners on unmount
   */
  componentWillUnmount() {
    document.removeEventListener('keydown', this._keyDown);
  }

  /**
   * Generates the short URL
   */
  _genShortlink() {
    this.context.showModal(<ModalPermalink url={window.location.href}/>);
  }

  /**
   * Generate Orbis link
   */
  _genOrbis() {
    const data = {};
    const ship = this.state.ship;
    ship.coriolisId = ship.id;
    data.coriolisShip = ship;
    data.url = window.location.href;
    data.title = this.state.buildName || ship.id;
    data.description = this.state.buildName || ship.id;
    data.ShipName = ship.id;
    data.Ship = ship.id;
    console.log(data);
    this.context.showModal(<ModalOrbis ship={data}/>);
  }

  /**
   * Open up a window for EDDB with a shopping list of our components
   */
  _eddbShoppingList() {
    const ship = this.state.ship;

    const shipId = Ships[ship.id].eddbID;
    // Provide unique list of non-PP module EDDB IDs
    const modIds = ship.internal.concat(ship.bulkheads, ship.standard, ship.hardpoints).filter(slot => slot !== null && slot.m !== null && !slot.m.pp).map(slot => slot.m.eddbID).filter((v, i, a) => a.indexOf(v) === i);

    // Open up the relevant URL
    window.open('https://eddb.io/station?s=' + shipId + '&m=' + modIds.join(','));
  }

  /**
   * Generates the shopping list
   */
  _genShoppingList() {
    this.context.showModal(<ModalShoppingList ship={this.state.ship}/>);
  }

  /**
   * Handle Key Down
   * @param  {Event} e  Keyboard Event
   */
  _keyDown(e) {
    // .keyCode will eventually be replaced with .key
    switch (e.keyCode) {
      case 69:     // 'e'
        if (e.ctrlKey || e.metaKey) { // CTRL/CMD + e
          e.preventDefault();
          this._exportBuild();
        }
        break;
    }
  }

  /**
   * Render the Page
   * @return {React.Component} The page contents
   */
  renderPage() {
    let state = this.state,
        { language, termtip, tooltip, sizeRatio, onWindowResize } = this.context,
        { translate, units, formats } = language,
        { ship, code, savedCode, buildName, newBuildName, sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange } = state,
        hide = tooltip.bind(null, null),
        menu = this.props.currentMenu,
        shipUpdated = this._shipUpdated,
        canSave = (newBuildName || buildName) && code !== savedCode,
        canRename = buildName && newBuildName && buildName != newBuildName,
        canReload = savedCode && canSave;

    // Code can be blank for a default loadout.  Prefix it with the ship name to ensure that changes in default ships is picked up
    code = ship.name + (code || '');

    // Markers are used to propagate state changes without requiring a deep comparison of the ship, as that takes a long time
    const _sStr = ship.getStandardString();
    const _iStr = ship.getInternalString();
    const _hStr = ship.getHardpointsString();
    const _pStr = `${ship.getPowerEnabledString()}${ship.getPowerPrioritiesString()}`;
    const _mStr = ship.getModificationsString();

    const standardSlotMarker = `${ship.name}${_sStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;
    const internalSlotMarker = `${ship.name}${_iStr}${_pStr}${_mStr}`;
    const hardpointsSlotMarker = `${ship.name}${_hStr}${_pStr}${_mStr}`;
    const boostMarker = `${ship.canBoost(cargo, fuel)}`;
    const shipSummaryMarker = `${ship.name}${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;

    const requirements = Ships[ship.id].requirements;
    let requirementElements = [];
    /**
     * Render the requirements for a ship / etc
     * @param {string} className Class names
     * @param {string} textKey The key for translating
     * @param {String} tooltipTextKey  Tooltip key
     */
    function renderRequirement(className, textKey, tooltipTextKey) {
      if (textKey.startsWith('empire') || textKey.startsWith('federation')) {
        requirementElements.push(<div key={textKey} className={className} onMouseEnter={termtip.bind(null, tooltipTextKey)} onMouseLeave={hide}><a href={textKey.startsWith('empire') ? 'http://elite-dangerous.wikia.com/wiki/Empire/Ranks' : 'http://elite-dangerous.wikia.com/wiki/Federation/Ranks'} target="_blank" rel="noopener">{translate(textKey)}</a></div>);
      } else {
        requirementElements.push(<div key={textKey} className={className} onMouseEnter={termtip.bind(null, tooltipTextKey)} onMouseLeave={hide}>{translate(textKey)}</div>);
      }
    }

    if (requirements) {
      requirements.federationRank && renderRequirement('federation', 'federation rank ' + requirements.federationRank, 'federation rank required');
      requirements.empireRank && renderRequirement('empire', 'empire rank ' + requirements.empireRank, 'empire rank required');
      requirements.horizons && renderRequirement('horizons', 'horizons', 'horizons required');
      requirements.horizonsEarlyAdoption && renderRequirement('horizons', 'horizons early adoption', 'horizons early adoption required');
    }

    return (
      <div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}>
        <div id='overview'>
          <h1>{ship.name}</h1>
          <div id='requirements'>{requirementElements}</div>
          <div id='build'>
            <input value={newBuildName || ''} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxLength={50} />
            <button onClick={canSave && this._saveBuild} disabled={!canSave} onMouseOver={termtip.bind(null, 'save')} onMouseOut={hide}>
              <FloppyDisk className='lg' />
            </button>
            <button onClick={canRename && this._renameBuild} disabled={!canRename} onMouseOver={termtip.bind(null, 'rename')} onMouseOut={hide}>
            <span style={{ textTransform: 'none', fontSize: '1.8em' }}>a|</span>
            </button>
            <button onClick={canReload && this._reloadBuild} disabled={!canReload} onMouseOver={termtip.bind(null, 'reload')} onMouseOut={hide}>
              <Reload className='lg'/>
            </button>
            <button className={'danger'} onClick={savedCode && this._deleteBuild} disabled={!savedCode} onMouseOver={termtip.bind(null, 'delete')} onMouseOut={hide}>
              <Bin className='lg'/>
            </button>
            <button onClick={code && this._resetBuild} disabled={!code} onMouseOver={termtip.bind(null, 'reset')} onMouseOut={hide}>
              <Switch className='lg'/>
            </button>
            <button onClick={buildName && this._exportBuild} disabled={!buildName} onMouseOver={termtip.bind(null, 'export')} onMouseOut={hide}>
              <Download className='lg'/>
            </button>
            <button onClick={this._eddbShoppingList} onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_LIST')} onMouseOut={hide}>
              <ShoppingIcon className='lg' />
            </button>
            <button onClick={this._genShortlink} onMouseOver={termtip.bind(null, 'shortlink')} onMouseOut={hide}>
              <LinkIcon className='lg' />
            </button>
            <button onClick={this._genOrbis} onMouseOver={termtip.bind(null, 'PHASE_UPLOAD_ORBIS')} onMouseOut={hide}>
              <OrbisIcon className='lg' />
            </button>
            <button onClick={this._genShoppingList} onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_MATS')} onMouseOut={hide}>
              <MatIcon className='lg' />
            </button>
          </div>
        </div>

        {/* Main tables */}
        <ShipSummaryTable ship={ship} fuel={fuel} cargo={cargo} marker={shipSummaryMarker} pips={{ sys: this.state.sys, wep: this.state.wep, eng: this.state.eng }} />
        <StandardSlotSection ship={ship} fuel={fuel} cargo={cargo} code={standardSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/>
        <InternalSlotSection ship={ship} code={internalSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/>
        <HardpointSlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/>
        <UtilitySlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/>

        {/* Control of ship and opponent */}
        <div className='group quarter'>
          <div className='group half'>
            <h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>{translate('ship control')}</h2>
          </div>
          <div className='group half'>
            <Boost marker={boostMarker} ship={ship} boost={boost} onChange={this._boostUpdated} />
          </div>
        </div>
        <div className='group quarter'>
          <Pips sys={sys} eng={eng} wep={wep} onChange={this._pipsUpdated} />
        </div>
        <div className='group quarter'>
          <Fuel fuelCapacity={ship.fuelCapacity} fuel={fuel} onChange={this._fuelUpdated}/>
        </div>
        <div className='group quarter'>
          { ship.cargoCapacity > 0 ? <Cargo cargoCapacity={ship.cargoCapacity} cargo={cargo} onChange={this._cargoUpdated}/> : null }
        </div>
        <div className='group half'>
          <div className='group quarter'>
            <h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>{translate('opponent')}</h2>
          </div>
          <div className='group threequarters'>
            <ShipPicker ship={opponent.id} build={opponentBuild} onChange={this._opponentUpdated}/>
          </div>
        </div>
        <div className='group half'>
          <EngagementRange ship={ship} engagementRange={engagementRange} onChange={this._engagementRangeUpdated}/>
        </div>

        {/* Tabbed subpages */}
        <OutfittingSubpages
          ship={ship}
          code={code}
          buildName={buildName}
          onChange={shipUpdated}
          sys={sys}
          eng={eng}
          wep={wep}
          boost={boost}
          cargo={cargo}
          fuel={fuel}
          engagementRange={engagementRange}
          opponent={opponent}
          opponentBuild={opponentBuild}
          opponentSys={opponentSys}
          opponentEng={opponentEng}
          opponentWep={opponentWep}
        />
      </div>
    );
  }
}
