// @ts-nocheck

import {Component, Input, OnDestroy, OnInit, ViewChild, NgZone} from "@angular/core";
import {ChartWidget, Series, SeriesMetadata, SeriesStyle} from "../models/ChartWidget";
import {DataService} from "../../../services/data.service";
import {MessageService} from "primeng/api";
import {WidgetService} from "../../../services/widget.service";

import {Subscription, forkJoin, interval} from "rxjs";

import { UtilsService } from 'src/app/services/utils.service';
import {ActivatedRoute, Params, Router} from "@angular/router";
//import {QueryStringParameterResolver} from "../../../services/parameter-resolvers/IParameterResolver";

import {ChartDataLocal} from "../models/ChartDataLocal";

import { ChartData, ChartConfiguration, ChartType} from 'chart.js/auto';

import { NgChartsModule } from 'ng2-charts';
import { BaseChartDirective } from 'ng2-charts';

import { Chart, ChartElement, ChartEvent, Element } from 'chart.js';

//import { Moment } from 'moment';
//import * as moment from "moment";
//import { DatasourceConfiguration } from "../../Widget";
import { Intervals } from "src/app/models/Parameter";
import zoomPlugin from 'chartjs-plugin-zoom';
Chart.register(zoomPlugin);


@Component({
selector: 'chart-widget',
templateUrl: './chart-widget.component.html',
styleUrls: ['./chart-widget.component.css']
})
export class ChartWidgetComponent implements OnInit, OnDestroy {
    
	
	@ViewChild(BaseChartDirective, { static: true }) public mychart: BaseChartDirective;
	@Input() widget?: ChartWidget;

	series: Series[] = [];
	newSeriesList: Series[] = [];
	debug = false;
    
	dataLocal: ChartDataLocal = new ChartDataLocal([],[],[],[]);
	
	data: ChartData = {
		datasets: [],
		labels : [],
	};
	responses: Response[] = [];

	public loadingData: boolean = false;
		
	subscriptions: Subscription[] = [];
   
	labels: string[] = [];
	
	options: ChartConfiguration['options'] = {};

	updatable: boolean = false;

	config = {
		type : 'bar' as ChartType,
		data: this.data,
		options: this.options
	  };

	validSeries: Series[] = [];
	
	intervals = Intervals.getIntervals();

	constructor(
			private widgetService: WidgetService,
			private dataService: DataService,
			private messageService: MessageService,
			private utils: UtilsService,
			private route: ActivatedRoute,
			private router: Router, 
			private zone: NgZone) {

	}

	ngOnInit(): void {
		//this.loadData();
		/**
		* In questo modo. il widget analizza prima la presenza di eventuali parametri quesrystring e poi
		* richiede al server remoto di ricevere i dati attraverso il comando loaddata();
		* Non e' pulito, pero' sembra funzionare.
		* Il sistema funziona SOLO per parametri che abbiano un valore numerico e non stringa. 
		* Da perfezionare. 
		*/
		this.route.queryParams.subscribe(params => {
			this.listenForConfigurationChanged(params);
		});
		this.setUpdatable();

	}

	
	
	
	ngOnDestroy() {
		for (const subscription of this.subscriptions)
		if (subscription)
		subscription.unsubscribe();
		this.subscriptions = [];
	}

	private setUpdatable(){
	   
		for (const series of this.validSeries) {
			for (let param of series.datasourceConfiguration?.parameters!){
				if(param.type === "any_date_choosen_by_user" || 
				param.type === "any_interval_choosen_by_user")  this.updatable = true; 
			}
		}
	}


	private loadData(){
		this.loadingData = true;

		let httpposts = [];
		
		this.validSeries = this.widget!.seriesList!.filter(o => !!o.datasourceConfiguration);
        
		for (const series of this.validSeries) {
			//console.log("getting data for: "  + JSON.stringify(series.datasourceConfiguration!));
			httpposts.push(this.dataService.getData(series.datasourceConfiguration!));
		}

		let forkJoinResult = forkJoin(httpposts).subscribe(result => {
			for (let res of result){
				let response = {
						"result": res?.result || [],
						"metadata": res?.metadata || {}
				};
				//console.log("response: "  + JSON.stringify(response));
				this.responses.push(response);
			}
					
			this.elaborateResponses();
			this.loadingData = false;
			this.chart?.update();
		});
		
	
	}

