import {
  AfterViewInit,
  ChangeDetectorRef,
  Component,
  HostListener,
  Input,
  OnChanges,
  OnInit,
  SimpleChanges,
  ViewChild
} from "@angular/core";
import {
  Format,
  Indicator,
  IndicatorPath,
  IndicatorSize,
  IndicatorType,
  Section,
  Stakeholder,
  Visibility
} from "../../monitor/entities/stakeholder";
import {
  AbstractControl, FormArray, FormGroup,
  UntypedFormArray,
  UntypedFormBuilder,
  UntypedFormControl,
  UntypedFormGroup,
  Validators
} from "@angular/forms";
import {AlertModal} from "../../utils/modal/alert";
import {StatisticsService} from "../utils/services/statistics.service";
import {HelperFunctions} from "../../utils/HelperFunctions.class";
import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser";
import {MoveIndicator, SectionInfo, StakeholderService} from "../../monitor/services/stakeholder.service";
import {BehaviorSubject, Observable, Subscriber} from "rxjs";
import {LayoutService} from "../../dashboard/sharedComponents/sidebar/layout.service";
import {Router} from "@angular/router";
import {Role, Session, User} from "../../login/utils/helper.class";
import {StringUtils} from "../../utils/string-utils.class";
import {Notification} from "../../notifications/notifications";
import {NotificationUtils} from "../../notifications/notification-utils";
import {NotifyFormComponent} from "../../notifications/notify-form/notify-form.component";
import {NotificationService} from "../../notifications/notification.service";
import {NotificationHandler} from "../../utils/notification-handler";
import {IndicatorStakeholderBaseComponent} from "../utils/stakeholder-base.component";
import {properties} from "../../../../environments/environment";
import {StatsProfilesService} from "../utils/services/stats-profiles.service";
import {TransitionGroupComponent} from "../../utils/transition-group/transition-group.component";

declare var UIkit;
declare var copy;

@Component({
  selector: 'indicators',
  templateUrl: './indicators.component.html'
})
export class IndicatorsComponent extends IndicatorStakeholderBaseComponent implements OnInit, OnChanges, AfterViewInit {
  filesToUpload: Array<File>;
  errorMessage = "";
  @Input()
  public topicIndex: number = 0;
  @Input()
  public categoryIndex: number = 0;
  @Input()
  public subcategoryIndex: number = 0;
  @Input()
  public stakeholder: Stakeholder = null;
  @Input()
  public changed: Observable<void>;
  @Input()
  public user: User = null;
  public preview: string;
  public statsProfiles = [];
  public numberIndicatorFb: UntypedFormGroup;
  public chartIndicatorFb: UntypedFormGroup;
  public chartSections: UntypedFormArray;
  public numberSections: UntypedFormArray;
  /**
   * Editable indicator
   */
  public section: Section;
  public indicator: Indicator;
  public index: number = -1;
  public editing: boolean = false;
  public dragging: boolean = false;
  /* Reorder indicators */
  public to: BehaviorSubject<SectionInfo> = new BehaviorSubject<SectionInfo>(null);
  /** Caches */
  public safeUrls: Map<string, SafeResourceUrl> = new Map<string, SafeResourceUrl>([]);
  public numberResponses: Map<string, any> = new Map<string, any>();
  public numberResults: Map<string, number> = new Map<string, number>();
  public loading: boolean = false;
  @ViewChild('editChartModal', {static: true}) editChartModal: AlertModal;
  @ViewChild('editNumberModal', {static: true}) editNumberModal: AlertModal;
  @ViewChild('deleteModal', {static: true}) deleteModal: AlertModal;
  @ViewChild('deleteSectionModal', {static: true}) deleteSectionModal: AlertModal;
  public sectionTypeToDelete: string;
  public sectionChildrenActionOnDelete: string;
  public indicatorChildrenActionOnDelete: string;
  private notification: Notification;
  @ViewChild('editNumberNotify', {static: true}) editNumberNotify: NotifyFormComponent;
  @ViewChild('editChartNotify', {static: true}) editChartNotify: NotifyFormComponent;
  @ViewChild('deleteNotify', {static: true}) deleteNotify: NotifyFormComponent;
  /* Transition Groups */
  @ViewChild('transitionGroup') transitionGroup: TransitionGroupComponent;

  public isFullscreen: boolean = false;

  @HostListener('fullscreenchange', ['$event'])
  @HostListener('webkitfullscreenchange', ['$event'])
  @HostListener('mozfullscreenchange', ['$event'])
  @HostListener('MSFullscreenChange', ['$event'])
  screenChange(event) {
    this.isFullscreen = !this.isFullscreen;
  }

  /**
   * Subscriptions
   **/
  private urlSubscriptions: any[] = [];
  private numberSubscription: any[] = [];

  constructor(private layoutService: LayoutService,
              private stakeholderService: StakeholderService,
              private statisticsService: StatisticsService,
              private statsProfileService: StatsProfilesService,
              private notificationService: NotificationService,
              private fb: UntypedFormBuilder,
              protected _router: Router,
              private sanitizer: DomSanitizer) {
    super()
    this.filesToUpload = [];
  }

  ngOnInit(): void {
    if (this.stakeholder) {
      this.setCharts();
      this.setNumbers();
    }
    this.changed.subscribe(() => {
      this.setCharts();
      this.setNumbers();
      this.initReorder();
    });
    if (this.isCurator) {
      this.subscriptions.push(this.statsProfileService.getStatsProfiles().subscribe(statsProfiles => {
        this.statsProfiles = [null].concat(statsProfiles);
      }, error => {
        this.statsProfiles = [];
      }));
    } else {
      this.statsProfiles = [];
    }
  }

  ngOnDestroy(): void {
    super.ngOnDestroy();
    this.urlSubscriptions.forEach(value => {
      if (value instanceof Subscriber) {
        value.unsubscribe();
      }
    });
    this.numberSubscription.forEach(value => {
      if (value instanceof Subscriber) {
        value.unsubscribe();
      }
    });
  }

  ngAfterViewInit(): void {
    this.initReorder();
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (this.canEdit) {
      if (changes.topicIndex || changes.categoryIndex || changes.subcategoryIndex) {
        this.initReorder();
        this.setCharts();
        this.setNumbers();
      }
    }
  }

