import baseComponent from '../helpers/baseComponent'
import classNames from '../helpers/libs/classNames'
import { getSystemInfoSync } from '../helpers/hooks/useNativeAPI'
import { getCanvasRef } from '../helpers/hooks/useCanvasAPI'

const toAngle = (a) => a / 180 * Math.PI
const percent = (a) => toAngle(a / 100 * 360)
const easeInOutCubic = (a, b, c, d) => {
    a /= d / 2
    if (a < 1) return c / 2 * a * a * a + b
    a -= 2
    return c / 2 * (a * a * a + 2) + b
}

baseComponent({
    properties: {
        prefixCls: {
            type: String,
            value: 'wux-circle',
        },
        percent: {
            type: Number,
            value: 0,
            observer: 'redraw',
        },
        strokeWidth: {
            type: Number,
            value: 10,
        },
        size: {
            type: Number,
            value: 120,
            observer: 'updateStyle',
        },
        lineCap: {
            type: String,
            value: 'round',
        },
        backgroundColor: {
            type: String,
            value: '#f3f3f3',
        },
        color: {
            type: String,
            value: '#33cd5f',
        },
        sAngle: {
            type: Number,
            value: 0,
            observer(newVal) {
                this.setData({
                    beginAngle: toAngle(newVal),
                })
            },
        },
        counterclockwise: {
            type: Boolean,
            value: false,
        },
        speed: {
            type: Number,
            value: 2000,
        },
        animate: {
            type: Boolean,
            value: true,
        },
        background: {
            type: Boolean,
            value: true,
        },
    },
    data: {
        beginAngle: 0,
        startAngle: 0,
        endAngle: 0,
        currentAngle: 0,
    },
    computed: {
        classes: ['prefixCls', function(prefixCls) {
            const wrap = classNames(prefixCls)
            const inner = `${prefixCls}__inner`

            return {
                wrap,
                inner,
            }
        }],
    },
    methods: {
        /**
         * 更新样式
         */
        updateStyle(size = this.data.size) {
            const style = `width: ${size}px; height: ${size}px;`

            this.setData({
                style,
            })
        },
        /**
         * 着帧绘制 canvas
         */
        redraw(value = this.data.percent) {
            const endAngle = percent(value)
            const now = Date.now()
            const decrease = this.data.currentAngle > endAngle
            const startAngle = !decrease ? this.data.currentAngle : this.data.endAngle

            this.cancelNextCallback()
            this.clearTimer()

            this.safeSetData({ startAngle, endAngle }, () => {
                this.animate(now, now, decrease)
            })
        },
        /**
         * 绘制 canvas
         */
        draw(hasLine = true) {
            const props = this.data
            const { lineCap, backgroundColor, color, size, strokeWidth, counterclockwise, background } = this.data
            const position = size / 2
            const radius = position - strokeWidth / 2
            const p = 2 * Math.PI
            const startAngle = counterclockwise ? p - props.beginAngle : props.beginAngle
            const endAngle = counterclockwise ? p - (props.beginAngle + props.currentAngle) : props.beginAngle + props.currentAngle

            // 创建 canvas 绘图上下文
            getCanvasRef(props.prefixCls, this).then((canvas) => {
                const ctx = canvas.getContext('2d')
                const ratio = getSystemInfoSync(['window']).pixelRatio
                const canvasWidth = size * ratio
                const canvasHeight = size * ratio

                canvas.width = canvasWidth
                canvas.height = canvasHeight

                ctx.scale(ratio, ratio)
                // ctx.fillStyle = '#ffffff'
                ctx.fillRect(0, 0, size, size)

                // 清除画布
                ctx.clearRect(0, 0, size, size)

                // 绘制背景
                if (background) {
                    ctx.beginPath()
                    ctx.arc(position, position, radius, 0, 2 * Math.PI)
                    ctx.lineWidth = strokeWidth
                    ctx.strokeStyle = backgroundColor
                    ctx.stroke()
                }

                // 绘制进度
                if (hasLine) {
                    ctx.beginPath()
                    ctx.arc(position, position, radius, startAngle, endAngle)
                    ctx.lineWidth = strokeWidth
                    ctx.strokeStyle = color
                    ctx.lineCap = lineCap
                    ctx.stroke()
                }

                // 绘制完成
                this.triggerEvent('change', { value: this.data.currentAngle })
            })
        },
        /**
         * 开始动画
         */
        animate(c, d, e) {
            const now = Date.now()
            const f = now - c < 1 ? 1 : now - c
            const { animate, speed, startAngle, endAngle } = this.data
            const isEnd = !e && 1000 * this.data.currentAngle <= Math.floor(1000 * endAngle) || e && 1000 * this.data.currentAngle >= Math.floor(1000 * endAngle)

            if (animate && c - d < 1.05 * speed && isEnd) {
                const value = easeInOutCubic((c - d) / f, startAngle, endAngle - startAngle, speed / f)
                const currentAngle = value < 0 ? 0 : value

                c = Date.now()

                this.safeSetData({ currentAngle }, () => {
                    this.draw(currentAngle !== 0)
                    this.timer = setTimeout(() => this.animate(c, d, e), 1000 / 60)
                })
            } else {
                this.safeSetData({ currentAngle: endAngle }, () => this.draw(endAngle !== 0))
            }
        },
        /**
         * 清除定时器
         */
        clearTimer() {
            if (this.timer) {
                clearTimeout(this.timer)
                this.timer = null
            }
        },
    },
    attached() {
        this.updateStyle()
        if (this.data.percent === 0) {
            this.draw(false)
        }
    },
    detached() {
        this.clearTimer()
    },
})
