/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

const constants = require('../constants');
const Immutable = require('devtools/client/shared/vendor/seamless-immutable');
const { mergeIn, setIn, deleteIn } = require('../utils');
const { makeLocationId } = require('../queries');

const initialState = Immutable({
  breakpoints: {}
});

function update(state = initialState, action, emitChange) {
  switch(action.type) {
  case constants.ADD_BREAKPOINT: {
    const id = makeLocationId(action.breakpoint.location);

    if (action.status === 'start') {
      const existingBp = state.breakpoints[id];
      const bp = existingBp || Immutable(action.breakpoint);

      state = setIn(state, ['breakpoints', id], bp.merge({
        disabled: false,
        loading: true,
        condition: action.condition || bp.condition || undefined
      }));

      emitChange(existingBp ? "breakpoint-enabled" : "breakpoint-added",
                 state.breakpoints[id]);
      return state;
    }
    else if (action.status === 'done') {
      const { actor, text } = action.value;
      let { actualLocation } = action.value;

      // If the breakpoint moved, update the map
      if (actualLocation) {
        // XXX Bug 1227417: The `setBreakpoint` RDP request rdp
        // request returns an `actualLocation` field that doesn't
        // conform to the regular { actor, line } location shape, but
        // it has a `source` field. We should fix that.
        actualLocation = { actor: actualLocation.source.actor,
                           line: actualLocation.line };

        state = deleteIn(state, ['breakpoints', id]);

        const movedId = makeLocationId(actualLocation);
        const currentBp = state.breakpoints[movedId] || Immutable(action.breakpoint);
        const prevLocation = action.breakpoint.location;
        const newBp = currentBp.merge({ location: actualLocation });
        state = setIn(state, ['breakpoints', movedId], newBp);

        emitChange('breakpoint-moved', {
          breakpoint: newBp,
          prevLocation: prevLocation
        });
      }

      const finalLocation = (
        actualLocation ? actualLocation : action.breakpoint.location
      );
      const finalLocationId = makeLocationId(finalLocation);
      state = mergeIn(state, ['breakpoints', finalLocationId], {
        disabled: false,
        loading: false,
        actor: actor,
        text: text
      });
      emitChange('breakpoint-updated', state.breakpoints[finalLocationId]);
      return state;
    }
    else if (action.status === 'error') {
      // Remove the optimistic update
      emitChange('breakpoint-removed', state.breakpoints[id]);
      return deleteIn(state, ['breakpoints', id]);
    }
      break;
  }

  case constants.REMOVE_BREAKPOINT: {
    if (action.status === 'done') {
      const id = makeLocationId(action.breakpoint.location);
      const bp = state.breakpoints[id];

      if (action.disabled) {
        state = mergeIn(state, ['breakpoints', id],
                        { loading: false, disabled: true });
        emitChange('breakpoint-disabled', state.breakpoints[id]);
        return state;
      }

      state = deleteIn(state, ['breakpoints', id]);
      emitChange('breakpoint-removed', bp);
      return state;
    }
    break;
  }

  case constants.SET_BREAKPOINT_CONDITION: {
    const id = makeLocationId(action.breakpoint.location);
    const bp = state.breakpoints[id];

    if (action.status === 'start') {
      return mergeIn(state, ['breakpoints', id], {
        loading: true,
        condition: action.condition
      });
    }
    else if (action.status === 'done') {
      return mergeIn(state, ['breakpoints', id], {
        loading: false,
        // Setting a condition creates a new breakpoint client as of
        // now, so we need to update the actor
        actor: action.value.actor
      });
    }
    else if (action.status === 'error') {
      return deleteIn(state, ['breakpoints', id]);
    }
    break;
  }}

  return state;
}

module.exports = update;
