import { Component, OnInit } from '@angular/core';
import {ChartOptions} from 'chart.js';
import {ApiService} from '../services/api.service';
import {animate, style, transition, trigger} from '@angular/animations';
import cloneDeep from 'lodash/cloneDeep';
import omit from 'lodash/omit';
import {DataService} from '../services/data.service';
import {ModelItemModel} from '../models/model-item.model';


@Component({
  selector: 'app-prediction',
  templateUrl: './prediction.component.html',
  styleUrls: ['./prediction.component.scss'],
  animations: [
    trigger(
      'inOut', [
        transition(':enter', [
          style({height: 0, opacity: 0}),
          animate('0.5s ease-out', style({height: '*', opacity: 1}))
        ]),
        transition(':leave', [
          style({height: '*', opacity: 1}),
          animate('0.5s ease-in', style({height: 0, opacity: 0}))
        ])
      ]
    )
  ]
})
export class PredictionComponent implements OnInit {

  result: {predictions: string[], mean: string};  // prediction result from python container
  loading = false;
  activeGroup: ModelItemModel = new ModelItemModel();
  activeModelIndex = 0;
  // if true, spectra belong to different samples, otherwise multiple spectra from the same sample are given
  multipleSamples = false;
  spectrumNames: string[] = [];
  spectrum: string[][] = [[]];
  flattenedSpectra = [];
  chart = [];
  readonly chartInit = {
    data: [],
    label: 'Spectrum',
    showLine: true,
    fill: false,
    pointRadius: 0,
    borderColor: '#00a8b0',
    borderWidth: 2
  };
  readonly chartOptions: ChartOptions = {
    scales: {
      xAxes: [{ticks: {min: 400, max: 4000, stepSize: 400, reverse: true}}],
      yAxes: [{ticks: {}}]
    },
    responsive: true,
    tooltips: {enabled: false},
    hover: {mode: null},
    maintainAspectRatio: true,
    plugins: {datalabels: {display: false}}
  };

  constructor(
    private api: ApiService,
    public d: DataService
  ) {
    this.chart[0] = cloneDeep(this.chartInit);
  }

  ngOnInit(): void {
    this.d.load('modelGroups', () => {
      this.activeGroup = this.d.arr.modelGroups[0];
    });
  }

  fileToArray(files) {
    this.loading = true;
    this.flattenedSpectra = [];
    this.chart = [];
    let load = files.length;
    this.spectrumNames = files.map(e => e.name);
    for (const i in files) {
      if (files.hasOwnProperty(i)) {
        const fileReader = new FileReader();
        fileReader.onload = () => {
          // parse to database spectrum representation
          this.spectrum = fileReader.result.toString().split('\r\n').map(e => e.split(',').map(el => parseFloat(el)))
            .filter(el => el.length === 2) as any;
          // flatten to format needed for prediction
          this.flattenedSpectra[i] = {labels: this.spectrum.map(e => e[0]), values: this.spectrum.map(e => e[1])};
          // add to chart
          this.chart[i] = cloneDeep(this.chartInit);
          this.chart[i].data = this.spectrum.map(e => ({x: parseFloat(e[0]), y: parseFloat(e[1])}));
          load --;
          if (load <= 0) {  // all loaded
            this.loadPrediction();
          }
        };
        fileReader.readAsText(files[i]);
      }
    }
  }

  loadPrediction() {
    this.loading = true;
    this.api.post<any>(this.activeGroup.models[this.activeModelIndex].url, this.flattenedSpectra, data => {
      this.result = {  // parse result into prediction and mean string
        predictions: Object.entries(omit(data, ['mean', 'std', 'label']))
          .map((p: any) => p[1].map(e => `${p[0]}: ${e} ${data.label[p[0]]}`))
          .reduce((s, e) => s.map((el, i) => this.clip(el) + ', ' + e[i])),
        mean: Object.keys(data.mean).map(e =>
            this.clip(`${e}: ${data.mean[e]} ${data.label[e]}`) +
            (data.std[e] !== '' ? ` (standard deviation: ${data.std[e]})` : '')
          ).join(', ')
      };
      this.loading = false;
    });
  }

  groupChange(index) {  // group was changed
    this.activeGroup = this.d.arr.modelGroups[index];
    this.activeModelIndex = 0;
    this.result = undefined;
  }

  clip(str) {  // clip spaces at start and end
    return str.replace(/^\s*(.*?)\s*$/, '$1');
  }
}
