import { number } from "echarts";
import moment from "moment";

export function createEmptyCanvas(id: string, width: number, height: number): void{
    let canvas: any = document.getElementById(id);
    canvas.width = width;
    canvas.height = height;

    let canvasContext: any = canvas.getContext('2d');
    canvasContext.fillStyle = 'white';
    canvasContext.fillRect(0,0, width, height);

    canvasContext.font = "normal 48px 微软雅黑";
    canvasContext.fillStyle = "#000000";
    canvasContext.fillText('暂无数据', (width / 2) - (canvasContext.measureText('暂无数据').width / 2), height / 2 - 24);
}

export class BoxDrawer{
    private canvas: any = null;
    private canvasContext: any = null;
    private targetCanvas: any = null;
    private targetCanvasContext: any = null;

    private borderWidth: number = 0;
    private borderHeight: number = 0;

    private readonly width: number = null;
    private readonly height: number = null;
    private readonly paddingLeft: number = 200;
    private readonly paddingRight: number = 130;
    private readonly paddingTop: number = 60;
    private readonly paddingBottom: number = 50;
    private readonly colorChart: ColorChart = null;
    private readonly values: any = null;
    private readonly verticalScaleSpecialCount: number = 6;
    private readonly horizontalScaleSpecialCount: number = 6;

    // 垂直刻度
    private readonly verticalScaleLine: number = 18;
    // 水平刻度
    private readonly horizontalScaleLine: number = 20;
    public base64Image: string = null;

    private verticalScaleIntervalLength: number = 0;
    private horizontalScaleIntervalLength: number = 0;

    private paddingColorLeft: number = 50;
    private defaultColorWidth: number = 30;

    private calc: Calc = null;
    private box: Box = null;

    constructor(width: number, height: number, colorChart: ColorChart, values: any, id: string, unit: string) {
        if (id == null) return;

        this.width = width;
        this.height = height;
        this.colorChart = colorChart;
        this.values = values;

        this.init();
        this.drawCoordinate(unit);
        this.drawColorChart();
        this.draw();

        this.setTargetCanvas(id);
        this.base64Image = this.canvas.toDataURL();
    }

    private drawColorChart(): void {
        let startX = this.paddingLeft + this.horizontalScaleLine + this.borderWidth + this.paddingColorLeft;
        let heightInterval = 0;
        let startY = this.paddingTop + this.verticalScaleLine + heightInterval;
        let interval = (this.borderHeight - heightInterval) / (this.colorChart.colors.length)
        
        this.canvasContext.font = "normal 24px 微软雅黑";
        this.canvasContext.fillStyle = "#000000";

        for(let lastLen = this.colorChart.colors.length - 1, index = lastLen; index >= 0; index --){
            const targetIndex = lastLen - index;
            const _y = startY + index * interval;

            this.canvasContext.fillStyle = this.colorChart.colors[targetIndex];
            this.canvasContext.fillRect(startX, _y, this.defaultColorWidth, interval);

            this.canvasContext.fillStyle = "#000000";
            if (index == 0){
                if (this.colorChart.showStartValue){
                    const text = this.colorChart.values[targetIndex + 1];
                    this.canvasContext.fillText(text, startX + this.defaultColorWidth,  _y + 9);
                }

                const text = this.colorChart.values[targetIndex];
                this.canvasContext.fillText(text, startX + this.defaultColorWidth,  _y + interval + 9);
            }else if (index == lastLen){
                if (this.colorChart.showEndValue){
                    const text = this.colorChart.values[targetIndex];
                    this.canvasContext.fillText(text, startX + this.defaultColorWidth,  _y + interval + 9);
                }

                const text = this.colorChart.values[targetIndex + 1];
                this.canvasContext.fillText(text, startX + this.defaultColorWidth,  _y + 9);
            }else{
                const text = this.colorChart.values[targetIndex];
                this.canvasContext.fillText(text, startX + this.defaultColorWidth,  _y + interval + 9);
            }
        }
    }

