import {BoundViewFactory, ViewSlot, bindable, customAttribute, templateController} from 'aurelia-templating';
import {inject} from 'aurelia-dependency-injection';
import {IfCore} from './if-core';

/**
 * Binding to conditionally include or not include template logic depending on returned result
 * - value should be Boolean or will be treated as such (truthy / falsey)
 */
@customAttribute('if')
@templateController
@inject(BoundViewFactory, ViewSlot)
export class If extends IfCore {
  @bindable({ primaryProperty: true }) condition: any;
  @bindable swapOrder: 'before'|'with'|'after';
  @bindable cache: boolean|string = true;

  /**@internal*/
  animating: any;
  /**@internal*/
  elseVm: any;

  /**
   * Binds the if to the binding context and override context
   * @param bindingContext The binding context
   * @param overrideContext An override context for binding.
   */
  bind(bindingContext, overrideContext) {
    super.bind(bindingContext, overrideContext);
    if (this.condition) {
      this._show();
    } else {
      this._hide();
    }
  }

  /**
   * Invoked everytime value property changes.
   * @param newValue The new value
   */
  conditionChanged(newValue) {
    this._update(newValue);
  }

  /**
   * @internal
   */
  _update(show) {
    if (this.animating) {
      return;
    }

    let promise;
    if (this.elseVm) {
      promise = show ? this._swap(this.elseVm, this) : this._swap(this, this.elseVm);
    } else {
      promise = show ? this._show() : this._hide();
    }

    if (promise) {
      this.animating = true;
      promise.then(() => {
        this.animating = false;
        if (this.condition !== this.showing) {
          this._update(this.condition);
        }
      });
    }
  }

  /**
   * @internal
   */
  _swap(remove, add) {
    switch (this.swapOrder) {
    case 'before':
      return Promise.resolve(add._show()).then(() => remove._hide());
    case 'with':
      return Promise.all([ remove._hide(), add._show() ]);
    default:  // "after", default and unknown values
      let promise = remove._hide();
      return promise ? promise.then(() => add._show()) : add._show();
    }
  }
}
