import {createFullOverrideContext, updateOverrideContexts} from './repeat-utilities';

/**
 * A strategy for repeating a template over a Set.
 */
export class SetRepeatStrategy {
  /**
   * Gets a Set observer.
   * @param items The items to be observed.
   */
  getCollectionObserver(observerLocator, items) {
    return observerLocator.getSetObserver(items);
  }

  /**
   * Process the provided Set entries.
   * @param items The entries to process.
   */
  instanceChanged(repeat, items) {
    let removePromise = repeat.removeAllViews(true, !repeat.viewsRequireLifecycle);
    if (removePromise instanceof Promise) {
      removePromise.then(() => this._standardProcessItems(repeat, items));
      return;
    }
    this._standardProcessItems(repeat, items);
  }

  /**
   * @internal
   */
  _standardProcessItems(repeat, items) {
    let index = 0;
    let overrideContext;

    items.forEach(value => {
      overrideContext = createFullOverrideContext(repeat, value, index, items.size);
      repeat.addView(overrideContext.bindingContext, overrideContext);
      ++index;
    });
  }

  /**
   * Handle changes in a Set collection.
   * @param repeat The repeat instance.
   * @param set The underlying Set collection.
   * @param records The change records.
   */
  instanceMutated(repeat, set, records) {
    let value;
    let i;
    let ii;
    let overrideContext;
    let removeIndex;
    let record;
    let rmPromises = [];
    let viewOrPromise;

    for (i = 0, ii = records.length; i < ii; ++i) {
      record = records[i];
      value = record.value;
      switch (record.type) {
      case 'add':
        let size = Math.max(set.size - 1, 0);
        overrideContext = createFullOverrideContext(repeat, value, size, set.size);
        repeat.insertView(size, overrideContext.bindingContext, overrideContext);
        break;
      case 'delete':
        removeIndex = this._getViewIndexByValue(repeat, value);
        viewOrPromise = repeat.removeView(removeIndex, true, !repeat.viewsRequireLifecycle);
        if (viewOrPromise instanceof Promise) {
          rmPromises.push(viewOrPromise);
        }
        break;
      case 'clear':
        repeat.removeAllViews(true, !repeat.viewsRequireLifecycle);
        break;
      default:
        continue;
      }
    }

    if (rmPromises.length > 0) {
      Promise.all(rmPromises).then(() => {
        updateOverrideContexts(repeat.views(), 0);
      });
    } else {
      updateOverrideContexts(repeat.views(), 0);
    }
  }

  /**
   * @internal
   */
  _getViewIndexByValue(repeat, value) {
    let i;
    let ii;
    let child;

    for (i = 0, ii = repeat.viewCount(); i < ii; ++i) {
      child = repeat.view(i);
      if (child.bindingContext[repeat.local] === value) {
        return i;
      }
    }

    return undefined;
  }
}