    private draw(): void{
        let startX = this.paddingLeft + this.horizontalScaleLine - this.verticalScaleIntervalLength / 2;
        let startY = this.paddingTop + this.verticalScaleLine - this.horizontalScaleIntervalLength / 2;

        let boxes = [];
        for(let dataIndex = 0, len = this.values.radar_data.length, lastDataLen = len - 1; dataIndex < len; dataIndex++){
            let value = this.values.radar_data[dataIndex];

            let rows = [];
            for(let lastInfoLen = this.values.radar_info.length - 1, infoIndex = lastInfoLen; infoIndex >= 0; infoIndex--){
                let x = startX + dataIndex * this.verticalScaleIntervalLength;
                let y = startY + (this.borderHeight - infoIndex * this.horizontalScaleIntervalLength);
                let width = this.verticalScaleIntervalLength;
                let height = this.horizontalScaleIntervalLength;
                if (dataIndex == 0) {
                    x = x + this.verticalScaleIntervalLength / 2;
                    width = width / 2;
                }
                if (infoIndex == lastInfoLen){
                    y = y + this.horizontalScaleIntervalLength / 2;
                    height = height / 2;
                }
                if (dataIndex == lastDataLen){
                    width = width / 2;
                }
                if (infoIndex == 0){
                    height = height / 2;
                }

                let radarInfo = this.values.radar_info[infoIndex];
                let targetValue = value[radarInfo.col_name];
                let color = this.colorChart.getColor(targetValue);
                rows.push(new Box(x, y, width, height, targetValue, radarInfo.col_factor, value.data_time, radarInfo.col_unit));
                if (color == null) continue;
                this.canvasContext.fillStyle = color;
                this.canvasContext.fillRect(x, y, width, height);
            }
            boxes.push(rows);
        }
        this.createCalc(boxes);
    }

    private createCalc(boxes: any): void{
        this.calc = new Calc(this.width, this.height, this.paddingLeft + this.verticalScaleLine, this.paddingTop + this.horizontalScaleLine,
            this.paddingRight + this.horizontalScaleLine, this.paddingBottom + this.verticalScaleLine, boxes);
    }

    private resetValues() : void{
        this.values.radar_data.sort((a, b) => {
            return moment(a.data_time).isBefore(b.data_time, 'second');
        });

        this.values.radar_info.sort((a, b) => {
            return a.col_factor - b.col_factor;
        });
    }

    private init(): void{
        // 创建画布
        this.canvas = document.createElement('canvas');
        this.canvas.width = this.width;
        this.canvas.height = this.height;
        this.canvasContext = this.canvas.getContext('2d');

        this.fillBackground();
        this.resetValues();
    }

    private setTargetCanvas(id): void{
        this.targetCanvas = document.getElementById(id);
        this.targetCanvas.width = this.width;
        this.targetCanvas.height = this.height;
        this.targetCanvasContext = this.targetCanvas.getContext('2d');
        this.copyCanvas();
        this.bindMoveEvent();
    }

    private copyCanvas(): void{
        const imgData= this.canvasContext.getImageData(0,0, this.width, this.height);
        this.targetCanvasContext.putImageData(imgData, 0, 0);
    }

    private _onMouseMove = this.onMouseMove.bind(this);
    private _onMouseOut = this.onMouseOut.bind(this)

    private bindMoveEvent(): void{
        this.targetCanvas.addEventListener('mousemove', this._onMouseMove);
        this.targetCanvas.addEventListener('mouseout', this._onMouseOut);
    }

    private onMouseOut() : void{
        this.copyCanvas();
    }

    private onMouseMove(e) : void{
        let box = this.calc.calc(e.offsetX, e.offsetY);
        if (box == null) {
            if (this.box != null){
                this.copyCanvas();
                this.box = null;
            }
            return;
        }

        if (this.box != null){
            if (this.box.id == box.id) return;
            this.copyCanvas();
            this.box = null;
        }
        
        box.show(this.targetCanvasContext);
        this.box = box;
    }

    public close(): void{
        this.targetCanvas.removeEventListener('mousemove', this._onMouseMove);
        this.targetCanvas.removeEventListener('mouseout', this._onMouseOut);
    }

    private fillBackground(): void{
        this.canvasContext.fillStyle = 'white';
        this.canvasContext.fillRect(0,0, this.width,this.height);
    }

