
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

import ActionTypes from './actiontypes';
import Helpers from '../helpers';

const initialState = {
  isLoaded: false,
  filters: [],
  changes: [],
  filteredChanges: [],
  maxChangesListed: 100,
  showingSubset: false,
  lastSequenceNum: null
};

function updateChanges(state, seqNum, changes, reset) {
  // reset to the initial state when switching between databases so changes aren't carried over
  const newState = (reset) ? {
    ...initialState,
    // make a note of the most recent sequence number. This is used for a point of reference for polling for new changes
    lastSequenceNum: seqNum,
    isLoaded: true
  } :
    {
      ...state,
      lastSequenceNum: seqNum,
      isLoaded: true
    };

  // mark any additional changes that come after first page load as "new" so we can add a nice highlight effect
  // when the new row is rendered
  const firstBatch = newState.changes.length === 0;
  newState.changes.forEach((change) => {
    change.isNew = false;
  });

  const newChanges = changes.map((change) => {
    const seq = Helpers.getSeqNum(change.seq);
    return {
      id: change.id,
      seq: seq,
      deleted: _.has(change, 'deleted') ? change.deleted : false,
      changes: change.changes,
      doc: change.doc, // only populated with ?include_docs=true
      isNew: !firstBatch
    };
  });

  // add the new changes to the start of the list
  newState.changes = newChanges.concat(newState.changes);
  updateFilteredChanges(newState);
  return newState;
}

function addFilter(state, filter) {
  const newFilters = state.filters.slice();
  newFilters.push(filter);

  const newState = {
    ...state,
    filters: newFilters
  };
  updateFilteredChanges(newState);
  return newState;
}

function removeFilter(state, filter) {
  const newFilters = state.filters.slice();
  const idx = newFilters.indexOf(filter);
  if (idx >= 0) {
    newFilters.splice(idx, 1);
  }

  const newState = {
    ...state,
    filters: newFilters
  };
  updateFilteredChanges(newState);
  return newState;
}

function updateFilteredChanges(state) {
  state.showingSubset = false;
  let numMatches = 0;
  state.filteredChanges = state.changes.filter((change) => {
    if (numMatches >= state.maxChangesListed) {
      state.showingSubset = true;
      return false;
    }
    let changeStr = JSON.stringify(change);
    let match = state.filters.every((filter) => {
      return new RegExp(filter, 'i').test(changeStr);
    });

    if (match) {
      numMatches++;
    }
    return match;
  });
}

export default function changes (state = initialState, action) {
  switch (action.type) {

    case ActionTypes.UPDATE_CHANGES:
      return updateChanges(state, action.seqNum, action.changes, action.resetChanges);

    case ActionTypes.ADD_CHANGES_FILTER_ITEM:
      return addFilter(state, action.filter);

    case ActionTypes.REMOVE_CHANGES_FILTER_ITEM:
      return removeFilter(state, action.filter);

    default:
      return state;
  }
}
