import {
  AdvancedProperty,
  FieldValueFromVariable,
  FilterComponent,
  FilterValuesStrategy,
  IInputControl,
  IReadablePageObjectStorage,
  IWritablePageObjectStorage,
  LocalStorageService,
  NotNull,
  PageContextHolder,
  PageObjectByIdStorage,
  PageObjectByNameStorage,
  PageObjectUtils,
  TaskParamsProvider,
  ValidatableContainer,
  Visible
} from "@webbpm/base-package";
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef} from "@angular/core";

@Component({
             moduleId: module.id,
             selector: 'filter-container',
             templateUrl: './../../../../../src/resources/template/ervu-dashboard/FilterContainer.html',
             changeDetection: ChangeDetectionStrategy.OnPush
           })
export class FilterContainer extends ValidatableContainer {

  @AdvancedProperty()
  public filterValuesLoadStrategy: FilterValuesStrategy;

  @Visible("filterValuesLoadStrategy == FilterValuesStrategy.BY_CUSTOM_NAME")
  @NotNull("filterValuesLoadStrategy == FilterValuesStrategy.BY_CUSTOM_NAME")
  @AdvancedProperty()
  public filterValuesLoadStorageName: string;

  @AdvancedProperty()
  public filterValuesSaveStrategy: FilterValuesStrategy;

  @Visible("filterValuesSaveStrategy == FilterValuesStrategy.BY_CUSTOM_NAME")
  @NotNull("filterValuesSaveStrategy == FilterValuesStrategy.BY_CUSTOM_NAME")
  @AdvancedProperty()
  public filterValuesSaveStorageName: string;

  protected localStorageService: LocalStorageService;
  protected taskParamsProvider: TaskParamsProvider;
  protected pageContextHolder: PageContextHolder;

  protected loadFilterCache: IReadablePageObjectStorage;
  protected saveFilterCache: IWritablePageObjectStorage;

  constructor(el: ElementRef, cd: ChangeDetectorRef) {
    super(el, cd);
  }

  initialize() {
    super.initialize();
    this.taskParamsProvider = this.injector.get(TaskParamsProvider);
    this.localStorageService = this.injector.get(LocalStorageService);
    this.pageContextHolder = this.injector.get(PageContextHolder);
    this.loadFilterCache =
        this.getFilterCache(this.filterValuesLoadStrategy, this.filterValuesLoadStorageName);
    this.saveFilterCache =
        this.getFilterCache(this.filterValuesSaveStrategy, this.filterValuesSaveStorageName);
  }

  public start() {
    super.start();
    return this.loadInitialValues()
  }

  public loadInitialValues(): Promise<any> {
    const scriptsInChildren: FilterComponent[] = this.getContainerComponents(FilterComponent);
    scriptsInChildren.forEach((filter: FilterComponent) => filter.setInitFilterInput());

    let fieldValueFromVariable: FieldValueFromVariable[]
        = this.getContainerComponents(FieldValueFromVariable);
    fieldValueFromVariable.forEach(script => script.setControlValueFromVariable());

    return this.loadDefaultValues()
        .then(() => {
          if (this.filterValuesLoadStrategy != null) {
            return this.restoreFiltersFromCache(scriptsInChildren);
          }
        });
  }

  protected loadContainer(): Promise<any> {
    return this.loadInitialValues();
  }

  protected validateCommon(): Promise<boolean> {
    return Promise.resolve(this.getValidatableFields()
                               .every(input => {
                                 input.updateValueAndValidity();
                                 return (!input.isVisible() || !input.isEnabled() ||
                                         input.hasValidModel());
                               }));
  }

  public getValidatableFields(): IInputControl[] {
    return this.getContainerComponents(FilterComponent)
        .filter((filter: FilterComponent) => filter.getControlWithValue() !=
                                             null) //FilterComponent.ts checks for collectible or
        // something
        .map((filter: FilterComponent) => filter.getScript('component.field.IInputControl'))
        .filter(o => o != null)
  }

  protected childPredicate(node): boolean {
    return super.childPredicate(node) &&
           !(node instanceof FilterContainer) &&
           node.interfaces.includes("component.Collectible");
  }

  protected restoreFiltersFromCache(scriptsInChildren: FilterComponent[]): Promise<any> {
    const valueSetPromises = scriptsInChildren
        .filter((filter: FilterComponent) => filter.getControlWithValue())
        .map((filter: FilterComponent) => {
          const cachedValue = this.loadFilterCache.get(filter);

          if (cachedValue) {
            return filter.setFilterInput(cachedValue);
          }
          else {
            return Promise.resolve();
          }
        });

    return Promise.all(valueSetPromises);
  }

  protected getFilterCache(filterValuesStorage, customKeyName: string) {

    if (!filterValuesStorage) {
      return null;
    }
    let webbpmStorage;

    switch (filterValuesStorage) {
      case FilterValuesStrategy.BY_PAGE_OBJECT_ID:
        webbpmStorage = this.readWebbpmStorage(PageObjectUtils.getPageObjectKey(this));
        return new PageObjectByIdStorage(webbpmStorage);
      case FilterValuesStrategy.BY_OBJECT_NAME:
        webbpmStorage = this.readWebbpmStorage(this.getObjectName());
        return new PageObjectByNameStorage(webbpmStorage);
      case FilterValuesStrategy.BY_CUSTOM_NAME:
        webbpmStorage = this.readWebbpmStorage(customKeyName);
        return new PageObjectByNameStorage(webbpmStorage);
      default:
        throw new Error("Unknown filter values storage type = " + filterValuesStorage)
    }
  }

  protected readWebbpmStorage(filterStorageKey: string) {

    if (this.pageContextHolder.isPageInBpmnContext()) {
      filterStorageKey = filterStorageKey + "$" + this.taskParamsProvider.processInstanceId
    }
    return this.localStorageService.readTemporalWebbpmStorage(filterStorageKey);
  }
}