    private drawCoordinate(unit): void{
        this.drawBorder();
        this.drawVerticalScale();
        this.drawHorizontalScale();
        this.drawHeightText();
        this.drawTemperatureText(unit);
    }
    //高度单位
    private drawHeightText(): void {
        let name =['高','度','(','k','m',')']; // 文本内容
        let x = 70,y = 150; // 文字开始的坐标
        let letterSpacing = 20; // 设置字间距
        for(let i = 0; i < name.length; i++) {
            const str = name.slice(i, i + 1).toString();
            if (str.match(/[A-Za-z0-9-()-]/) && (y < 576)) { // 非汉字 旋转
                this.canvasContext.save();
                this.canvasContext.translate(x, y);
                this.canvasContext.rotate(Math.PI / 180 * 90);
                this.canvasContext.textBaseline = 'bottom';
                this.canvasContext.font = "normal 35px 微软雅黑";
                this.canvasContext.fillStyle = "#000000";
                this.canvasContext.fillText(str, 0, 0);
                this.canvasContext.restore();
                y += this.canvasContext.measureText(str).width + letterSpacing; // 计算文字宽度
            } else if (str.match(/[\u4E00-\u9FA5]/) && (y < 576)) {
                this.canvasContext.save();
                this.canvasContext.textBaseline = 'top';
                this.canvasContext.font = "normal 35px 微软雅黑";
                this.canvasContext.fillStyle = "#000000";
                this.canvasContext.fillText(str, x, y);
                this.canvasContext.restore();
                y += this.canvasContext.measureText(str).width + letterSpacing; // 计算文字宽度
            }
        }
    }
    //温度单位
    private drawTemperatureText(unit): void {
        this.canvasContext.font = "normal 35px 微软雅黑";
        this.canvasContext.fillStyle = "#000000";
        let unitX = 1390
        if (unit === '(degree)') unitX =1350
        this.canvasContext.fillText(unit,  unitX,50);
    }

    private drawVerticalScale(): void{
        let scaleCount = this.values.radar_data.length;
        this.verticalScaleIntervalLength = this.borderWidth / (scaleCount - 1);
        let scaleSpecial = parseInt((scaleCount * 1.0 / this.verticalScaleSpecialCount).toString());

        let startX = this.paddingLeft + this.horizontalScaleLine;
        let startY = this.paddingTop + this.verticalScaleLine;
        for(let index = 0, len = scaleCount, lastLen = scaleCount - 1; index < len; index++){
            if (index == 0 || index == lastLen) continue;

            const flag = index % scaleSpecial == 0;
            this.setScaleStyle(flag);
            const yInterval =  flag ? this.verticalScaleLine : this.verticalScaleLine / 2;

            const _x = startX + index * this.verticalScaleIntervalLength;
            this.canvasContext.beginPath();
            this.canvasContext.moveTo(_x, startY);
            this.canvasContext.lineTo(_x,startY - yInterval);
            this.canvasContext.moveTo(_x, startY + this.borderHeight);
            this.canvasContext.lineTo(_x,startY + yInterval + this.borderHeight);
            this.canvasContext.stroke();

            if (flag){
                this.canvasContext.font = "normal 30px 微软雅黑";
                this.canvasContext.fillStyle = "#000000";
                const text = moment(this.values.radar_data[index].data_time).format("HH:mm");
                this.canvasContext.fillText(text, _x -  this.canvasContext.measureText(text).width / 2, startY + yInterval + this.borderHeight + 36);
            }
        }
    }

    private drawHorizontalScale(): void{
        let scaleCount = this.values.radar_info.length;
        this.horizontalScaleIntervalLength = this.borderHeight / (scaleCount - 1) ;
        let scaleSpecial = parseInt((scaleCount * 1.0 / this.horizontalScaleSpecialCount + 1).toString());
        let startX = this.paddingLeft + this.horizontalScaleLine;
        let startY = this.paddingTop + this.verticalScaleLine;
        const lastLen = scaleCount - 1;
        for(let index = lastLen; index >= 0; index--) {

            const flag = index % scaleSpecial == 0;
            this.setScaleStyle(flag);
            const xInterval = flag ? this.horizontalScaleLine : this.horizontalScaleLine / 2;

            const _y = startY + (this.borderHeight - index * this.horizontalScaleIntervalLength);
            this.canvasContext.beginPath();
            this.canvasContext.moveTo(startX, _y);
            this.canvasContext.lineTo(startX - xInterval, _y);
            this.canvasContext.moveTo(this.borderWidth + startX, _y);
            this.canvasContext.lineTo(this.borderWidth + startX + xInterval, _y);
            this.canvasContext.stroke();

            if (flag) {
                    this.canvasContext.font = "normal 30px 微软雅黑";
                    this.canvasContext.fillStyle = "#000000";
                    const text = this.values.radar_info[index].col_factor;
                    this.canvasContext.fillText(text, startX - this.canvasContext.measureText(text).width - this.horizontalScaleLine, _y + 10);
            }
        }
    }

    private setScaleStyle(isSpecial: boolean): void{
        if (isSpecial){
            this.canvasContext.lineWidth = 3;
            this.canvasContext.strokeStyle = '#101010';
            return;
        }
        this.canvasContext.lineWidth = 2;
        this.canvasContext.strokeStyle = '#5e5e5e';
    }

