/* eslint no-underscore-dangle: 0 */
import DeepMerge from 'deepmerge';
import { Chart as ChartJS } from 'chart.js';
import Bar from './Bar';
import BarHorizontal from './BarHorizontal';
import Doughnut from './Doughnut';
import Line from './Line';
import Scatter from './Scatter';

export default class Chart {
	static Bar = Bar;
	static BarHorizontal = BarHorizontal;
	static Doughnut = Doughnut;
	static Line = Line;
	static Scatter = Scatter;

	static pointIndex = null;

	static Style = {
		merge(options) {
			if (options.length === 1) {
				return options[0];
			}

			return DeepMerge.all(options);
		},
		Default: {
			plugins: {
				PluginScatter: false
			},
			maintainAspectRatio: false,
			layout: {
				padding: {
					top: 0,
					right: 0,
					bottom: 0,
					left: 0
				}
			},
			legend: {
				display: false
			},
			hover: {
				animationDuration: 0
			},
			animation: false,
			cutoutPercentage: 25,
			events: ['click', 'mousemove'],
			tooltips: {
				displayColors: false,
				caretSize: 5,
				caretPadding: 5,
				cornerRadius: 0,
				xPadding: 8,
				yPadding: 6,
				backgroundColor: '#000',
				titleFontColor: '#FFF',
				titleFontFamily: 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif',
				titleFontSize: 2,
				titleFontStyle: 'bold',
				titleSpacing: 0,
				titleMarginBottom: 0,
				bodyFontColor: '#FFF',
				bodyFontFamily: 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif',
				bodyFontSize: 13,
				bodyFontStyle: 'bold',
				bodySpacing: 0,
				footerFontColor: '#FFF',
				footerFontFamily: 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif',
				footerFontSize: 0,
				footerSpacing: 0,
				callbacks: {
					title: () => {
						return ' ';
					},
					label: (item) => {
						return item.yLabel;
					},
					footer: () => {
						return '';
					}
				}
			}
		},
		Scatter: {
			plugins: {
				PluginScatter: true
			},
			tooltips: {
				enabled: false
			},
			enableCustomTooltips: true,
			scales: {
				yAxes: [{
					gridLines: {
						display: true,
						tickMarkLength: 10
					},
					ticks: {
						reverse: true,
						stepSize: 50,
						beginAtZero: true,
						max: 100,
						fontSize: 13,
						fontColor: '#99A4AC',
						fontFamily: 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif',
						callback: (label) => {
							switch (label) {
							case 0:
								return 'Low';
							case 50:
								return 'Average';
							default:
								return 'High';
							}
						}
					},
					scaleLabel: {
						display: true,
						labelString: 'Annual Cost',
						fontSize: 15,
						fontColor: '#99A4AC',
						fontFamily: 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif'
					}
				}],
				xAxes: [{
					position: 'top',
					gridLines: {
						display: true,
						tickMarkLength: 10
					},
					ticks: {
						stepSize: 50,
						beginAtZero: true,
						max: 100,
						fontSize: 13,
						fontColor: '#99A4AC',
						fontFamily: 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif',
						callback: (label) => {
							switch (label) {
							case 0:
								return 'Worst';
							case 50:
								return 'Average';
							default:
								return 'Best';
							}
						}
					},
					scaleLabel: {
						display: true,
						labelString: 'Performance',
						fontSize: 15,
						fontColor: '#99A4AC',
						fontFamily: 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif'
					}
				}]
			}
		},
		Dashboard: {
			scales: {
				xAxes: [{
					scaleLabel: {
						fontSize: 30,
						fontColor: '#99A4AC',
						fontFamily: 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif'
					},
					gridLines: {
						display: false,
						tickMarkLength: 20,
						drawBorder: false
					},
					ticks: {
						beginAtZero: true,
						fontColor: '#99A4AC',
						fontSize: 30,
						fontFamily: 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif'
					}
				}],
				yAxes: [{
					scaleLabel: {
						fontSize: 30,
						fontColor: '#99A4AC',
						fontFamily: 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif'
					},
					gridLines: {
						display: false,
						tickMarkLength: 20,
						drawBorder: false
					},
					ticks: {
						min: 0,
						beginAtZero: true,
						fontColor: '#99A4AC',
						fontSize: 30,
						fontFamily: 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif'
					}
				}]
			}
		},
		Axes: {
			scales: {
				xAxes: [{
					barThickness: 'flex',
					barPercentage: 0.6,
					scaleLabel: {
						fontSize: 15,
						fontColor: '#99A4AC',
						fontFamily: 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif'
					},
					gridLines: {
						display: false,
						tickMarkLength: 20
					},
					ticks: {
						beginAtZero: true,
						fontColor: '#99A4AC',
						fontSize: 13,
						fontFamily: 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif'
					}
				}],
				yAxes: [{
					barThickness: 'flex',
					barPercentage: 0.6,
					scaleLabel: {
						fontSize: 15,
						fontColor: '#99A4AC',
						fontFamily: 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif'
					},
					gridLines: {
						display: false,
						tickMarkLength: 20
					},
					ticks: {
						min: 0,
						beginAtZero: true,
						fontColor: '#99A4AC',
						fontSize: 13,
						fontFamily: 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif'
					}
				}]
			}
		},
		Point: {
			pointHitRadius: 6,
			pointBorderWidth: 0,
			pointHoverBorderWidth: 0,
			pointRadius: 4,
			pointHoverRadius: 4,
			borderWidth: 4,
			spanGaps: true,
			fill: false,
			pointBackgroundColor: '#0099CC',
			pointHoverBackgroundColor: '#0099CC',
			pointBorderColor: '#0099CC',
			pointHoverBorderColor: '#0099CC',
			borderColor: 'rgba(0, 153, 204, 0.4)'
		},
		createHatchPattern(color, ctx, document) {
			const patternCanvas = document.createElement('canvas');
			const pctx = patternCanvas.getContext('2d', { antialias: true });

			const CANVAS_SIDE_LENGTH = 10;
			const WIDTH = CANVAS_SIDE_LENGTH;
			const HEIGHT = CANVAS_SIDE_LENGTH;
			const DIVISIONS = 4;

			patternCanvas.width = WIDTH;
			patternCanvas.height = HEIGHT;
			pctx.fillStyle = color;

			// Top line
			pctx.beginPath();
			pctx.moveTo(0, HEIGHT * (1 / DIVISIONS));
			pctx.lineTo(WIDTH * (1 / DIVISIONS), 0);
			pctx.lineTo(0, 0);
			pctx.lineTo(0, HEIGHT * (1 / DIVISIONS));
			pctx.fill();

			// Middle line
			pctx.beginPath();
			pctx.moveTo(WIDTH, HEIGHT * (1 / DIVISIONS));
			pctx.lineTo(WIDTH * (1 / DIVISIONS), HEIGHT);
			pctx.lineTo(0, HEIGHT);
			pctx.lineTo(0, HEIGHT * ((DIVISIONS - 1) / DIVISIONS));
			pctx.lineTo(WIDTH * ((DIVISIONS - 1) / DIVISIONS), 0);
			pctx.lineTo(WIDTH, 0);
			pctx.lineTo(WIDTH, HEIGHT * (1 / DIVISIONS));
			pctx.fill();

			// Bottom line
			pctx.beginPath();
			pctx.moveTo(WIDTH, HEIGHT * ((DIVISIONS - 1) / DIVISIONS));
			pctx.lineTo(WIDTH * ((DIVISIONS - 1) / DIVISIONS), HEIGHT);
			pctx.lineTo(WIDTH, HEIGHT);
			pctx.lineTo(WIDTH, HEIGHT * ((DIVISIONS - 1) / DIVISIONS));
			pctx.fill();

			return ctx.createPattern(patternCanvas, 'repeat');
		}
	};

