"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.dispatchAction = dispatchAction;
exports.getActiveInstance = void 0;
exports.instances = instances;
exports.instancesInitialState = void 0;
var _actionTypes = require("../constants/actionTypes");
var _socketActionTypes = require("../constants/socketActionTypes");
var _parseJSON = _interopRequireDefault(require("../utils/parseJSON"));
var _updateState = require("../utils/updateState");
const instancesInitialState = exports.instancesInitialState = {
  selected: null,
  current: 'default',
  sync: false,
  connections: {},
  options: {
    default: {
      features: {}
    }
  },
  states: {
    default: {
      actionsById: {},
      computedStates: [],
      currentStateIndex: -1,
      nextActionId: 0,
      skippedActionIds: [],
      stagedActionIds: []
    }
  }
};
function updateState(state, request, id, serialize) {
  let payload = request.payload;
  const actionsById = request.actionsById;
  if (actionsById) {
    payload = {
      // eslint-disable-next-line @typescript-eslint/ban-types
      ...payload,
      actionsById: (0, _parseJSON.default)(actionsById, serialize),
      computedStates: (0, _parseJSON.default)(request.computedStates, serialize)
    };
    if (request.type === 'STATE' && request.committedState) {
      payload.committedState = payload.computedStates[0].state;
    }
  } else {
    payload = (0, _parseJSON.default)(payload, serialize);
  }
  let newState;
  const liftedState = state[id] || state.default;
  const action = request.action && (0, _parseJSON.default)(request.action, serialize) || {};
  switch (request.type) {
    case 'INIT':
      newState = (0, _updateState.recompute)(state.default, payload, {
        action: {
          type: '@@INIT'
        },
        timestamp: action.timestamp || Date.now()
      });
      break;
    case 'ACTION':
      {
        const isExcess = request.isExcess;
        const nextActionId = request.nextActionId || liftedState.nextActionId + 1;
        const maxAge = request.maxAge;
        if (Array.isArray(action)) {
          // Batched actions
          newState = liftedState;
          for (let i = 0; i < action.length; i++) {
            newState = (0, _updateState.recompute)(newState, request.batched ? payload : payload[i], action[i], newState.nextActionId + 1, maxAge, isExcess);
          }
        } else {
          newState = (0, _updateState.recompute)(liftedState, payload, action, nextActionId, maxAge, isExcess);
        }
        break;
      }
    case 'STATE':
      newState = payload;
      if (newState.computedStates.length <= newState.currentStateIndex) {
        newState.currentStateIndex = newState.computedStates.length - 1;
      }
      break;
    case 'PARTIAL_STATE':
      {
        const maxAge = request.maxAge;
        const nextActionId = payload.nextActionId;
        const stagedActionIds = payload.stagedActionIds;
        let computedStates = payload.computedStates;
        let oldActionsById;
        let oldComputedStates;
        let committedState;
        if (nextActionId > maxAge) {
          const oldStagedActionIds = liftedState.stagedActionIds;
          const excess = oldStagedActionIds.indexOf(stagedActionIds[1]);
          let key;
          if (excess > 0) {
            oldComputedStates = liftedState.computedStates.slice(excess - 1);
            oldActionsById = {
              ...liftedState.actionsById
            };
            for (let i = 1; i < excess; i++) {
              key = oldStagedActionIds[i];
              if (key) delete oldActionsById[key];
            }
            committedState = computedStates[0].state;
          } else {
            oldActionsById = liftedState.actionsById;
            oldComputedStates = liftedState.computedStates;
            committedState = liftedState.committedState;
          }
        } else {
          oldActionsById = liftedState.actionsById;
          oldComputedStates = liftedState.computedStates;
          committedState = liftedState.committedState;
        }
        computedStates = [...oldComputedStates, ...computedStates];
        const statesCount = computedStates.length;
        let currentStateIndex = payload.currentStateIndex;
        if (statesCount <= currentStateIndex) currentStateIndex = statesCount - 1;
        newState = {
          ...liftedState,
          actionsById: {
            ...oldActionsById,
            ...payload.actionsById
          },
          computedStates,
          currentStateIndex,
          nextActionId,
          stagedActionIds,
          committedState
        };
        break;
      }
    case 'LIFTED':
      newState = liftedState;
      break;
    default:
      return state;
  }
  if (request.liftedState) newState = {
    ...newState,
    ...request.liftedState
  };
  return {
    ...state,
    [id]: newState
  };
}
function dispatchAction(state, _ref) {
  let {
    action
  } = _ref;
  if (action.type === 'JUMP_TO_STATE' || action.type === 'JUMP_TO_ACTION') {
    const id = state.selected || state.current;
    const liftedState = state.states[id];
    const currentStateIndex = action.type === 'JUMP_TO_STATE' ? action.index : liftedState.stagedActionIds.indexOf(action.actionId);
    return {
      ...state,
      states: {
        ...state.states,
        [id]: {
          ...liftedState,
          currentStateIndex
        }
      }
    };
  }
  return state;
}
function removeState(state, connectionId) {
  const instanceIds = state.connections[connectionId];
  if (!instanceIds) return state;
  const connections = {
    ...state.connections
  };
  const options = {
    ...state.options
  };
  const states = {
    ...state.states
  };
  let selected = state.selected;
  let current = state.current;
  let sync = state.sync;
  delete connections[connectionId];
  instanceIds.forEach(id => {
    if (id === selected) {
      selected = null;
      sync = false;
    }
    if (id === current) {
      const inst = Object.keys(connections)[0];
      if (inst) current = connections[inst][0];else current = 'default';
    }
    delete options[id];
    delete states[id];
  });
  return {
    selected,
    current,
    sync,
    connections,
    options,
    states
  };
}
function init(_ref2, connectionId, current) {
  let {
    type,
    action,
    name,
    libConfig = {}
  } = _ref2;
  let lib;
  let actionCreators;
  let creators = libConfig.actionCreators || action;
  if (typeof creators === 'string') creators = JSON.parse(creators);
  if (Array.isArray(creators)) actionCreators = creators;
  if (type === 'STATE') lib = 'redux';
  return {
    name: libConfig.name || name || current,
    connectionId,
    explicitLib: libConfig.type,
    lib,
    actionCreators,
    features: libConfig.features ? libConfig.features : {
      lock: lib === 'redux',
      export: libConfig.type === 'redux' ? 'custom' : true,
      import: 'custom',
      persist: true,
      pause: true,
      reorder: true,
      jump: true,
      skip: true,
      dispatch: true,
      sync: true,
      test: true
    },
    serialize: libConfig.serialize
  };
}
function instances() {
  let state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : instancesInitialState;
  let action = arguments.length > 1 ? arguments[1] : undefined;
  switch (action.type) {
    case _actionTypes.UPDATE_STATE:
      {
        const {
          request
        } = action;
        if (!request) return state;
        const connectionId = action.id || request.id;
        const current = request.instanceId || connectionId;
        let connections = state.connections;
        let options = state.options;
        if (typeof state.options[current] === 'undefined') {
          connections = {
            ...state.connections,
            [connectionId]: [...(connections[connectionId] || []), current]
          };
          options = {
            ...options,
            [current]: init(request, connectionId, current)
          };
        }
        return {
          ...state,
          current,
          connections,
          options,
          states: updateState(state.states, request, current, options[current].serialize)
        };
      }
    case _actionTypes.SET_STATE:
      return {
        ...state,
        states: {
          ...state.states,
          [getActiveInstance(state)]: action.newState
        }
      };
    case _actionTypes.TOGGLE_PERSIST:
      return {
        ...state,
        persisted: !state.persisted
      };
    case _actionTypes.SET_PERSIST:
      return {
        ...state,
        persisted: action.payload
      };
    case _actionTypes.TOGGLE_SYNC:
      return {
        ...state,
        sync: !state.sync
      };
    case _actionTypes.SELECT_INSTANCE:
      return {
        ...state,
        selected: action.selected,
        sync: false
      };
    case _actionTypes.REMOVE_INSTANCE:
      return removeState(state, action.id);
    case _actionTypes.LIFTED_ACTION:
      {
        if (action.message === 'DISPATCH') return dispatchAction(state, action);
        if (action.message === 'IMPORT') {
          const id = state.selected || state.current;
          if (state.options[id].features.import === true) {
            return {
              ...state,
              states: {
                ...state.states,
                [id]: (0, _parseJSON.default)(action.state)
              }
            };
          }
        }
        return state;
      }
    case _socketActionTypes.DISCONNECTED:
      return instancesInitialState;
    default:
      return state;
  }
}
const getActiveInstance = instances => instances.selected || instances.current;
exports.getActiveInstance = getActiveInstance;