class JADTheme extends am5.Theme {
	setupDefaultRules() {
		this.rule("ColorSet").setAll({
			colors: [
				am5.color(0x95c11f),
				am5.color(0x40a7da),
				am5.color(0x0064b8),
				am5.color(0x6610f2),
				am5.color(0x6a1686),
				am5.color(0xe83e8c),
				am5.color(0xff3200),
				am5.color(0xff6f00),
				am5.color(0xffbb00),
				am5.color(0x20c997)
			],
			reuse: true
		});
	}
}

class BaseChart {
	
	static THEME_FACTORIES = {};
	
	constructor(divId, options, defaultOptions) {
		
		if(!options)
			options = {};
		if(!defaultOptions)
			defaultOptions  = {};
		
		this.options = { ...defaultOptions, ...options };
			
		am5.addLicense("AM5C387765625");
		
		this.root = am5.Root.new(divId);
		this.allSeries = [];

		try {
			this.root.setThemes(BaseChart.THEME_FACTORIES[
				this.options.themeKey 
					? this.options.themeKey 
					: divId
				](this.root));
		}
		catch (e) {
			this.root.setThemes([
				am5themes_Animated.new(this.root),
			]);
		}
		
		if(this.options.refresh) {
			window.setTimeout(() => this.reload(), parseInt(this.options.refresh) * 1000);
		}
	}

	setOptions(newOptions) {
		this.options = { ...this.options, ...newOptions };
	}

	add(series) {
		throw "Not implemented";
	}
	
	reload() {
		this.allSeries.forEach(e => {
			e._load(this);
		});
	}
}

class DateXYChart extends BaseChart {
	constructor(divId, options) {
		super(divId, options, {
			numberFormat: false,
			timeUnit: "hour",
			legends: true
		});
		
		/* Make chart */
		this.amChart = am5xy.XYChart.new(this.root, {
			panX: false,
			panY: false,
			paddingLeft: 0,
			wheelX: "panX",
			wheelY: "zoomX",
			layout: this.root.verticalLayout
		});
		this.root.container.children.push(this.amChart);
		

		if(this.options.numberFormat)
			this.amChart.getNumberFormatter().set("numberFormat", this.options.numberFormat);
		
		/* Legends */
		if(this.options.legends) {
			this.legend = am5.Legend.new(this.root, {
				centerX: am5.p50,
				x: am5.p50
			});
			this.amChart.children.push(this.legend);
		}
					

		/* X Axis */
		this.xAxis = am5xy.DateAxis.new(this.root, {
			maxDeviation: 0.2,
			baseInterval: {
				timeUnit: this.options.timeUnit,
				count: 1
			},
			renderer: am5xy.AxisRendererX.new(this.root, {
				minorGridEnabled: true
			}),
			tooltip: am5.Tooltip.new(this.root, {})
		}); 
		this.amChart.xAxes.push(this.xAxis);

		this.root.dateFormatter.setAll({
			dateFormat: "d MMM",
			dateFields: ["valueX"]
		});

		/* Y Axis */
		this.yAxis = am5xy.ValueAxis.new(this.root, {
			renderer: am5xy.AxisRendererY.new(this.root, {
				strokeOpacity: 0.1
			})
		}); 
		this.yAxis.set("maxPrecision", 0);
		this.amChart.yAxes.push(this.yAxis);
		
	}

	setOptions(newOptions) {
		super.setOptions(newOptions);
		this.xAxis.set("baseInterval", {
			timeUnit: this.options.timeUnit,
			count: 1
		});
	}
	
	add(series) {
		if(series instanceof Series) {
			this.allSeries.push(series);
			series._load(this, () => {
				
				if(this.options.legends) {
					this.legend.data.push(series.amSeries);
				}
				
				series.amSeries.appear();
				this.amChart.appear(1000, 100);
			});
		}
		else
			throw "Must be a Series";
	}
	
	_createXAxis() {
	}
	
}

class SpeedGauge extends BaseChart {
	constructor(divId, options) {
		super(divId, options, {
			numberFormat: false,
			startAngle: 180,
			endAngle: 360
		});

		/* Make chart */
		this.amChart = am5radar.RadarChart.new(this.root, {
			panX: false,
			panY: false,
			startAngle: this.options.startAngle,
			endAngle: this.options.endAngle
		});
		this.root.container.children.push(this.amChart);
		
		if(this.options.numberFormat)
			this.amChart.getNumberFormatter().set("numberFormat", this.options.numberFormat);
		

		// Create axis and its renderer
		// https://www.amcharts.com/docs/v5/charts/radar-chart/gauge-charts/#Axes
		var axisRenderer = am5radar.AxisRendererCircular.new(this.root, {
			innerRadius: -40
		});

		axisRenderer.grid.template.setAll({
			stroke: this.root.interfaceColors.get("background"),
			visible: true,
			strokeOpacity: 0.8
		});

		var xAxis = this.amChart.xAxes.push(
			am5xy.ValueAxis.new(this.root, {
				maxDeviation: 0,
				min: 0,
				max: maxValue,
				strictMinMax: true,
				renderer: axisRenderer
			})
		);

		// Add clock hand
		// https://www.amcharts.com/docs/v5/charts/radar-chart/gauge-charts/#Clock_hands
		var axisDataItem = xAxis.makeDataItem({});

		var clockHand = am5radar.ClockHand.new(this.root, {
			pinRadius: 50,
			radius: am5.percent(100),
			innerRadius: 50,
			bottomWidth: 0,
			topWidth: 0
		});

		clockHand.pin.setAll({
			fillOpacity: 0,
			strokeOpacity: 0.5,
			stroke: am5.color(0x000000),
			strokeWidth: 1,
			strokeDasharray: [2, 2]
		});
		clockHand.hand.setAll({
			fillOpacity: 0,
			strokeOpacity: 0.5,
			stroke: am5.color(0x000000),
			strokeWidth: 0.5
		});

		var bullet = axisDataItem.set(
			"bullet",
			am5xy.AxisBullet.new(this.root, {
				sprite: clockHand
			})
		);

		xAxis.createAxisRange(axisDataItem);

		var label = this.amChart.radarContainer.children.push(
			am5.Label.new(this.root, {
				centerX: am5.percent(50),
				textAlign: "center",
				centerY: am5.percent(50),
				fontSize: "1.5em"
			})
		);

		axisDataItem.set("value", 50);
		bullet.get("sprite").on("rotation", function() {
			var value = axisDataItem.get("value");
			label.set("text", humanFileSize(Math.round(value) * 1024 * 1024, dp = 0, false) + '/s');
		});
	}
}