	// Draws a custom tooltip
	static drawTooltip(ctx, dataset, index, element, hover) {
		const fontSize = 13;
		ctx.font = ChartJS.helpers.fontString(fontSize, 'bold', 'Roboto, "Avenir", "Helvetica Neue", Helvetica, Arial, sans-serif');
		ctx.textBaseline = 'middle';
		ctx.textAlign = 'center';

		const data = dataset.data[index];
		const labelSize = ctx.measureText(data.label); // Gets the size of the text element
		const position = element.tooltipPosition();
		const padding = 15;

		// If value is near boundary move towards x-axis
		if (data.x < 6) {
			position.x += (labelSize.width / 2);
		} else if (data.x > 94) {
			position.x -= (labelSize.width / 2);
		}

		if (data.y < 50) {
			position.y += 30;
		} else if (data.y >= 50) {
			position.y -= 17;
		}

		// Where the label fill position is determined
		const start = { x: position.x - (labelSize.width / 2) - (padding / 2), y: position.y - fontSize - (padding / 2) };
		const width = labelSize.width + padding;
		const height = fontSize + padding;

		ctx.lineJoin = 'square';
		ctx.fillStyle = hover ? '#000' : 'rgba(255, 255, 255, 0)';
		ctx.fillRect(start.x, start.y, width, height);
		ctx.fillStyle = hover ? '#FFF' : 'rgba(0, 0, 0, 0.2)';
		ctx.fillText(data.label, position.x, position.y - (fontSize / 2));
	}

	static createPlugins() {
		ChartJS.plugins.register({
			id: 'PluginScatter',
			beforeDatasetsUpdate: (chartInstance) => {
				// If 'axisGuard', we make sure no value falls directly on an axis
				// Compensates for glitch where y=0 values disappear from chart
				if (chartInstance.config.options.axisGuard) {
					chartInstance.data.datasets.map((dataset) => {
						const newDataset = Object.assign({}, dataset);

						dataset.data.map((data) => {
							const newData = Object.assign({}, data);

							// Check if x is near a boundary
							if (newData.x < 0.5) {
								newData.x = 0.5;
							} else if (newData.x > 99.5) {
								newData.x = 99.5;
							}

							// Check if y is near a boundary
							if (newData.y < 0.5) {
								newData.y = 0.5;
							} else if (newData.y > 99.5) {
								newData.y = 99.5;
							}

							return newData;
						});

						return newDataset;
					});
				}
			},
			afterDatasetsDraw: (chartInstance) => {
				// Create the custom tooltips
				if (chartInstance.config.options.enableCustomTooltips) {
					const { ctx } = chartInstance.chart;

					chartInstance.data.datasets.forEach((dataset, i) => {
						const meta = chartInstance.getDatasetMeta(i);
						// Only uses the data from the first dataset [0], not the line graph [1]
						if (!meta.hidden && i === 0) {
							meta.data.forEach((element, index) => {
								// Doesn't disable any tooltips
								let hover = false;

								if (index === Chart.pointIndex) {
									hover = true;
								}

								Chart.drawTooltip(ctx, dataset, index, element, hover);
							});
						}
					});
				}
			},
			afterEvent: (chartInstance, e) => {
				// Updates `pointIndex`, which tells us the array index of the point being hovered on
				if (chartInstance.config.options.enableCustomTooltips) {
					const point = chartInstance.getElementAtEvent(e)[0];

					// Use `._datasetIndex === 1` to ignore any events over the second (line) dataset.
					if (!point || typeof point._index === 'undefined' || point._datasetIndex === 1) {
						Chart.pointIndex = null;
					} else {
						Chart.pointIndex = point._index;
					}
				}
			}
		});
	}
}