    private drawBorder(): void{
        const x = this.horizontalScaleLine + this.paddingLeft;
        const y = this.verticalScaleLine + this.paddingTop;
        const _x = this.width - this.horizontalScaleLine - this.paddingRight;
        const _y = this.height - this.verticalScaleLine - this.paddingBottom;

        this.canvasContext.beginPath();
        this.canvasContext.lineWidth = "2";
        this.canvasContext.strokeStyle = "black";

        this.borderWidth = _x - x;
        this.borderHeight = _y - y;
        this.canvasContext.rect(x, y, this.borderWidth, this.borderHeight);
        this.canvasContext.stroke();
    }
}

export class ColorChart{

    public colors: any = null;
    public values: any = null;
    public showStartValue: boolean = false;
    public showEndValue: boolean = false;

    private minDefaultColor: string = null;
    private maxDefaultColor: string = null;

    private readonly valueLength: number = 0;

    constructor(colors: any, values: any, showStartValue: boolean = false, showEndValue: boolean = false) {
        this.verify(colors, values);
        this.colors = colors;
        this.values = values;
        this.showStartValue = showStartValue;
        this.showEndValue = showEndValue;
        this.valueLength = this.values.length;

        this.resetValues();
    }

    public setDefaultColors(minDefaultColor: string, maxDefaultColor: string): void{
        this.minDefaultColor = minDefaultColor;
        this.maxDefaultColor = maxDefaultColor;
    }

    // 按照数值从小到大排序,供颜色值判断使用
    private resetValues(): void{
        if (this.values[0] < this.values[1]) return;
        this.values = this.values.reverse();
        this.colors = this.colors.reverse();
    }

    private verify(colors: any, values: any): void{
        if (values.length <= 2)
            throw new Error("色标数据至少包含两个数据");

        if (colors.length != values.length - 1)
            throw new Error("色标数据不正确,数据个数应为颜色个数 + 1");
    }

    public getColor(value: number): string{
        if (isNaN(value)) return null;

        const length = this.valueLength;
        if (this.minDefaultColor != null){
            if (value < this.values[0]) return this.minDefaultColor;
            if (value > this.values[length - 1]) return this.maxDefaultColor;
        }

        for(let index = 1; index < length; index ++){
            if (this.values[index -1] < value && this.values[index] >= value){
                return this.colors[index - 1];
            }
        }
        return null;
    }
}

export interface IBox {
    show(canvasContext: any);
}

export class Box implements IBox{
    id: string = null;

    x: number;
    y: number;
    width: number;
    height: number;

    public value: number;
    public dataHeight: number;
    time: string;
    util: string;
    //提示框的宽高
    rectWidth: number = 240;
    rectHeight: number = 90;
    rectX: number;
    rectY: number;

    isReserved: boolean = false;

    constructor(x: number, y: number, width: number, height: number, value: number, dataHeight: number, time: string, util: string) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        this.value = value;

        this.dataHeight = dataHeight;
        this.time = time;
        this.util = util;