	updateData(){
		this.data.datasets = [];
		this.data.labels = [];
		this.dataLocal.datasets = [];
		this.dataLocal.labels = [];
		this.route.queryParams.subscribe(params => {
			this.listenForConfigurationChanged(params);
		});
	}

	listenForConfigurationChanged(params: Params) {  
		this.newSeriesList = [];
		this.data.datasets = [];
		this.data.labels = [];
		this.responses = [];

		this.series = this.widget!.seriesList!;
		for (var i in this.series){
			let serie = this.series[i];
			for (const param in params){
				for (var p in serie.datasourceConfiguration!.parameters){
					let parameter = serie.datasourceConfiguration!.parameters[p];
					if(parameter.type === "querystring" && parameter.name === param){
						parameter.value = params[param];
					}
				}
			}
			this.newSeriesList.push(serie);
		}
		this.widget!.seriesList = this.newSeriesList;
		this.debug ? this.createFakeData() : this.loadData();
	}

	public createFakeData (){
		this.utils.showInfoMessage("Creating fake data" );

		this.options = {
			"responsive": true,
			"scales": {
			  "x": {
				"title": {
				  "display": true,
				  "text": ""
				}
			  },
			  "y": {
				"title": {
				  "display": true,
				  "text": "W | watts"
				}
			  }
			},
			onClick: (e, elements) => {this.elaborateClick(e, elements)}
		  };
	
		/*	this.data = {
					  labels: [
						"00:00:00", "00:15:00", "00:30:00", "00:45:00", "01:00:00", "01:15:00", "01:30:00", "01:45:00", 
						"02:00:00", "02:15:00", "02:30:00", "02:45:00", "03:00:00", "03:15:00", "03:30:00", "03:45:00", 
						"04:00:00", "04:15:00", "04:30:00", "04:45:00", "05:00:00", "05:15:00", "05:30:00", "05:45:00", 
						"06:00:00", "06:15:00", "06:30:00", "06:45:00", "07:00:00", "07:15:00", "07:30:00", "07:45:00", 
						"08:00:00", "08:15:00", "08:30:00", "08:45:00", "09:00:00", "09:15:00", "09:30:00", "09:45:00", 
						"10:00:00", "10:15:00", "10:30:00", "10:45:00", "11:00:00", "11:15:00", "11:30:00", "11:45:00", 
						"12:00:00", "12:15:00", "12:30:00", "12:45:00"
					  ],
					  datasets: [{
					    type: 'bar',
					    label: 'Today average values in customisable intervals',
					    data: [ 274, 266, 217, 261, 152, 100, 156, 152, 146, 205, 247, 248, 89, 142, 143, 90, 149, 245, 
					              188, 191, 246, 241, 88, 144, 140, 88, 131, 221, 172, 205, 402, 1611, 328, 169, 150, 131,
					              299, 338, 260, 210, 238, 155, 247, 235, 298, 1746, 262, 350, 187, 237, 263, 196 ], 
					    borderColor: 'blue',
					    backgroundColor: 'blue'
					  }, {
					    type: 'line',
					    label: 'Line Dataset',
					    data: [ 196, 145, 156, 122, 177, 122, 123, 84, 116, 96, 113, 133, 102, 137, 95, 111, 104, 158, 101, 
					              123, 304, 151, 282, 125, 76, 253, 250, 315, 112, 150, 148, 456, 297, 273, 313, 135, 171, 141, 
					              127, 134, 208, 234, 223, 231, 163, 408, 176, 289, 211, 408, 335, 201 ], 
					    fill: true,
					    cubicInterpolationMode: 'monotone',
					    borderColor: 'rgb(54, 162, 235)'
					  }]
					};
	*/	
			this.data= {
				"labels": [ ],
				"datasets": [{
				  "type": 'bar',
				  "label": 'Today average values in customisable intervals',
				  "data": [ 
				  {"x": "00:00:00", "y": 274 }, {"x": "00:15:00", "y": 266 }, {"x": "00:30:00", "y": 217 }, {"x": "00:45:00", "y": 152 },
				  {"x": "01:00:00", "y": 100 }, {"x": "01:15:00", "y": 156 }, {"x": "01:30:00", "y": 152 }, {"x": "01:45:00", "y": 146 },
				   {"x": "02:00:00", "y": 205 }, {"x": "02:15:00", "y": 247 }, {"x": "02:30:00", "y": 248 }, {"x": "02:45:00", "y": 89 },      
				  {"x": "03:00:00", "y": 142 }, {"x": "03:15:00", "y": 143 }, {"x": "03:30:00", "y": 90 }, {"x": "03:45:00", "y": 149 }, 
				   {"x": "04:00:00", "y": 245 }, {"x": "04:15:00", "y": 188 }, {"x": "04:30:00", "y": 191 }, {"x": "04:45:00", "y": 246 },
				   {"x": "05:00:00", "y": 274 }, {"x": "05:15:00", "y": 266 }, {"x": "05:30:00", "y": 217 }, {"x": "05:45:00", "y": 152 },
				  {"x": "06:00:00", "y": 100 }, {"x": "06:15:00", "y": 156 }, {"x": "06:30:00", "y": 152 }, {"x": "06:45:00", "y": 146 },
				   {"x": "07:00:00", "y": 205 }, {"x": "07:15:00", "y": 247 }, {"x": "07:30:00", "y": 248 }, {"x": "07:45:00", "y": 89 },      
				  {"x": "08:00:00", "y": 142 }, {"x": "08:15:00", "y": 143 }, {"x": "08:30:00", "y": 90 }, {"x": "08:45:00", "y": 149 }, 
				   {"x": "09:00:00", "y": 245 }, {"x": "09:15:00", "y": 188 }, {"x": "09:30:00", "y": 191 }, {"x": "09:45:00", "y": 246 },
				   {"x": "10:00:00", "y": 274 }, {"x": "10:15:00", "y": 266 }, {"x": "10:30:00", "y": 217 }, {"x": "10:45:00", "y": 152 },
				  {"x": "11:00:00", "y": 100 }, {"x": "11:15:00", "y": 156 }, {"x": "11:30:00", "y": 152 }, {"x": "11:45:00", "y": 146 },
				   {"x": "12:00:00", "y": 205 }, {"x": "12:15:00", "y": 247 }, {"x": "12:30:00", "y": 248 }, {"x": "12:45:00", "y": 89 },      
				  {"x": "13:00:00", "y": 142 }, {"x": "13:15:00", "y": 143 }, {"x": "13:30:00", "y": 90 }, {"x": "13:45:00", "y": 149 }, 
				   {"x": "14:00:00", "y": 245 }, {"x": "14:15:00", "y": 188 }, {"x": "14:30:00", "y": 191 }, {"x": "14:45:00", "y": 246 }
				   ]
				   ,
				  "borderColor": 'blue',
				  "backgroundColor": 'blue'
				}, 
				{
				  "type": 'line',
				  "label": 'Line Dataset',
				  "data": [ {"x": "06:00:00", "y": 100 }, {"x": "06:15:00", "y": 156 }, {"x": "06:30:00", "y": 152 }, {"x": "06:45:00", "y": 146 },
				   {"x": "07:00:00", "y": 205 }, {"x": "07:15:00", "y": 247 }, {"x": "07:30:00", "y": 248 }, {"x": "07:45:00", "y": 89 },      
				  {"x": "08:00:00", "y": 142 }, {"x": "08:15:00", "y": 143 }, {"x": "08:30:00", "y": 90 }, {"x": "08:45:00", "y": 149 }, 
				   {"x": "09:00:00", "y": 245 }, {"x": "09:15:00", "y": 188 }, {"x": "09:30:00", "y": 191 }, {"x": "09:45:00", "y": 246 }],
				  "cubicInterpolationMode": 'monotone',
				  "borderColor": 'rgb(54, 162, 235)'
				}]
			  };
		}
	
	
	getChartType(chartInput?: string){
		return chartInput;
	}
	