  initReorder() {
    this.subscriptions.forEach(value => {
      if (value instanceof Function) {
        value();
      }
    });
    if (document !== undefined) {
      let callback = (list): string[] => {
        let items: HTMLCollection = list.current.children;
        let reordered = [];
        for (let i = 0; i < items.length; i++) {
          if (items.item(i).id) {
            reordered.push(items.item(i).id);
          }
        }
        return reordered;
      };
      this.numbers.forEach((section) => {
        this.subscriptions.push(UIkit.util.on(document, 'start', '#number-' + section._id, (): void => {
          this.dragging = true;
        }));
        this.subscriptions.push(UIkit.util.on(document, 'stop', '#number-' + section._id, (): void => {
          this.dragging = false;
        }));
        this.subscriptions.push(UIkit.util.on(document, 'moved', '#number-' + section._id, (list): void => {
          this.reorderIndicators(section._id, 'number', callback(list));
        }));
        this.subscriptions.push(UIkit.util.on(document, 'added', '#number-' + section._id, (list): void => {
          this.to.next({id: section._id, indicators: callback(list)});
        }));
        this.subscriptions.push(UIkit.util.on(document, 'removed', '#number-' + section._id, (list): void => {
          let sub = this.to.asObservable().subscribe(to => {
            if (to) {
              let from: SectionInfo = {id: section._id, indicators: callback(list)};
              this.moveIndicator({target: list.detail[1].id, from: from, to: to});
              setTimeout(() => {
                sub.unsubscribe();
              })
            }
          })
        }));
      });
      this.charts.forEach((section) => {
        this.subscriptions.push(UIkit.util.on(document, 'moved', '#chart-' + section._id, (list): void => {
          this.reorderIndicators(section._id, 'chart', callback(list));
        }));
        this.subscriptions.push(UIkit.util.on(document, 'added', '#chart-' + section._id, (list): void => {
          //callback(list, "chart", 'added');
        }));
        this.subscriptions.push(UIkit.util.on(document, 'removed', '#chart-' + section._id, (list): void => {
          // callback(list, "chart", 'removed');
        }));
      });
    }
  }

  hide(element: any) {
    UIkit.dropdown(element).hide();
  }

