import { Component, OnInit, ViewChild, Input, AfterViewInit,
    ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'
import { MatSlideToggle } from '@angular/material/slide-toggle'

import { Observable, of } from 'rxjs'
import { map, catchError } from 'rxjs/operators'

import { MatSelectChange } from '@angular/material/select'
import { MatSnackBar } from '@angular/material/snack-bar'

import { PlotlyComponent, PlotlyService } from 'angular-plotly.js'

import { DataService } from '../../_services/data.service'
import { FullInfo, InfoDataService, PlotDataParams } from '../info-data.service'
import { DownloadService, PlotParams } from '../../openapi'

@Component({
    selector: 'gisaf-info-plot',
    templateUrl: './plot.component.html',
    styleUrls: ['./plot.component.css'],
    providers: [
        DataService,
        PlotlyService,
    ],
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class InfoPlotComponent implements OnInit, AfterViewInit {
    routerItems: Object[] = []
    icon: string = ''
    hasPlot: boolean = false
    revision: number = 0

    rangeFrom: Date
    rangeTo: Date

    resampling: string = '0'
    value: string = ''
    comment: string = ''

    fontColor: string = 'white'

    useResizeHandler = true

    minY: number
    minEY: number
    maxY: number
    maxEY: number
    borderY: number

    plotStyle = {
        position: 'relative',
        height: '100%',
        width: '100%',
    }

    chartColor: string = 'blue'

    @Input() marginRatio: number = 0.1
    @ViewChild(MatSlideToggle) extendedRange: MatSlideToggle
    xRange = ['1968-02-28', '2099-12-31']

    @Input() source: FullInfo

    @Input() hasTools: boolean = true
    @Input() hasControls: boolean = true

    @ViewChild(PlotlyComponent, { static: true }) plot: PlotlyComponent

    @Input() plotXAxisType = 'date'
    @Input() plotTitle: string = ''
    @Input() plotLayout: object = {
        margin: {
            l: 50,
            r: 10,
            b: 30,
            t: 10,
            pad: 5
        },
        dragmode: 'pan',
        autosize: true,
        shapes: [],
        //showlegend: true,
    }

    plotlyConfig = {
        displaylogo: false,
        scrollZoom: true,
        showLink: false,
        modeBarButtonsToRemove: ['sendDataToCloud', 'select2d', 'lasso2d'],
        responsive: true,

        //plot_bgcolor: "rgba(0, 0, 0, 0)",
        //paper_bgcolor: "rgba(0,0,0,0)",
        toImageButtonOptions: {
            format: 'svg', // one of png, svg, jpeg, webp
            //filename: 'custom_image',
            height: null,
            width: null,
            //scale: 1 // Multiply title/legend/axis/canvas sizes by this factor
        }
    }
    plotData: object[] = []

    constructor(
        protected dataService: DataService,
        protected infoDataService: InfoDataService,
        public plotly: PlotlyService,
        public snackBar: MatSnackBar,
        private cdr: ChangeDetectorRef,
        public downloadService: DownloadService,
    ) {}

    ngOnInit() {
        let now = new Date()
        let lastYear = new Date()
        lastYear.setFullYear(now.getFullYear() - 1)
        this.rangeTo = now
        this.rangeFrom = lastYear

        this.plotLayout['xaxis'] = {
            type: this.plotXAxisType,
            range: [this.rangeFrom, this.rangeTo],
        }
        this.plotLayout['yaxis'] = {
            fixedrange: true,
            range: [-10, 10],
        }
        this.plotLayout['title'] = this.plotTitle
    }

    ngAfterViewInit() {
        this.infoDataService.dataProviderService$.subscribe(
            (fullInfo: FullInfo) => {
                this.source = fullInfo
                if (this.source.modelInfo.values.length > 0) {
                    // Eventually set the default value to plot as the first one
                    if (!this.source.modelInfo.values.map(v=>v.name).includes(this.value)) {
                        this.value = this.source.modelInfo.values[0].name
                    }
                    this.refreshGraph()
                }
                else {
                    this.createPlot([])
                }
            }
        )
    }

    getValues(): Observable<any> {
        return this.infoDataService.getPlotDataAndParams(
            this.source.modelInfo.store,
            this.source.featureInfo.id,
            this.value,
            this.resampling,
            //this.rangeFrom.toISOString(),
            //this.rangeTo.toISOString()
        ).pipe(
            map(
                (resp: PlotDataParams) => {
                    this.comment = resp.comment
                    this.createPlot(resp.data, resp.params)
                }
            ),
            catchError(
                err => {
                    // Switch to resampling if the server replies so
                    let message = err.statusText
                    let resampling = err['headers'].get('resampling')
                    if (resampling) {
                        this.resampling = resampling
                        this.refreshGraph()
                    }
                    this.snackBar.open(message, 'Close', {duration: 4000})
                    return of([])
                }
            )
        )
    }

    createPlot(data: Object[], plotParams?: PlotParams) {
        // Kept compatibility with previous API
        const values = data.map(d => d[this.value])
        this.plotData = []
        if (values.length > 0) {
            const valueInfo = this.source.modelInfo.values.find(v => v.name == this.value)

            // Add data

            const plotData = {
                x: data.map(d => d['time']),
                y: values,
                type: valueInfo.chartType || 'line',
                name: this.value,
                marker: {
                    color: valueInfo.chartColor || this.chartColor,
                    opacity: 1,
                    line: {
                        width: 0,
                    }
                },
            }
            if (plotParams?.barBase) {
                plotData['base'] = plotParams.barBase
                // Adjust the height of the bars
                plotData['y'] = plotData['y'].map(v => v - plotParams.barBase)
            }
            this.plotData.push(plotData)

            // Add plot "decorations" found in plotParams
            //const xRange = [data[0].time, data[data.length-1].time]
            plotParams?.baseLines.forEach(
                bl => {
                    this.plotData.push({
                        x: this.xRange,
                        y: [bl.value, bl.value],
                        mode: 'lines',
                        name: bl.name,
                        opacity: 0.5,
                        line: {
                            color: bl.color,
                            //width: 3
                            }
                    })
                }
            )
            this.plotLayout['shapes'] = plotParams?.bgShapes.map(
                shape => {
                    return {
                        type: 'rect',
                        xref: 'x',
                        yref: 'y',
                        x0: this.xRange[0],
                        y0: shape.valueTop,
                        x1: this.xRange[1],
                        y1: shape.valueBottom,
                        fillcolor: shape.color,
                        opacity: 1,
                        layer: 'below',
                        line: {
                            width: 0
                        }
                    }
                }
            )
            // Fake plot, just to add legend entries
            plotParams?.bgShapes.forEach(
                bl => {
                    this.plotData.push({
                        x: [0],
                        y: [0],
                        name: bl.name,
                        type: 'bar',
                        marker: {
                            color: bl.color,
                            opacity: 1,
                            line: {
                                width: 0,
                            }
                        },
                    })
                }
            )
            //this.plotLayout['shapes'] = []

            // Give some space around min and max values
            this.minY = Math.min.apply(Math, values)
            this.minEY = Math.min(
                Math.min.apply(Math, plotParams?.baseLines.map(bl => bl.value)),
                this.minY
            )
            this.maxY = Math.max.apply(Math, values)
            this.maxEY = Math.max(
                Math.max.apply(Math, plotParams?.baseLines.map(bl => bl.value)),
                this.maxY
            )
            this.borderY = (this.maxY - this.minY) * this.marginRatio
            this.adjustYRange()
            if (!this.plotLayout['yaxis']['title']) {
                this.plotLayout['yaxis']['title'] = {}
            }

            this.plotLayout['yaxis']['title']['text'] = valueInfo ? valueInfo.unit : ''
        }

        // FIXME: set yaxis plot layout parameters:
        //        bug in angular-plotly, relayout gives too much of a problem?
        this.cdr.markForCheck()
    }

    adjustYRange() {
        if (this.extendedRange.checked) {
            this.plotLayout['yaxis']['range'] = [this.minEY - this.borderY, this.maxEY + this.borderY]
        }
        else {
            this.plotLayout['yaxis']['range'] = [this.minY - this.borderY, this.maxY + this.borderY]
        }
        this.revision += 1
        this.cdr.markForCheck()
    }

    refreshGraph(evt?: MatSelectChange) {
        this.getValues().subscribe({
            next: data => {
                this.revision += 1
                this.cdr.markForCheck()
            },
            error: err => {
                this.snackBar.open(err.statusText, 'Close', {duration: 4000})
            }
        })
    }

    download() {
        // XXX: Ideally we should use openapi calls, but it's more simple to use window.open
        // this.downloadService.downloadCsvApiDownloadCsvStoreModelIdValueResampleGet({
        //     store: this.source.modelInfo.store,
        //     modelId: +this.source.featureInfo.id,
        //     value: this.value,
        //     resample: this.resampling
        // }).subscribe(
        //     data => {
        //         const blob = new Blob(data, { type: 'text/csv'})
        //         const blobUrl = URL.createObjectURL(blob)
        //         window.open(blobUrl)
        //     }
        // )
        window.open('/api/download/csv/' + this.source.modelInfo.store +
                    '/' + this.source.featureInfo.id +
                    '/' + this.value +
                    '/' + this.resampling)
    }
}