	elaborateResponses(){
		let items =[];
		const validSeries = this.widget!.seriesList!.filter(o => !!o.datasourceConfiguration);
		var response: Response = new Response();
		if(this.debug) this.utils.showInfoMessage(validSeries.length + " = " + this.responses.length);
		
		if (validSeries.length === this.responses.length){
			for (let i = 0; i < validSeries.length; i++) {
				let response = this.responses[i];
				let series = validSeries[i];
				const chartData = this.fromObjectsList(response, series);
				if(this.debug) this.utils.showInfoMessage( JSON.stringify( chartData.metadata ) );
				for (const dataset of chartData.datasets){
					this.dataLocal?.datasets.push(dataset);
					this.data!.datasets.push(dataset);
					
				}			
				if(this.widget?.sortData){	
				this.dataLocal.labels = this.dataLocal.labels.concat(
					chartData.labels.filter(x => this.dataLocal.labels.every(y => y !== x)));
				}

				this.options = chartData.configuration;
				
				}
		}else {

		}
		this.data!.labels = this.dataLocal.labels.sort();
	}
	
	fromObjectsList(objectsList: any, series: Series): ChartDataLocal {
        const chartDataSets: ChartDataset[] = [];
        const mapDataSets: any = {};
        const mapDataSets2: any = {};
        const colors: BackgroundColor[]= [];
        const metadata: Metadata[] = [];
        var x: any = {};
        var y: any = {};
        var xMetadata: any = {};
        var yMetadata: any = {};
        const myChart: Chart = this.mychart.chart!; 
        const myWidget: ChartWidget = this.widget!;
        	
        for (const column of series.selectedColumns)
            mapDataSets[column] = []; //Inizializza la tabella

        for (const column of series.selectedColumns)
            mapDataSets2[column] = []; //Inizializza la tabella


        const labels: string[] = [];
       
        for (const row of objectsList.result) {
            if (series.xaxisColumn){
                labels.push(row[series.xaxisColumn])
                x = row[series.xaxisColumn];
         	}
            for (const column of series.selectedColumns) {
                mapDataSets[column].push(row[column]);
                y = row[column];
                var input: any = {
                    x: x,
                    y: y
                }
                mapDataSets2[column].push(input);
            }
            
        }
        
        if (series.xaxisColumn){
        	xMetadata = SeriesMetadata.getFor(series.xaxisColumn,
        			series.datasourceConfiguration!.parametricQuery!.metadata);
        	metadata.push(xMetadata);
        }

        for (const column of series.selectedColumns) {
            const columnStyle = SeriesStyle.getFor(column, series.styles);
			let labelTemp :string = "";
            labelTemp = (series.datasourceConfiguration?.label ? series.datasourceConfiguration?.label : series.datasourceConfiguration?.parametricQuery?.description) as string;
            let backgroundColor =  (columnStyle?.fill ? this.hexToRGB(columnStyle.color!,"0.3") : columnStyle.color) as string;
            
            let chartdataset: ChartDataset = {
            	label: labelTemp,
                data: mapDataSets2[column],
                backgroundColor: backgroundColor,
                type: (columnStyle.chartType as string) as ChartType,
                fill: columnStyle?.fill,
                borderColor: this.hexToRGB(columnStyle.color!),
                pointRadius: 1,
                cubicInterpolationMode: columnStyle?.interpolate ? 'monotone' : 'default'
            }
    
            chartDataSets.push(chartdataset);
            colors.push(new BackgroundColor({
					backgroundColor: columnStyle.color
					}));
            
            yMetadata = SeriesMetadata.getFor(column,series.datasourceConfiguration!.parametricQuery!.metadata);
            metadata.push(yMetadata);
        }
        
        let xUnit = xMetadata.unitOfMeasure ? 
        		(xMetadata?.unitOfMeasure.includes("N/A") ? '' : xMetadata?.unitOfMeasure) : '';
        let yUnit = yMetadata.unitOfMeasure ? 
        		(yMetadata?.unitOfMeasure.includes("N/A") ? '' : yMetadata?.unitOfMeasure) : '';
        
        let configuration = {
    		    responsive: true,
                plugins: {
                    zoom: {
                        pan: {
                            enabled: true,
                            mode: 'x',
                            modifierKey: 'shift'
                          },
                      zoom: {
                        wheel: {
                          enabled: true
                        },
                        pinch: {
                          enabled: true
                        },
                        mode: 'x'
                      }
                    }
                  },
    		   	scales: {
    		    	x: { 
    		    		title: {
    		    	        display: true,
    		    	        text: xUnit
    		    	   	},
                        ticks: {
                            autoSkip: myWidget!.skipXLabel
                        }
                    },
    		    	 y: {
    		    	   title: {
    		    	    display: true,
    		    	    text: yUnit
    		    	   }
    		    	}
    		    },
                onClick: (e, elements) => {this.elaborateClick(e, elements)}
        };
                
        return new ChartDataLocal(labels, chartDataSets, colors, configuration);
    }
    