  setCharts() {
    this.chartSections = this.fb.array([]);
    this.charts.forEach(section => {
      this.chartSections.push(this.fb.group({
        _id: this.fb.control(section._id),
        title: this.fb.control(section.title),
        creationDate: this.fb.control(section.creationDate),
        stakeholderAlias: this.fb.control(section.stakeholderAlias),
        defaultId: this.fb.control(section.defaultId),
        type: this.fb.control(section.type),
        indicators: this.fb.control(section.indicators)
      }));
      section.indicators.forEach(indicator => {
        indicator.indicatorPaths.forEach(indicatorPath => {
          let url = this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath);
          if (!this.safeUrls.get('url')) {
            indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
            this.safeUrls.set(url, indicatorPath.safeResourceUrl);
          }
        });
      })
    });
  }

  setNumbers() {
    this.numberSections = this.fb.array([]);
    this.numberResults.clear();
    let urls: Map<string, [number, number, number][]> = new Map<string, [number, number, number][]>();
    this.numbers.forEach((section, i) => {
      this.numberSections.push(this.fb.group({
        _id: this.fb.control(section._id),
        title: this.fb.control(section.title),
        creationDate: this.fb.control(section.creationDate),
        stakeholderAlias: this.fb.control(section.stakeholderAlias),
        defaultId: this.fb.control(section.defaultId),
        type: this.fb.control(section.type),
        indicators: this.fb.control(section.indicators)
      }));
      section.indicators.forEach((number, j) => {
        number.indicatorPaths.forEach((indicatorPath, k) => {
          let url = this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath);
          const pair = JSON.stringify([indicatorPath.source, url]);
          const indexes = urls.get(pair) ? urls.get(pair) : [];
          indexes.push([i, j, k]);
          urls.set(pair, indexes);
        });
      });
    });
    this.numberSubscription.forEach(value => {
      if (value instanceof Subscriber) {
        value.unsubscribe();
      }
    });
    urls.forEach((indexes, pair) => {
      let parsed = JSON.parse(pair);
      let response = this.numberResponses.get(pair);
      if (response) {
        this.calculateResults(response, indexes);
      } else {
        this.numberSubscription.push(this.statisticsService.getNumbers(this.indicatorUtils.getSourceType(parsed[0]), parsed[1]).subscribe(response => {
          this.calculateResults(response, indexes);
          this.numberResponses.set(pair, response);
        }));
      }
    });
  }

  private calculateResults(response: any, indexes: [number, number, number][]) {
    indexes.forEach(([i, j, k]) => {
      let result = JSON.parse(JSON.stringify(response));
      this.numbers[i].indicators[j].indicatorPaths[k].jsonPath.forEach(jsonPath => {
        if (result) {
          result = result[jsonPath];
        }
      });
      if (typeof result === 'string' || typeof result === 'number') {
        result = Number(result);
        if (result === Number.NaN) {
          result = 0;
        }
      } else {
        result = 0;
      }
      this.numberResults.set(i + '-' + j + '-' + k, result);
    });
  }

  get charts(): Section[] {
    if (this.stakeholder.topics[this.topicIndex] &&
        this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
        this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]) {
      return this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].charts;
    } else {
      return [];
    }
  }

  get numbers(): Section[] {
    if (this.stakeholder.topics[this.topicIndex] &&
        this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
        this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]) {
      return this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].numbers;
    } else {
      return [];
    }
  }

  set numbers(sections: Section[]) {
    this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].numbers = sections;
  }

  get open(): boolean {
    return this.layoutService.open;
  }

  get canEdit() {
    return this.stakeholder &&
        this.stakeholder.topics[this.topicIndex] &&
        this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
        this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex] && !this.loading;
  }

  public get numberIndicatorPaths(): UntypedFormArray {
    return this.numberIndicatorFb.get('indicatorPaths') as UntypedFormArray;
  }

  public get chartIndicatorPaths(): UntypedFormArray {
    return this.chartIndicatorFb.get('indicatorPaths') as UntypedFormArray;
  }

  public getActiveIndicatorPath(indicator: Indicator) {
    if (indicator.activePath) {
      return indicator.indicatorPaths[indicator.activePath];
    } else {
      return indicator.indicatorPaths[0];
    }
  }

  public getNumberClassBySize(size: IndicatorSize) {
    if (size === 'small') {
      return 'uk-width-medium@m uk-width-1-1';
    } else if (size === 'medium') {
      return 'uk-width-1-4@l uk-width-1-2@m uk-width-1-1';
    } else {
      return 'uk-width-1-2@l uk-width-1-1@m uk-width-1-1';
    }
  }

  public getChartClassBySize(size: IndicatorSize) {
    if (size === 'small') {
      return 'uk-width-1-3@xl uk-width-1-2@m uk-width-1-1';
    } else if (size === 'medium') {
      return 'uk-width-1-2@l uk-width-1-1';
    } else {
      return 'uk-width-1-1';
    }
  }

  public addJsonPath(index: number) {
    if (index == 0 && this.getJsonPath(index).getRawValue()[index].indexOf(",") != -1) {
      //if in the first path there are more than one paths comma separated, split them and autogenerate the forms
      let paths = this.getJsonPath(index).getRawValue()[index].split(",");
      for (let i = 0; i < paths.length; i++) {
        if (i != 0) {
          this.getJsonPath(index).push(this.fb.control('', Validators.required));
        }
      }
      this.getJsonPath(index).setValue(paths)
    } else {
      this.getJsonPath(index).push(this.fb.control('', Validators.required));
    }
  }

  public removeJsonPath(i: number, j: number) {
    if (this.getJsonPath(i).enabled) {
      this.getJsonPath(i).removeAt(j);
    }
  }

  public validateJsonPath(index: number, dirty: boolean = false) {
    let indicatorPath: UntypedFormGroup = <UntypedFormGroup>this.numberIndicatorPaths.at(index);
    if (this.indicator.defaultId === null) {
      this.getJsonPath(index).disable();
    }
    indicatorPath.get('result').setErrors({validating: true});
    this.subscriptions.push(this.statisticsService.getNumbers(indicatorPath.get('source').value, this.indicatorUtils.getFullUrl(this.stakeholder, this.indicator.indicatorPaths[index])).subscribe(response => {
      let result = JSON.parse(JSON.stringify(response));
      this.getJsonPath(index).controls.forEach(jsonPath => {
        if (result) {
          result = result[jsonPath.value];
        }
      });
      setTimeout(() => {
        if (this.indicator.defaultId === null) {
          this.getJsonPath(index).enable();
          if (dirty) {
            this.getJsonPath(index).markAsDirty();
          }
        }
        indicatorPath.get('result').setErrors(null);
        if (typeof result === 'string' || typeof result === 'number') {
          result = Number(result);
          if (result !== Number.NaN) {
            indicatorPath.get('result').setValue(result);
          } else {
            indicatorPath.get('result').setValue(0);
          }
        } else {
          indicatorPath.get('result').setValue(0);
        }
      }, 500);
    }, error => {
      setTimeout(() => {
        if (this.indicator.defaultId === null) {
          this.getJsonPath(index).enable();
          if (dirty) {
            this.getJsonPath(index).markAsDirty();
          }
        }
        indicatorPath.get('result').setErrors(null);
        indicatorPath.get('result').setValue(0);
      }, 500);
    }));
  }

  public getJsonPath(index: number): UntypedFormArray {
    return this.numberIndicatorPaths.at(index).get('jsonPath') as UntypedFormArray;
  }

  public getCurrentJsonPath(index: number): string[] {
    return this.section.indicators[this.index].indicatorPaths[index].jsonPath;
  }

  public getParameters(index: number, type: IndicatorType = 'chart'): UntypedFormArray {
    if (type === 'chart') {
      return this.chartIndicatorPaths.at(index).get('parameters') as UntypedFormArray;
    } else {
      return this.numberIndicatorPaths.at(index).get('parameters') as UntypedFormArray;
    }
  }

  public getParameter(index: number, key: string, type: IndicatorType = 'chart'): UntypedFormControl {
    return this.getParameters(index, type).controls.filter(control => control.value.key === key)[0] as UntypedFormControl;
  }

  private getSecureUrlByStakeHolder(indicatorPath: IndicatorPath) {
    return this.sanitizer.bypassSecurityTrustResourceUrl(
        this.indicatorUtils.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)));
  }

  private getUrlByStakeHolder(indicatorPath: IndicatorPath) {
    return this.indicatorUtils.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath));
  }

  public addNumberIndicatorPath(url: string = '', parameters: UntypedFormArray = new UntypedFormArray([]), source: string = 'stats-tool', jsonPath: UntypedFormArray = new UntypedFormArray([]), format: Format = "NUMBER") {
    if (jsonPath.length === 0) {
      jsonPath.push(this.fb.control('', Validators.required));
    }
    this.numberIndicatorPaths.push(this.fb.group({
          url: this.fb.control(url, [Validators.required, StringUtils.urlValidator()]),
          jsonPath: jsonPath,
          result: this.fb.control(0, Validators.required),
          source: this.fb.control(source, Validators.required),
          parameters: parameters,
          format: this.fb.control(format, Validators.required)
        }
    ));
    let index = this.numberIndicatorPaths.length - 1;
    if (this.numberIndicatorPaths.at(index).get('url').valid) {
      this.validateJsonPath(index);
    }
    if (this.indicator.defaultId === null) {
      this.subscriptions.push(this.numberIndicatorPaths.at(index).get('url').valueChanges.subscribe(value => {
            this.numberIndicatorPaths.at(index).get('result').setValue(null);
            if (this.numberIndicatorPaths.at(index).get('url').valid) {
              let indicatorPath: IndicatorPath = this.indicatorUtils.generateIndicatorByNumberUrl(this.indicatorUtils.getNumberSource(value), value, this.stakeholder, this.numberIndicatorPaths.at(index).get('jsonPath').value, this.indicatorUtils.numberSources.get(this.indicatorUtils.getNumberSource(value)));
              if (this.indicator.indicatorPaths[index]) {
                this.indicator.indicatorPaths[index] = indicatorPath;
              } else {
                this.indicator.indicatorPaths.push(indicatorPath);
              }
              if (indicatorPath.source) {
                this.numberIndicatorPaths.at(index).get('source').setValue(indicatorPath.source);
              }
              (this.numberIndicatorPaths.at(index) as UntypedFormGroup).setControl('parameters', this.getParametersAsFormArray(indicatorPath));
              if (indicatorPath.jsonPath.length > 1 && this.getJsonPath(index).length == 1) {
                let paths = indicatorPath.jsonPath;
                for (let i = 0; i < paths.length; i++) {
                  if (i == this.getJsonPath(index).length) {
                    this.getJsonPath(index).push(this.fb.control('', Validators.required));
                  }
                }
                this.getJsonPath(index).setValue(paths)
              }
            }
          })
      );

      this.subscriptions.push(this.numberIndicatorPaths.at(index).get('jsonPath').valueChanges.subscribe(value => {
            if (this.indicator.indicatorPaths[index]) {
              this.indicator.indicatorPaths[index].jsonPath = value;
            }
            this.numberIndicatorPaths.at(index).get('result').setValue(null);
          })
      );
      this.subscriptions.push(this.numberIndicatorPaths.at(index).get('source').valueChanges.subscribe(value => {
            if (this.indicator.indicatorPaths[index]) {
              this.indicator.indicatorPaths[index].source = value;
            }
          })
      );
    } else {
      this.numberIndicatorPaths.at(index).get('url').disable();
      this.numberIndicatorPaths.at(index).get('jsonPath').disable();
      this.numberIndicatorPaths.at(index).get('source').disable();
    }
  }

  public addChartIndicatorPath(value: string = '', parameters: UntypedFormArray = new UntypedFormArray([]), disableUrl: boolean = false, type: string = null) {
    this.chartIndicatorPaths.push(this.fb.group({
          url: this.fb.control(value, [Validators.required, StringUtils.urlValidator()]),
          parameters: parameters,
          type: this.fb.control(type)
        }
    ));
    let index = this.chartIndicatorPaths.length - 1;
    if (disableUrl) {
      this.chartIndicatorPaths.at(index).get('url').disable();
    } else {
      this.urlSubscriptions.push(this.chartIndicatorPaths.at(index).get('url').valueChanges.subscribe(value => {
        if (this.chartIndicatorPaths.at(index).get('url').valid) {
          let indicatorPath: IndicatorPath = this.indicatorUtils.generateIndicatorByChartUrl(this.indicatorUtils.getChartSource(value), value, this.chartIndicatorPaths.at(index).get('type').value, this.stakeholder);
          (this.chartIndicatorPaths.at(index) as UntypedFormGroup).get('type').setValue(indicatorPath.type);
          (this.chartIndicatorPaths.at(index) as UntypedFormGroup).setControl('parameters', this.getParametersAsFormArray(indicatorPath));
          if (!this.indicator.indicatorPaths[index]) {
            this.indicator.indicatorPaths[index] = indicatorPath;
            this.indicator.indicatorPaths[index].safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
          } else {
            indicatorPath.safeResourceUrl = this.indicator.indicatorPaths[index].safeResourceUrl;
            this.indicator.indicatorPaths[index] = indicatorPath;
          }
        }
      }));
    }
  }

  public removeNumberIndicatorPath(index: number) {
    this.numberIndicatorPaths.removeAt(index);
    this.indicator.indicatorPaths.splice(index, 1);
    this.transitionGroup.init();
    if (this.indicator.activePath === index) {
      this.activeNumberIndicatorPath(Math.max(0, index - 1));
    } else if (this.indicator.activePath > index) {
      this.activeNumberIndicatorPath(this.indicator.activePath - 1);
    }
    this.numberIndicatorFb.markAsDirty();
  }

  public removeChartIndicatorPath(index: number) {
    this.chartIndicatorPaths.removeAt(index);
    this.indicator.indicatorPaths.splice(index, 1);
    this.transitionGroup.init();
    if (this.indicator.activePath === index) {
      this.activeChartIndicatorPath(Math.max(0, index - 1));
    } else if (this.indicator.activePath > index) {
      this.activeChartIndicatorPath(this.indicator.activePath - 1);
    }
    this.chartIndicatorFb.markAsDirty();
  }

  public moveIndicatorPath(form: FormGroup,
                           indicatorPaths: FormArray, index: number,
                           newIndex: number = index - 1) {
    this.transitionGroup.init();
    let a = indicatorPaths.at(index);
    let b = indicatorPaths.at(newIndex);
    indicatorPaths.setControl(index, b);
    indicatorPaths.setControl(newIndex, a);
    HelperFunctions.swap(this.indicator.indicatorPaths, index, newIndex);
    if (this.indicator.activePath === index) {
      this.indicator.activePath = newIndex;
    } else if (this.indicator.activePath === newIndex) {
      this.indicator.activePath = index;
    }
    form.markAsDirty();
  }

  public activeNumberIndicatorPath(index: number) {
    let paths = this.numberIndicatorPaths;
    if (index == paths.length) {
      this.addNumberIndicatorPath();
      this.transitionGroup.init();
    }
    this.indicator.activePath = index;
  }

  public activeChartIndicatorPath(index: number) {
    let paths = this.chartIndicatorPaths;
    if (index == paths.length) {
      this.addChartIndicatorPath();
      this.transitionGroup.init();
    }
    this.indicator.activePath = index;
  }

  private getJsonPathAsFormArray(indicatorPath: IndicatorPath): UntypedFormArray {
    let jsonPath = this.fb.array([]);
    if (indicatorPath.jsonPath) {
      indicatorPath.jsonPath.forEach(path => {
        jsonPath.push(this.fb.control(path, Validators.required));
      });
    }
    return jsonPath;
  }

  private getParametersAsFormArray(indicatorPath: IndicatorPath): UntypedFormArray {
    let parameters = this.fb.array([]);
    if (indicatorPath.parameters) {
      if(!indicatorPath.parameters.statsProfile) {
        indicatorPath.parameters.statsProfile = null;
      }
      Object.keys(indicatorPath.parameters).forEach(key => {
        if (this.indicatorUtils.ignoredParameters.indexOf(key) === -1) {
          if (this.indicatorUtils.parametersValidators.has(key)) {
            parameters.push(this.fb.group({
              key: this.fb.control(key),
              value: this.fb.control(indicatorPath.parameters[key], this.indicatorUtils.parametersValidators.get(key))
            }));
          } else {
            parameters.push(this.fb.group({
              key: this.fb.control(key),
              value: this.fb.control(indicatorPath.parameters[key])
            }));
          }
        }
      });
    }
    return parameters;
  }

  public editNumberIndicatorOpen(section: Section, id = null) {
    this.editNumberModal.cancelButtonText = 'Cancel';
    this.editNumberModal.okButtonLeft = false;
    this.editNumberModal.alertMessage = false;
    if (this.index === -1) {
      this.editNumberModal.alertTitle = 'Create a new number indicator';
      this.editNumberModal.okButtonText = 'Save';
      this.notification = NotificationUtils.createIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
      this.editNumberNotify.reset(this.notification.message);
    } else {
      this.editNumberModal.alertTitle = 'Edit number indicator\'s information';
      this.editNumberModal.okButtonText = 'Save Changes';
      this.notification = NotificationUtils.editIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
      this.editNumberNotify.reset(this.notification.message);
    }
    this.editNumberModal.stayOpen = true;
    this.section = section;
    this.index = (id) ? section.indicators.findIndex(value => value._id === id) : -1;
    if (this.index !== -1) {
      this.indicator = HelperFunctions.copy(this.section.indicators[this.index]);
      this.numberIndicatorFb = this.fb.group({
        _id: this.fb.control(this.indicator._id),
        name: this.fb.control(this.indicator.name, Validators.required),
        description: this.fb.control(this.indicator.description),
        creationDate: this.fb.control(this.indicator.creationDate),
        additionalDescription: this.fb.control(this.indicator.additionalDescription),
        visibility: this.fb.control(this.indicator.visibility),
        indicatorPaths: this.fb.array([], Validators.required),
        type: this.fb.control(this.indicator.type),
        width: this.fb.control(this.indicator.width),
        height: this.fb.control(this.indicator.height),
        defaultId: this.fb.control(this.indicator.defaultId)
      });
      this.indicator.indicatorPaths.forEach(indicatorPath => {
        this.addNumberIndicatorPath(this.indicatorUtils.getNumberUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)), this.getParametersAsFormArray(indicatorPath), indicatorPath.source, this.getJsonPathAsFormArray(indicatorPath), indicatorPath.format);
      });
    } else {
      this.indicator = new Indicator('', '', '', 'number', 'small', 'small', "PUBLIC", []);
      this.numberIndicatorFb = this.fb.group({
        _id: this.fb.control(this.indicator._id),
        name: this.fb.control(this.indicator.name, Validators.required),
        description: this.fb.control(this.indicator.description),
        additionalDescription: this.fb.control(this.indicator.additionalDescription),
        visibility: this.fb.control(this.indicator.visibility),
        indicatorPaths: this.fb.array([], Validators.required),
        type: this.fb.control(this.indicator.type),
        width: this.fb.control(this.indicator.width),
        height: this.fb.control(this.indicator.height),
        defaultId: this.fb.control(this.indicator.defaultId)
      });
      this.addNumberIndicatorPath();
    }
    if (this.indicator.defaultId) {
      setTimeout(() => {
        this.numberIndicatorFb.get('description').disable();
      }, 0);
    }
    this.editNumberModal.open();
  }

  public editChartIndicatorOpen(section: Section, id = null) {
    this.editChartModal.cancelButtonText = 'Cancel';
    this.editChartModal.okButtonLeft = false;
    this.editChartModal.alertMessage = false;
    if (this.index === -1) {
      this.editChartModal.alertTitle = 'Create a new chart indicator';
      this.editChartModal.okButtonText = 'Save';
      this.notification = NotificationUtils.createIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
      this.editChartNotify.reset(this.notification.message);
    } else {
      this.editChartModal.alertTitle = 'Edit chart indicator\'s information';
      this.editChartModal.okButtonText = 'Save Changes';
      this.notification = NotificationUtils.editIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
      ;
      this.editChartNotify.reset(this.notification.message);
    }
    this.editChartModal.stayOpen = true;
    this.urlSubscriptions.forEach(value => {
      if (value instanceof Subscriber) {
        value.unsubscribe();
      }
    });
    this.section = section;
    this.index = (id) ? section.indicators.findIndex(value => value._id === id) : -1;
    if (this.index !== -1) {
      this.indicator = HelperFunctions.copy(this.section.indicators[this.index]);
      this.chartIndicatorFb = this.fb.group({
        _id: this.fb.control(this.indicator._id),
        name: this.fb.control(this.indicator.name),
        creationDate: this.fb.control(this.indicator.creationDate),
        description: this.fb.control(this.indicator.description),
        additionalDescription: this.fb.control(this.indicator.additionalDescription),
        visibility: this.fb.control(this.indicator.visibility),
        indicatorPaths: this.fb.array([]),
        width: this.fb.control(this.indicator.width),
        height: this.fb.control(this.indicator.height),
        defaultId: this.fb.control(this.indicator.defaultId)
      });
      this.indicator.indicatorPaths.forEach(indicatorPath => {
        this.addChartIndicatorPath(this.getUrlByStakeHolder(indicatorPath),
            this.getParametersAsFormArray(indicatorPath), this.indicator.defaultId !== null, indicatorPath.type);
        indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
      });
    } else {
      this.indicator = new Indicator('', '', '', 'chart', 'medium', 'medium', "PUBLIC", []);
      this.chartIndicatorFb = this.fb.group({
        _id: this.fb.control(this.indicator._id),
        name: this.fb.control(this.indicator.name),
        description: this.fb.control(this.indicator.description),
        additionalDescription: this.fb.control(this.indicator.additionalDescription),
        visibility: this.fb.control(this.indicator.visibility),
        indicatorPaths: this.fb.array([]),
        width: this.fb.control(this.indicator.width, Validators.required),
        height: this.fb.control(this.indicator.height, Validators.required),
        defaultId: this.fb.control(this.indicator.defaultId)
      });
      this.addChartIndicatorPath();
    }
    if (this.indicator.defaultId) {
      setTimeout(() => {
        this.chartIndicatorFb.get('description').disable();
      }, 0);
    }
    this.editChartModal.open();
  }

  saveIndicator() {
    this.editing = true;
    if (this.indicator.type === 'chart') {
      this.chartIndicatorFb.get('description').enable();
      this.indicator = this.indicatorUtils.generateIndicatorByForm(this.chartIndicatorFb.value, this.indicator.indicatorPaths, this.indicator.type);
      this.section = this.charts.find(section => section._id === this.section._id);
    } else {
      this.numberIndicatorFb.get('description').enable();
      this.indicator = this.indicatorUtils.generateIndicatorByForm(this.numberIndicatorFb.value, this.indicator.indicatorPaths, this.indicator.type);
      this.section = this.numbers.find(section => section._id === this.section._id);
    }
    let path = [
      this.stakeholder._id,
      this.stakeholder.topics[this.topicIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
      this.section._id
    ];
    this.subscriptions.push(this.stakeholderService.saveElement(this.properties.monitorServiceAPIURL, this.indicator, path).subscribe(indicator => {
      if (this.index !== -1) {
        this.section.indicators[this.index] = indicator;
      } else {
        this.section.indicators.push(indicator);
      }
      this.notification.entity = indicator._id;
      this.notification.stakeholder = this.stakeholder.alias;
      this.notification.stakeholderType = this.stakeholder.type;
      this.notification.groups = [Role.curator(this.stakeholder.type)];
      if (this.stakeholder.defaultId) {
        this.notification.groups.push(Role.manager(this.stakeholder.type, this.stakeholder.alias));
        if (this.indicator.type === "chart") {
          this.setCharts();
          this.chartIndicatorFb = null;
          this.editChartNotify.sendNotification(this.notification);
        } else {
          this.setNumbers();
          this.numberIndicatorFb = null;
          this.editNumberNotify.sendNotification(this.notification);
        }
      } else {
        this.stakeholderService.getStakeholders(this.properties.monitorServiceAPIURL, null, this.stakeholder._id).subscribe(stakeholders => {
          stakeholders.forEach(value => {
            this.notification.groups.push(Role.manager(value.type, value.alias))
          });
          if (this.indicator.type === "chart") {
            this.setCharts();
            this.chartIndicatorFb = null;
            this.editChartNotify.sendNotification(this.notification);
          } else {
            this.setNumbers();
            this.numberIndicatorFb = null;
            this.editNumberNotify.sendNotification(this.notification);
          }
        });
      }
      UIkit.notification('Indicator has been <b>successfully saved</b>', {
        status: 'success',
        timeout: 6000,
        pos: 'bottom-right'
      });
      this.editing = false;
      if (this.indicator.type === "chart") {
        this.editChartModal.cancel();
      } else {
        this.editNumberModal.cancel();
      }
    }, error => {
      this.chartIndicatorFb = null;
      UIkit.notification(error.error.message, {
        status: 'danger',
        timeout: 6000,
        pos: 'bottom-right'
      });
      this.editing = false;
      if (this.indicator.type === "chart") {
        this.editChartModal.cancel();
      } else {
        this.editNumberModal.cancel();
      }
    }));
  }

  saveIndicators(sections) {
    this.editing = true;
    let path = [
      this.stakeholder._id,
      this.stakeholder.topics[this.topicIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index]._id
    ];
    this.subscriptions.push(this.stakeholderService.saveBulkElements(this.properties.monitorServiceAPIURL, sections, path).subscribe(stakeholder => {
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].charts = stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].charts;
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].numbers = stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].numbers;
      this.setCharts();
      this.setNumbers();
      this.initReorder();
      if (properties.notificationsAPIURL) {
        this.notification = NotificationUtils.importIndicators(this.user.fullname, this.stakeholder.alias);
        this.notification.entity = this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index]._id;
        this.notification.name = this.user.firstname;
        this.notification.surname = this.user.lastname;
        this.notification.stakeholder = this.stakeholder.alias;
        this.notification.stakeholderType = this.stakeholder.type;
        this.notification.groups = [Role.curator(this.stakeholder.type)];
        if (this.stakeholder.defaultId) {
          this.notification.groups.push(Role.manager(this.stakeholder.type, this.stakeholder.alias));
          this.notificationService.sendNotification(this.notification).subscribe(notification => {
            UIkit.notification('A notification has been <b>sent</b> successfully', {
              status: 'success',
              timeout: 6000,
              pos: 'bottom-right'
            });
          }, error => {
            UIkit.notification('An error has occurred. Please try again later', {
              status: 'danger',
              timeout: 6000,
              pos: 'bottom-right'
            });
          });
        } else {
          this.stakeholderService.getStakeholders(this.properties.monitorServiceAPIURL, null, this.stakeholder._id).subscribe(stakeholders => {
            stakeholders.forEach(value => {
              this.notification.groups.push(Role.manager(value.type, value.alias))
            });

            this.notificationService.sendNotification(this.notification).subscribe(notification => {
              NotificationHandler.rise('A notification has been <b>sent</b> successfully');
            }, error => {
              NotificationHandler.rise('An error has occurred. Please try again later', 'danger');
            });
          });
        }
      }
      this.finish();
      NotificationHandler.rise('Indicators have been <b>imported</b> successfully!');
    }, error => {
      this.chartIndicatorFb = null;
      NotificationHandler.rise('An error has occurred. Please try again later', 'danger');
      this.finish();
    }));


  }

  finish() {
    this.editing = false;
    this.loading = false;
  }

  moveIndicator(moveIndicator: MoveIndicator) {
    this.editing = true;
    let path = [
      this.stakeholder._id,
      this.stakeholder.topics[this.topicIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id
    ];
    this.subscriptions.push(this.stakeholderService.moveIndicator(this.properties.monitorServiceAPIURL, path, moveIndicator).subscribe(subCategory => {
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex] = subCategory;
      this.setCharts();
      this.setNumbers();
      this.editing = false;
    }));
  }

  reorderIndicators(sectionId: string, type: IndicatorType, indicators: string[]) {
    this.editing = true;
    let path = [
      this.stakeholder._id,
      this.stakeholder.topics[this.topicIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
      sectionId
    ];
    this.subscriptions.push(this.stakeholderService.reorderIndicators(this.properties.monitorServiceAPIURL, path, indicators).subscribe(indicators => {
      if (type === 'chart') {
        this.charts.find(section => section._id === sectionId).indicators = indicators;
        this.setCharts();
      } else {
        this.numbers.find(section => section._id === sectionId).indicators = indicators;
        this.setNumbers();
      }
      this.editing = false;
    }));
  }

  hasDifference(index: number, type: IndicatorType = 'chart'): boolean {
    let hasDifference = false;
    if (type === 'chart') {
      this.chartIndicatorPaths.at(index).value.parameters.forEach(parameter => {
        if (parameter.value !== this.indicator.indicatorPaths[index].parameters[parameter.key]) {
          hasDifference = true;
          return;
        }
      });
      return hasDifference || this.indicator.indicatorPaths[index].safeResourceUrl.toString() !==
          this.getSecureUrlByStakeHolder(this.indicator.indicatorPaths[index]).toString();
    } else if (type === 'number') {
      let indicatorPath = this.numberIndicatorPaths.at(index).value;
      indicatorPath.parameters.forEach(parameter => {
        if (parameter.value !== this.indicator.indicatorPaths[index].parameters[parameter.key]) {
          hasDifference = true;
          return;
        }
      });
    }
    return hasDifference;
  }

  public get isAdministrator(): boolean {
    return Session.isPortalAdministrator(this.user);
  }

  public get isCurator(): boolean {
    return this.isAdministrator || Session.isCurator(this.stakeholder.type, this.user);
  }

  refreshIndicator(type: IndicatorType = 'chart') {
    if (type === 'chart') {
      this.indicator = this.indicatorUtils.generateIndicatorByForm(this.chartIndicatorFb.value, this.indicator.indicatorPaths, 'chart');
      this.indicator.indicatorPaths.forEach(indicatorPath => {
        indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
      });
    } else if (type === 'number') {
      this.indicator = this.indicatorUtils.generateIndicatorByForm(this.numberIndicatorFb.value, this.indicator.indicatorPaths, 'number');
      this.indicator.indicatorPaths.forEach((indicatorPath, index) => {
        this.validateJsonPath(index);
      });
    }
  }

  deleteIndicatorOpen(section: Section, indicatorId: string, type: string, childrenAction: string = null) {
    this.indicatorChildrenActionOnDelete = null;
    if (childrenAction == "delete") {
      this.indicatorChildrenActionOnDelete = childrenAction;
    } else if (childrenAction == "disconnect") {
      this.indicatorChildrenActionOnDelete = childrenAction;
    }

    this.section = section;
    if (type === 'chart') {
      this.index = this.charts.find(value => value._id == section._id).indicators.findIndex(value => value._id == indicatorId);
    } else {
      this.index = this.numbers.find(value => value._id == section._id).indicators.findIndex(value => value._id == indicatorId);
    }
    this.indicator = section.indicators.find(value => value._id == indicatorId);
    this.deleteModal.alertTitle = 'Delete indicator';
    this.deleteModal.cancelButtonText = 'No';
    this.deleteModal.okButtonText = 'Yes';
    this.notification = NotificationUtils.deleteIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
    this.deleteNotify.reset(this.notification.message);
    this.deleteModal.stayOpen = true;
    this.deleteModal.open();
  }

  deleteIndicator() {
    this.editing = true;
    let path = [
      this.stakeholder._id,
      this.stakeholder.topics[this.topicIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
      this.section._id,
      this.indicator._id
    ];
    this.subscriptions.push(this.stakeholderService.deleteElement(this.properties.monitorServiceAPIURL, path, this.indicatorChildrenActionOnDelete).subscribe(() => {
      if (this.indicator.type === 'chart') {
        this.charts.find(section => section._id === this.section._id).indicators.splice(this.index, 1);
        this.setCharts();
      } else {
        this.numbers.find(section => section._id === this.section._id).indicators.splice(this.index, 1);
        this.setNumbers();
      }
      UIkit.notification('Indicator has been <b>successfully deleted</b>', {
        status: 'success',
        timeout: 6000,
        pos: 'bottom-right'
      });
      this.notification.entity = this.indicator._id;
      this.notification.stakeholder = this.stakeholder.alias;
      this.notification.stakeholderType = this.stakeholder.type;
      this.notification.groups = [Role.curator(this.stakeholder.type)];
      if (this.stakeholder.defaultId) {
        this.notification.groups.push(Role.manager(this.stakeholder.type, this.stakeholder.alias));
        this.deleteNotify.sendNotification(this.notification);
      } else {
        this.stakeholderService.getStakeholders(this.properties.monitorServiceAPIURL, null, this.stakeholder._id).subscribe(stakeholders => {
          stakeholders.forEach(value => {
            this.notification.groups.push(Role.manager(value.type, value.alias))
          });
          this.deleteNotify.sendNotification(this.notification);
        });
      }
      this.editing = false;
      this.deleteModal.cancel();
    }, error => {
      UIkit.notification(error.error.message, {
        status: 'danger',
        timeout: 6000,
        pos: 'bottom-right'
      });
      this.editing = false;
      this.deleteModal.cancel();
    }));
  }

  changeIndicatorStatus(sectionId: string, indicator: Indicator, visibility: Visibility) {
    this.editing = true;
    let path = [
      this.stakeholder._id,
      this.stakeholder.topics[this.topicIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
      sectionId,
      indicator._id
    ];
    this.subscriptions.push(this.stakeholderService.changeVisibility(this.properties.monitorServiceAPIURL, path, visibility).subscribe(returnedElement => {
      indicator.visibility = returnedElement.visibility;
      UIkit.notification('Indicator has been <b>successfully changed</b> to ' + indicator.visibility.toLowerCase(), {
        status: 'success',
        timeout: 6000,
        pos: 'bottom-right'
      });
      this.editing = false;
    }, error => {
      UIkit.notification('An error has been occurred. Try again later', {
        status: 'danger',
        timeout: 6000,
        pos: 'bottom-right'
      });
      this.editing = false;
    }));
  }

  saveSection(focused: boolean, sectionControl: AbstractControl, index: number, type: IndicatorType = "chart") {
    if (!focused && sectionControl.dirty) {
      this.editing = true;
      let path = [
        this.stakeholder._id,
        this.stakeholder.topics[this.topicIndex]._id,
        this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
        this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id
      ];
      this.subscriptions.push(this.stakeholderService.saveSection(this.properties.monitorServiceAPIURL, sectionControl.value, path, index).subscribe(section => {
        if (type === 'chart') {
          this.charts[index] = section;
          this.setCharts();
        } else {
          this.numbers[index] = section;
          this.setNumbers();
        }
        this.initReorder();
        UIkit.notification('Section has been <b>successfully saved</b>', {
          status: 'success',
          timeout: 6000,
          pos: 'bottom-right'
        });
        this.editing = false;
      }, error => {
        UIkit.notification(error.error.message, {
          status: 'danger',
          timeout: 6000,
          pos: 'bottom-right'
        });
        this.editing = false;
      }));
    }
  }

  createSection(index = -1, type: IndicatorType = 'chart') {
    this.editing = true;
    this.section = new Section(type, null, null, this.stakeholder.alias);
    let path = [
      this.stakeholder._id,
      this.stakeholder.topics[this.topicIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id
    ];
    this.subscriptions.push(this.stakeholderService.saveSection(this.properties.monitorServiceAPIURL, this.section, path, index).subscribe(section => {
      if (type === 'chart') {
        if (index !== -1) {
          this.charts.splice(index, 0, section);
        } else {
          this.charts.push(section);
        }
        this.setCharts();
      } else {
        if (index !== -1) {
          this.numbers.splice(index, 0, section);
        } else {
          this.numbers.push(section);
        }
        this.setNumbers();
      }
      this.initReorder();
      UIkit.notification('Section has been <b>successfully created</b>', {
        status: 'success',
        timeout: 6000,
        pos: 'bottom-right'
      });
      this.editing = false;
    }, error => {
      UIkit.notification(error.error.message, {
        status: 'danger',
        timeout: 6000,
        pos: 'bottom-right'
      });
      this.editing = false;
    }));
  }

  deleteSectionOpen(section: Section, index: number, type: IndicatorType, childrenAction: string = null) {
    if (!this.editing && !section.defaultId) {
      this.sectionTypeToDelete = type;
      this.sectionChildrenActionOnDelete = null;
      if (childrenAction == "delete") {
        this.sectionChildrenActionOnDelete = childrenAction;
      } else if (childrenAction == "disconnect") {
        this.sectionChildrenActionOnDelete = childrenAction;
      }

      this.section = section;
      this.index = index;
      this.deleteSectionModal.alertTitle = 'Delete Section';
      this.deleteSectionModal.cancelButtonText = 'No';
      this.deleteSectionModal.okButtonText = 'Yes';
      this.deleteSectionModal.stayOpen = true;
      this.deleteSectionModal.open();
    }
  }

  deleteSection() {
    this.editing = true;
    let path = [
      this.stakeholder._id,
      this.stakeholder.topics[this.topicIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
      this.section._id
    ];
    this.subscriptions.push(this.stakeholderService.deleteElement(this.properties.monitorServiceAPIURL, path, this.sectionChildrenActionOnDelete).subscribe(() => {
      if (this.sectionTypeToDelete === "chart") {
        this.charts.splice(this.index, 1);
        this.setCharts();
      } else {
        this.numbers.splice(this.index, 1);
        this.setNumbers();
      }
      this.initReorder();
      UIkit.notification('Section has been <b>successfully deleted</b>', {
        status: 'success',
        timeout: 6000,
        pos: 'bottom-right'
      });
      this.editing = false;
      this.deleteSectionModal.cancel();
    }, error => {
      UIkit.notification(error.error.message, {
        status: 'danger',
        timeout: 6000,
        pos: 'bottom-right'
      });
      this.editing = false;
      this.deleteSectionModal.cancel();
    }));
  }

  migrateFromOldImportJsonFile(charts) {
    // first section contains numbers
    // second contains charts
    let hasNumbers = false;
    for (let chart of charts) {
      if (chart['type'] == 'number') {
        hasNumbers = true;
        break;
      }
    }
    let chartsSection = (hasNumbers ? 1 : 0); // no numbers: first sections contains charts
    for (let chart of charts) {
      if (chart['sectionIndex'] == null) {
        chart['sectionIndex'] = chart['type'] == 'chart' ? chartsSection : 0;
      }
      if (chart.url && chart.jsonPath) {
        chart.indicatorPaths = [{url: chart.url, jsonPath: chart.jsonPath}];
      } else if(chart.url) {
        chart.indicatorPaths = [{url: chart.url}];
      }
    }
    return charts;
  }

  importIndicatorsAndSave(stakeholder: Stakeholder, charts: any[]) {
    let sectionsToSave: Section[] = [];
    let countIndicators = 0;
    if (stakeholder.type !== this.stakeholder.type) {
      UIkit.notification("The type of this profile is not the same with the file's one!", {
        status: 'warning',
        timeout: 6000,
        pos: 'bottom-right'
      });
      this.finish();
      return;
    }
    // name description additionalDescription, height, width, visibility
    let duplicates = 0;
    charts = this.migrateFromOldImportJsonFile(charts);
    for (let chart of charts) {
      chart.visibility = this.showVisibility ? chart.visibility : this.stakeholderUtils.defaultValue(this.stakeholderUtils.visibilities);
      if (!sectionsToSave[chart['sectionIndex']]) {
        let sectionToSave = new Section(chart['sectionType'] ? chart['sectionType'] : chart['type'], chart['sectionTitle']);
        sectionToSave.indicators = [];
        sectionsToSave[chart['sectionIndex']] = sectionToSave;
      }
      let exists = false;
      let indicatorPaths: IndicatorPath[] = [];
      // validate indicators' schema from file
      let invalid_file_message;
      if (!chart.type) {
        invalid_file_message = "No indicator type is specified. Type should be chart or number.";
      } else if (chart.type != "chart" && chart.type != "number") {
        invalid_file_message = "Invalid indicator type. Type should be chart or number.";
      } else if (chart.indicatorPaths.length === 0) {
        invalid_file_message = "No indicator paths are specified."
      } else if (chart.type == "number" && chart.indicatorPaths.filter(path => !path.jsonPath).length > 0) {
        invalid_file_message = "No jsonPath is specified for number indicator."
      } else if (chart.indicatorPaths.filter(path => !path.url).length > 0) {
        invalid_file_message = "No indicator url is specified.";
      }

      if (invalid_file_message) {
        UIkit.notification(invalid_file_message, {
          status: 'danger',
          timeout: 6000,
          pos: 'bottom-right'
        });
        this.finish();
        break;
      }

      if (chart.type == "chart") {
        indicatorPaths = chart.indicatorPaths.map(path => this.indicatorUtils.generateIndicatorByChartUrl(this.indicatorUtils.getChartSource(path.url), path.url, chart.type, stakeholder));
        this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].charts.forEach((section: Section) => {
          section.indicators.forEach(indicator => {
            indicator.indicatorPaths.forEach(path => {
              let size = indicatorPaths.length;
              indicatorPaths = indicatorPaths.filter(indicatorPath => JSON.stringify(path.chartObject) !== JSON.stringify(indicatorPath.chartObject))
              if (indicatorPaths.length < size) {
                duplicates = duplicates + (size - indicatorPaths.length);
                exists = true;
              }
            });
          });
        });
      } else if (chart.type == "number") {
        indicatorPaths = chart.indicatorPaths.map(path =>
            this.indicatorUtils.generateIndicatorByNumberUrl(this.indicatorUtils.getNumberSource(path.url), path.url,
                stakeholder, path.jsonPath, this.indicatorUtils.numberSources.get(this.indicatorUtils.getNumberSource(path.url))));
        this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].numbers.forEach((section: Section) => {
          section.indicators.forEach(indicator => {
            indicator.indicatorPaths.forEach(path => {
              let size = indicatorPaths.length;
              indicatorPaths = indicatorPaths.filter(indicatorPath => JSON.stringify(path.chartObject) !== JSON.stringify(indicatorPath.chartObject))
              if (indicatorPaths.length < size) {
                duplicates = duplicates + (size - indicatorPaths.length);
                exists = true;
              }
            });
          });
        });
      }
      if (indicatorPaths.length > 0) {
        let i: Indicator = new Indicator(chart.name, chart.description, chart.additionalDescription, chart.type, chart.width, chart.height, this.showVisibility ? "RESTRICTED" : this.stakeholderUtils.defaultValue(this.stakeholderUtils.visibilities), indicatorPaths);
        sectionsToSave[chart['sectionIndex']].indicators.push(i);
        countIndicators++;
      }
    }
    if (duplicates > 0) {
      UIkit.notification(duplicates + " urls already exist and will not be imported!", {
        status: 'warning',
        timeout: 6000,
        pos: 'bottom-right'
      });
    }
    if (sectionsToSave.length > 0 && countIndicators > 0) {
      this.saveIndicators(sectionsToSave.filter(section => !!section));
    }
    if (sectionsToSave.length == 0 || countIndicators == 0) {
      UIkit.notification(" No urls imported!", {
        status: 'warning',
        timeout: 6000,
        pos: 'bottom-right'
      });
      this.finish();
    }
  }

  public exportIndicators(subcategoryIndex) {
    this.editing = true;
    let indicators = [];
    let index: number = 0;
    let indexIndicator: number = 0;
    this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[subcategoryIndex].numbers.forEach(section => {
      section.indicators.forEach(indicator => {
        indicators[indexIndicator] = {
          "indicatorPaths": indicator.indicatorPaths.map(path => {
            return {
              jsonPath: path.jsonPath,
              url: this.indicatorUtils.getNumberUrl(path.source, this.indicatorUtils.getFullUrl(this.stakeholder, path))
            }
          }),
          "type": indicator.type, "name": indicator.name,
          "description": indicator.description, "additionalDescription": indicator.additionalDescription,
          "visibility": indicator.visibility, "width": indicator.width, "height": indicator.height,
          "sectionTitle": section.title,
          "sectionType": section.type,
          "sectionIndex": index
        };
        indexIndicator++;
      });
      index++;
    });

    this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[subcategoryIndex].charts.forEach(section => {
      section.indicators.forEach(indicator => {
        indicators[indexIndicator] = {
          "indicatorPaths": indicator.indicatorPaths.map(path => {
            return {
              url: this.getUrlByStakeHolder(path)
            }
          }),
          "type": indicator.type, "name": indicator.name,
          "description": indicator.description, "additionalDescription": indicator.additionalDescription,
          "visibility": indicator.visibility, "width": indicator.width, "height": indicator.height,
          "sectionTitle": section.title,
          "sectionType": section.type,
          "sectionIndex": index
        };
        indexIndicator++;
      });
      index++;

    });

    let topic = this.stakeholder ? this.stakeholder.topics[this.topicIndex] : null;
    let category = topic ? topic.categories[this.categoryIndex] : null;
    let subCategory = category ? category.subCategories[subcategoryIndex] : null;
    let json = {
      stakeholder: HelperFunctions.copy(this.stakeholder),
      indicators: indicators
    }
    delete json.stakeholder.topics;
    var jsonFileUrl = window.URL.createObjectURL(new Blob([JSON.stringify(json)], {type: 'application/json'}));
    var a = window.document.createElement('a');
    window.document.body.appendChild(a);
    a.setAttribute('style', 'display: none');
    a.href = jsonFileUrl;
    a.download = this.stakeholder.alias + "_" + topic.alias + "_" + category.alias + "_" + subCategory.alias + ".json";
    a.click();
    window.URL.revokeObjectURL(jsonFileUrl);
    a.remove(); // remove the element
    this.finish();
  }

  fileChangeEvent(fileInput: any, index) {
    this.index = index;
    this.editing = true;
    this.loading = true;
    this.filesToUpload = <Array<File>>fileInput.target.files;
    this.upload();
  }

  upload() {
    if (this.filesToUpload.length == 0) {
      console.error("There is no selected file to upload.");
      UIkit.notification("There is no selected file to upload.", {
        status: 'danger',
        timeout: 6000,
        pos: 'bottom-right'
      });
      this.finish();
      return;
    } else {
      if (this.filesToUpload[0].name.indexOf(".json") == -1 || (this.filesToUpload[0].type != "application/json")) {
        console.error("No valid file type. The required type is JSON");
        UIkit.notification("No valid file type. The required type is JSON", {
          status: 'danger',
          timeout: 6000,
          pos: 'bottom-right'
        });
        this.finish();
        return;
      }
    }

    this.makeFileRequest(this.properties.utilsService + '/upload?type=json', [], this.filesToUpload).then(async (result: string) => {
      let json = JSON.parse(result);
      // validate file
      if (json && Array.isArray(json)) {
        UIkit.notification("This file is not supported any more. Please export indicators and try again!", {
          status: 'danger',
          timeout: 6000,
          pos: 'bottom-right'
        });
        this.finish();
      } else if (!json || json?.indicators.length == 0) {
        UIkit.notification("Importing file is empty", {
          status: 'danger',
          timeout: 6000,
          pos: 'bottom-right'
        });
        this.finish();
      } else {
        this.importIndicatorsAndSave(json.stakeholder, json.indicators);
      }
    }, (error) => {
      console.error("Error importing files", error);
      UIkit.notification("Error importing files", {
        status: 'danger',
        timeout: 6000,
        pos: 'bottom-right'
      });
      this.finish();
    });
  }

  makeFileRequest(url: string, params: Array<string>, files: Array<File>) {
    return new Promise((resolve, reject) => {
      const formData: any = new FormData();
      const xhr = new XMLHttpRequest();
      for (let i = 0; i < files.length; i++) {
        formData.append("uploads[]", files[i], files[i].name);
      }
      xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
          if (xhr.status == 200) {
            resolve(xhr.response);
          } else {
            reject(xhr.response);
          }
        }
      };
      xhr.open("POST", url, true);
      xhr.send(formData);
    });
  }

  copyToClipboard(value) {
    const tempBox = document.createElement('div');
    tempBox.id = 'tempBox';
    tempBox.style.position = 'fixed';
    tempBox.style.left = '0';
    tempBox.style.top = '0';
    tempBox.style.opacity = '0';
    tempBox.innerHTML = value.toString();
    document.body.appendChild(tempBox);
    copy.exec('tempBox');
    document.body.removeChild(tempBox);
    NotificationHandler.rise('Copied to clipboard');
  }
}