class Series {
	
	constructor(options, defaultOptions) {
		if(!options)
			options = {};
		if(!defaultOptions)
			defaultOptions  = {};
		this.options = { ...defaultOptions, ...options };
	}
	
	_load(axis) {
		throw "Not implemented";
	}
}

class JsonSeries extends Series {
	constructor(url, name, options, defaultOptions) {
		super(options, defaultOptions);
		
		this.url = url;
		this.name = name;
		this.amSeries = null;
	}

	_createSeries(chart) {
		throw "Not implemented";
	}
	
	_load(chart, onLoad) {
		if(!this.amSeries) {

			this.amSeries = this._createSeries(chart);
			chart.amChart.series.push(this.amSeries);
		}
		
		am5.net.load(typeof this.url === 'function' ? this.url() : this.url, 
			this.name).then(result => {

			var data = am5.JSONParser.parse(result.response);
			this._updateSeries(chart, data);
			
			if(onLoad)
				onLoad();

		}).catch(result => {
			console.log("Error loading " + result.xhr);
		});
	}
	
	_updateSeries(data) {
		throw "Not implemented";		
	}
	
}

class JsonDataPointSeries extends JsonSeries {

	constructor(url, name, options) {
		super(url, name, options, {
		});
	}

	_createSeries(chart) {
	}

	_updateSeries(chart, data) {
		var value = data['*'][speedColumn] / 1024.0 / 1024.0; 

		axisDataItem.animate({
			key: "value",
			to: value,
			duration: 500,
			easing: am5.ease.out(am5.ease.cubic)
		});

		axisRange0.animate({
			key: "endValue",
			to: value,
			duration: 500,
			easing: am5.ease.out(am5.ease.cubic)
		});

		axisRange1.animate({
			key: "value",
			to: value,
			duration: 500,
			easing: am5.ease.out(am5.ease.cubic)
		});
	}
}

class JsonLineSeries extends JsonSeries {
	constructor(url, name, options) {
		super(url, name, options, {
			filled: false,
			stokeWidth: 2,
			label: name
		});
	}
	
	_createSeries(chart) {
		var series = am5xy.LineSeries.new(chart.root, {
			name: this.name,
			xAxis: chart.xAxis,
			yAxis: chart.yAxis,
			valueYField: "value",
			valueXField: "date",
			legendValueText: "{valueY}",
			categoryXField: "date",
			tooltip: am5.Tooltip.new(chart.root, {
				pointerOrientation: "horizontal",
				labelText: "{valueX}: " + "{valueY} " + this.options.label
			})
		});

		series.strokes.template.set("strokeWidth", this.options.strokeWidth);
		
		if(this.options.filled) {
			series.fills.template.setAll({
			  visible: true,
			  fillOpacity: 0.4
			});
		}
		
		return series;
	}
	
	_updateSeries(chart, data) {
		chart.xAxis.data.setAll(data.resource);
		this.amSeries.data.setAll(data.resource);
	}
}

class JsonColumnSeries extends JsonSeries {
	constructor(url, name, options) {
		super(url, name, options, {
			label: name
		});
	}
	
	_createSeries(chart) {
		var series = am5xy.ColumnSeries.new(chart.root, {
			name: this.name,
			xAxis: chart.xAxis,
			yAxis: chart.yAxis,
			valueYField: "value",
			valueXField: "date",
			legendValueText: "{valueY}",
			categoryXField: "date",
			tooltip: am5.Tooltip.new(chart.root, {
				pointerOrientation: "horizontal",
				labelText: "{valueX}: " + "{valueY} " + this.options.label
			})
		});
		

		series.columns.template.setAll({
			tooltipText: "{valueY}",
			width: am5.percent(90),
			tooltipY: 0,
			strokeOpacity: 0
		});

		/*this.series.columns.template.setAll({ 
			cornerRadiusTL: 5, 
			cornerRadiusTR: 5, 
			strokeOpacity: 0 
		});*/
		
		return series;
	}	

	_updateSeries(chart, data) {
		chart.xAxis.data.setAll(data.resource);
		this.amSeries.data.setAll(data.resource);
	}
}

class ChartUtils {
	static daysToResolution(days) {
		days = parseInt(days);
		if(days <= 1) {
			return 'MINUTE'
		}
		else if(days <= 7) {
			return 'HOUR'
		}
		else {
			return 'DAY';		
		}
	}
	
	static daysToTimeUnit(days) {
		return ChartUtils.daysToResolution(days).toLowerCase();
	}
}