    transparentize(color?: string, opacity?: number) {
    	const _opacity = Math.round(Math.min(Math.max(opacity || 1, 0), 1) * 255);
        return color + _opacity.toString(16).toUpperCase();
    	}

    hexToRGB(hex: string, alpha?: string) {
            var r = parseInt(hex.slice(1, 3), 16),
                g = parseInt(hex.slice(3, 5), 16),
                b = parseInt(hex.slice(5, 7), 16);
        
            if (alpha) {
                return "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")";
            } else {
                return "rgb(" + r + ", " + g + ", " + b + ")";
            }
        }



	elaborateClick(evt: any, array: any[]){
		//console.log(evt);
		//console.log(array);
		const elements = this.mychart.chart!.getElementsAtEventForMode(evt, 'point', { intersect: false }, false);
		
			//console.log("WidgetComponent");
			if (elements && this.widget.parameterName){
				var dataIndex = elements[0].index;
				var datasetIndex = elements[0].datasetIndex;
				let element: Element = elements[0];
			/*
			* Questo potrebbe essere migliorato perche' non utilizza le informazioni nei dati ma una bieca estrazioe del vvalore dalle proprieta' dell'oggeto
			*/
			const xValue = element.element.$context.raw.x;
			
			let queryParams: any = {};
        		queryParams[this.widget.parameterName] = xValue;
        		if (this.widget.target) {

					//In this case, the target is decided by the administrator and I can move to the target when row is selected
            let navigationExtras: NavigationExtras = {
                queryParams: queryParams
            };
			/*
			* Questo pezzo di codice differisce da quello usato nel caso del table-widget perche', probabilmente, questa chiamata non e' fatta dalla pagina HTML 
			* ma da una elaborazione successiva. Questo significa che per angular, la richiesta esce dalla sua "zone".
			*/
			this.zone.run(() => {
            	this.router.navigate(["/pages/" + this.widget.target], {
                	relativeTo: this.route,
                	queryParams: queryParams,
                	queryParamsHandling: 'merge'
				});
			});
        }
        else{
            //in this case, only a parameter value is selcted and assigned
        
       	 // changes the route without moving from the current view or
       	 // triggering a navigation event,
       	 this.router.navigate([], {
            relativeTo: this.route,
            queryParams: queryParams,
            queryParamsHandling: 'merge',  // preserve the existing query params in the route
            
        });
        }
	}
}
	
}


export class Response {
  	result?: any[];
    metadata?: any[];
    constructor(init?: Partial<Response>) {
        Object.assign(this, init);
    }
}

export class BackgroundColor{
	backgroundColor: string = "#92b3db";
	
	constructor(init?: Partial<BackgroundColor>) {
		
        Object.assign(this, init);
    }
	
}