        this.isReserved = this.y - 10 - this.rectHeight < 0;
        this.id = this.uuid();
    }

    updateBorderInfo(x: number, y: number, width: number, height: number): void{
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;

        this.isReserved = this.y - 10 - this.rectHeight < 0;
    }

    show(canvasContext: any) {
        this.setSelectStyle(canvasContext);
        this.setTip(canvasContext);
    }

    private setTip(canvasContext: any): void{
        this.setBackground(canvasContext);
        this.setText(canvasContext);
    }

    private setBackground(canvasContext: any): void{
        canvasContext.fillStyle = 'rgba(59, 55, 55, 0.8)';
        this.rectX = this.x + this.width / 2 - this.rectWidth / 2;
        this.rectY = this.isReserved ? this.y + this.height / 2 + 10 : this.y - this.height / 2 - this.rectHeight - 10;
        this.fillRoundRect(canvasContext, this.rectX, this.rectY, this.rectWidth, this.rectHeight, 10, 2, 'rgba(59, 55, 55, 0.8)');
    }

    /**该方法用来绘制圆角矩形
     *@param cxt:canvas的上下文环境
     *@param x:左上角x轴坐标
     *@param y:左上角y轴坐标
     *@param width:矩形的宽度
     *@param height:矩形的高度
     *@param radius:圆的半径
     *@param lineWidth:线条粗细
     *@param strokeColor:线条颜色
     *@user: xiaowuler
     **/
    private fillRoundRect(cxt, x, y, width, height, radius, lineWidth, strokeColor): void {
        //圆的直径必然要小于矩形的宽高
        if (2 * radius > width || 2 * radius > height) { return; }

        cxt.save();
        cxt.translate(x, y);
        //绘制圆角矩形的各个边
        this.drawRoundRectPath(cxt, width, height, radius);
        cxt.lineWidth = lineWidth || 2; //若是给定了值就用给定的值否则给予默认值2
        cxt.fillStyle = strokeColor || "#000";
        cxt.fill();
        cxt.restore();
    }

    private drawRoundRectPath(cxt, width, height, radius): void {
        cxt.beginPath();
        //从右下角顺时针绘制,弧度从0到1/2PI
        cxt.arc(width - radius, height - radius, radius, 0, Math.PI / 2);

        //矩形下边线
        cxt.lineTo(radius, height);

        //左下角圆弧,弧度从1/2PI到PI
        cxt.arc(radius, height - radius, radius, Math.PI / 2, Math.PI);

        //矩形左边线
        cxt.lineTo(0, radius);

        //左上角圆弧,弧度从PI到3/2PI
        cxt.arc(radius, radius, radius, Math.PI, Math.PI * 3 / 2);

        //上边线
        cxt.lineTo(width - radius, 0);

        //右上角圆弧
        cxt.arc(width - radius, radius, radius, Math.PI * 3 / 2, Math.PI * 2);

        //右边线
        cxt.lineTo(width, height - radius);
        cxt.closePath();
    }

    private setText(canvasContext: any) : void{
        canvasContext.font = "normal 20px 微软雅黑";
        canvasContext.fillStyle = "#fafafa";

        const centerX = this.rectX + this.rectWidth / 2;
        const timeText = "time:" + this.time;
        canvasContext.fillText(timeText, centerX - canvasContext.measureText(timeText).width / 2,  this.rectY + 25);
        const valueText = 'value:' + this.value.toPrecision(3);
        canvasContext.fillText(valueText, centerX - canvasContext.measureText(valueText).width / 2,  this.rectY + 8 + 45);
        const heightText = 'height:' + this.dataHeight + ' ' + this.util;
        canvasContext.fillText(heightText, centerX - canvasContext.measureText(heightText).width / 2,  this.rectY + 8 + 12 + 60);
    }

    private setSelectStyle(canvasContext: any): void{
        canvasContext.lineWidth = 2;
        canvasContext.strokeStyle = "#ff0000";
        canvasContext.strokeRect(this.x, this.y, this.width, this.height);
    }


    uuid(): string{
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
            let r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
            return v.toString(16);
        });
    }
}

export class Calc{
    private readonly width: number;
    private readonly height: number;

    private readonly paddingLeft: number;
    private readonly paddingTop: number;
    private readonly paddingRight: number;
    private readonly paddingBottom: number;

    private boxWidth: number;
    private boxHeight: number;

    private xMax: number = null;
    private yMax: number = null;

    private startX: number = null;
    private startY: number = null;

    private readonly boxes: any = null;

    constructor(width: number, height: number,
                paddingLeft: number, paddingTop: number, paddingRight: number, paddingBottom: number, boxes: any) {
        this.width = width;
        this.height = height;
        this.paddingLeft = paddingLeft;
        this.paddingTop = paddingTop;
        this.paddingRight = paddingRight;
        this.paddingBottom = paddingBottom;
        this.boxes = boxes;
        this.boxWidth = this.boxes[1][1].width;
        this.boxHeight = this.boxes[1][1].height;
        
        // this.setWidthAndHeight();
        this.init();
    }

    private setWidthAndHeight() : void{
        for(let row = 0, rowLen = this.boxes.length; row < rowLen; row++){
            for(let col = 0, colLen = this.boxes[row].length; col < colLen; col++){
                let box = this.boxes[row][col];
                if (box == null) continue;

                console.log(box)
                this.boxWidth = box.width;
                this.boxHeight = box.height;
                return;
            }
        }
    }

    private init(): void{
        this.xMax = this.width - this.paddingRight;
        this.yMax = this.height - this.paddingBottom;

        this.startX = this.paddingLeft - this.boxWidth / 2;
        this.startY = this.paddingTop - this.boxHeight / 2;
    }

    public calc(x: number, y: number): Box{
        if (x <= this.paddingLeft || x >= this.xMax || y <= this.paddingTop || y >= this.yMax){
            return null;
        }

        const indexX = parseInt(((x - this.startX) * 1.0 / this.boxWidth).toString());
        const indexY = parseInt(((y - this.startY) * 1.0 / this.boxHeight).toString());

        return this.boxes[indexX][indexY];
    }
}