/* 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: {}
});

// Return the first argument that is a string, or null if nothing is a
// string.
function firstString(...args) {
  for (var arg of args) {
    if (typeof arg === "string") {
      return arg;
    }
  }
  return null;
}

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,
        // We want to do an OR here, but we can't because we need
        // empty strings to be truthy, i.e. an empty string is a valid
        // condition.
          condition: firstString(action.condition, bp.condition)
        }));

        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];
      emitChange("breakpoint-condition-updated", bp);

      if (!action.status) {
      // No status means that it wasn't a remote request. Just update
      // the condition locally.
        return mergeIn(state, ["breakpoints", id], {
          condition: action.condition
        });
      }
      else 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,
        condition: action.condition,
        // 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") {
      emitChange("breakpoint-removed", bp);
      return deleteIn(state, ["breakpoints", id]);
    }

      break;
    }}

  return state;
}

module.exports = update;
