// Learn cc.Class:
//  - https://docs.cocos.com/creator/manual/en/scripting/class.html
// Learn Attribute:
//  - https://docs.cocos.com/creator/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
//  - https://docs.cocos.com/creator/manual/en/scripting/life-cycle-callbacks.html

const defaultData = require('../script/defaultData');
import {
    asyncTweenTo,
    asyncTweenBy,
    asyncDelay,
    asyncPlayDragonBoneAnimation,
    asyncPlayEffectByUrl,
    RandomInt
} from '../script/utils';

cc.Class({
    extends: cc.Component,

    properties: {

        letter: 'b',

        starAnimePrefab: {
            type: cc.Prefab,
            default: null,
        },
        starPreFab: {
            type: cc.Prefab,
            default: null,
        },
        sharkPreFab: {
            type: cc.Prefab,
            default: null,
        },
        arrowPrefab: {
            type: cc.Prefab,
            default: null,
        },

        letterAPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterBPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterCPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterDPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterEPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterFPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterGPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterHPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterIPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterJPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterKPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterLPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterMPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterNPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterOPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterPPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterQPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterRPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterSPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterTPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterUPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterVPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterWPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterXPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterYPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterZPrefab: {
            type: cc.Prefab,
            default: null,
        },

        letteraPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterbPrefab: {
            type: cc.Prefab,
            default: null,
        },
        lettercPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterdPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterePrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterfPrefab: {
            type: cc.Prefab,
            default: null,
        },
        lettergPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterhPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letteriPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterjPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterkPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterlPrefab: {
            type: cc.Prefab,
            default: null,
        },
        lettermPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letternPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letteroPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterpPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterqPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterrPrefab: {
            type: cc.Prefab,
            default: null,
        },
        lettersPrefab: {
            type: cc.Prefab,
            default: null,
        },
        lettertPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letteruPrefab: {
            type: cc.Prefab,
            default: null,
        },
        lettervPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterwPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterxPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letteryPrefab: {
            type: cc.Prefab,
            default: null,
        },
        letterzPrefab: {
            type: cc.Prefab,
            default: null,
        },
    },

    // LIFE-CYCLE CALLBACKS:

    // onLoad () {},

    ctor() {
        this._imageResList = [];
        this._audioResList = [];
    },

    start() {
        this.data = defaultData;
        this.getData((data) => {
            if (data) {
                this.data = data;
            }
            this.preloadItem();
        });
    },

    getData(func) {
        if (window && window.courseware) {
            window.courseware.getData(func, 'Z_05_nxuan1');
        } else {
            func(defaultData);
        }
    },

    _imageResList: null,
    _audioResList: null,
    preloadItem() {
        this.addPreloadImage();
        this.addPreloadAudio();
        this.preload();
        this.onLoadFinish();
    },

    addPreloadImage() { },

    addPreloadAudio() {
    },

    preload() {
        const preloadArr = this._imageResList.concat(this._audioResList);
        cc.assetManager.loadAny(preloadArr, null, null, (err, data) => {
            this.hideAirClassLoading();
        });
        if (preloadArr.length == 0) {
            this.hideAirClassLoading();
        }
    },

    hideAirClassLoading() {
        if (window && window["air"]) {
            window["air"].hideAirClassLoading();
            console.log('hideAirClassLoading');
        }
    },

    initListeners() {

    },

    onLoadFinish() {
        this.initListeners();

        this.letter = 'M';
        this.createLetterBig(this.letter);
    },

    createLetterBig(letter) {
        this._status = {
            currentStrokeIdx: 0,
            currentStrokeStageIdx: 0
        }

        const letterNodeBase = cc.instantiate(this[`letter${letter}Prefab`]);
        letterNodeBase.name = 'letterNodeBase';
        letterNodeBase.parent = this.node;

        const btnUpdate = letterNodeBase.getChildByName('BtnUpdate');
        btnUpdate.on('click', () => {
            this.drawCompleteLine(this._status.currentStrokeIdx);
            this._status.currentStrokeIdx++;
            if (this._status.currentStrokeIdx >= 4) {
                this._status.currentStrokeIdx = 0;
            }
            this.updateArrows(this._status.currentStrokeIdx);
        });
        this.updateArrows(this._status.currentStrokeIdx);

        // this.drawCompleteLine(this._status.currentStrokeIdx);

        let showMonkey = false;
        if ('abcdefghijklmnopqrstuvwxyz'.indexOf(letter) !== -1) {
            showMonkey = true;
        }
        const pos0 = cc.find(`Canvas/letterNodeBase/NodePosBase/NodePos_0_0`);
        this.createShark(pos0, this.getSharkAngle(0, 0), showMonkey);
    },

    getPosListByStrokeIdx(strokeIdx) {
        let stepIdx = 0;
        let pos0 = null;
        const posList = [];
        do {
            pos0 = cc.find(`Canvas/letterNodeBase/NodePosBase/NodePos_${strokeIdx}_${stepIdx}`);
            stepIdx++;

            if (pos0) {
                posList.push(pos0);
            }
        } while (pos0);
        return posList;
    },

    getCurrentCheckPos() {
        const nodePosBase = cc.find('Canvas/letterNodeBase/NodePosBase');
        let pos1 = nodePosBase.getChildByName(`NodePos_${this._status.currentStrokeIdx}_${this._status.currentStrokeStageIdx}`);
        let pos2 = nodePosBase.getChildByName(`NodePos_${this._status.currentStrokeIdx}_${this._status.currentStrokeStageIdx + 1}`);
        if (pos2) {
            return [pos1, pos2];
        }
        return null;
    },

    updateArrows(idx) {
        const nodePosBase = cc.find(`Canvas/letterNodeBase/NodePosBase`);

        if (this.arrowPrefabList) {
            this.arrowPrefabList.forEach(node => {
                node.parent = null;
            });
        }
        this.arrowPrefabList = [];

        for (let i = 0; i < 5; i++) {
            let startNode = nodePosBase.getChildByName(`NodePos_${i}_0`);
            if (!startNode) {
                break;
            }

            startNode.opacity = 0;
            let lastNode = startNode;
            for (let j = 1; j < 100; j++) {
                let endNode = nodePosBase.getChildByName(`NodePos_${i}_${j}`);
                if (!endNode) {
                    break;
                }
                endNode.opacity = 0;
                const lineVec2 = cc.v2(endNode.x - lastNode.x, endNode.y - lastNode.y);
                const lineLength = lineVec2.mag();

                const arrowNumber = Math.ceil(lineLength / 100);

                for (let k = 0; k < arrowNumber; k++) {
                    if (i != idx) {
                        continue;
                    }
                    const arrowPrefab = cc.instantiate(this.arrowPrefab);
                    arrowPrefab.x = lastNode.x + lineVec2.x * (k + 1) / (arrowNumber + 1);
                    arrowPrefab.y = lastNode.y + lineVec2.y * (k + 1) / (arrowNumber + 1);
                    arrowPrefab.angle = cc.v2(1, 0).signAngle(lineVec2) * 180 / Math.PI;
                    arrowPrefab.name = name;
                    arrowPrefab.parent = this.node;
                    this.arrowPrefabList.push(arrowPrefab);

                    const nodeArrow = arrowPrefab.getChildByName('NodeArrow');
                    cc.tween(nodeArrow)
                        .delay((j + k) * 0.3)
                        .to(0.2, { opacity: 0 })
                        .delay(0.5)
                        .to(0.2, { opacity: 255 })
                        .delay(7 - (j + k) * 0.3)
                        .union()
                        .repeatForever()
                        .start();
                }
                lastNode = endNode;
            }
            if (i == idx) {
                this.createStar(lastNode);
            }
        }
    },

    createShark(startNode, angle, monkey = false) {
        let sharkNode = null;
        if (!this.sharkNode) {
            this.sharkNode = cc.instantiate(this.sharkPreFab);
            this.sharkNode.parent = this.node;
            this.sharkNode.zIndex = 2;
            this.sharkNode.x = startNode.x;
            this.sharkNode.y = startNode.y;
            this.addSharkListeners();
        }
        sharkNode = this.sharkNode.getChildByName('SharkNode');
        sharkNode.angle = angle;
        sharkNode.active = !monkey;

        const monkeyNode = this.sharkNode.getChildByName('MonkeyNode');
        monkeyNode.active = monkey;

        cc.tween(this.sharkNode)
            .to(0.1, { x: startNode.x, y: startNode.y })
            .start();
        cc.tween(sharkNode)
            .to(0.1, { angle: angle })
            .start();
    },

    onSharkMoveStart(event) {
        this.sharkNode.startPos = event.getLocation();
        this.onDrawStart(event);
    },

    onSharkMoving(event) {
        if (!this.sharkNode) {
            return;
        }
        const startPos = this.sharkNode.startPos;
        if (!startPos) {
            return;
        }
        const location = event.getLocation();
        const currentPosList = this.getCurrentCheckPos();
        if (!currentPosList) {
            return;
        }
        const [pos1, pos2] = currentPosList;
        const checkLine = cc.v2(pos2.x - pos1.x, pos2.y - pos1.y);

        const locationPos = this.node.convertToNodeSpaceAR(location);
        this.sharkNode.x = locationPos.x;
        this.sharkNode.y = locationPos.y;

        const trueSharkNode = this.sharkNode.getChildByName('SharkNode');

        let angle = cc.v2(1, 0).signAngle(checkLine) * 180 / Math.PI;
        if (angle - trueSharkNode.angle < -180) {
            angle += 360;
        }
        if (angle - trueSharkNode.angle > 180) {
            angle -= 360;
        }

        if (!this.sharkNode.angleAction) {
            this.sharkNode.angleAction = true;
            cc.tween(trueSharkNode)
                .to(0.2, { angle: angle })
                .call(() => {
                    this.sharkNode.angleAction = false;
                })
                .start();
        }

        if (this.checkPointInCircle(this.sharkNode, pos2, 30)) {
            console.log('this._status.currentStrokeStageIdx = ' + this._status.currentStrokeStageIdx);
            this._status.currentStrokeStageIdx++
            if (this.getCurrentCheckPos() === null) {
                this.drawCompleteLine(this._status.currentStrokeIdx);
                this.showStar();
                this._status.currentStrokeIdx++;
                this._status.currentStrokeStageIdx = 0;
                if (this.getCurrentCheckPos() === null) {
                    this.showOver();
                }
                this.updateArrows(this._status.currentStrokeIdx);
                this.onSharkMoveEnd(event);
            }
        }

        const posList = this.getPosListByStrokeIdx(this._status.currentStrokeIdx);
        if (!this.checkPointInRangeList(this.node.convertToNodeSpaceAR(location), posList, 30)) {
            this._status.currentStrokeStageIdx = 0;
            this.onSharkMoveEnd(event);
            return;
        }

        this.onDrawMove(event);
    },

    getSharkAngle(currentStrokeIdx, currentStrokeStageIdx) {
        let pos1 = cc.find(`Canvas/letterNodeBase/NodePosBase/NodePos_${currentStrokeIdx}_${currentStrokeStageIdx}`);
        let pos2 = cc.find(`Canvas/letterNodeBase/NodePosBase/NodePos_${currentStrokeIdx}_${currentStrokeStageIdx + 1}`);
        const line = cc.v2(pos2.x - pos1.x, pos2.y - pos1.y);
        return cc.v2(1, 0).signAngle(line) * 180 / Math.PI;
    },

    onSharkMoveEnd(event) {
        let pos = cc.find(`Canvas/letterNodeBase/NodePosBase/NodePos_${this._status.currentStrokeIdx}_0`);
        if (!pos) {
            return;
        }
        this._status.currentStrokeStageIdx = 0;

        cc.tween(this.sharkNode)
            .to(0.1, { x: pos.x, y: pos.y })
            .start();

        cc.tween(this.sharkNode.getChildByName('SharkNode'))
            .to(0.1, { angle: this.getSharkAngle(this._status.currentStrokeIdx, 0) })
            .start();

        this.sharkNode.startPos = null;
        this.onDrawEnd(event);
    },

    addSharkListeners() {
        this.sharkNode.on('touchstart', this.onSharkMoveStart, this);
        this.sharkNode.on('touchmove', this.onSharkMoving, this);
        this.sharkNode.on('touchend', this.onSharkMoveEnd, this);
        this.sharkNode.on('touchcancel', this.onSharkMoveEnd, this);
    },

    hideShark() {
        cc.tween(this.sharkNode)
            .to(0.3, { opacity: 0 })
            .call(() => {
                this.sharkNode.parent = null;
                this.sharkNode = null;
            })
            .start();
    },

    async showOver() {
        this.hideShark();
        await this.showCloud();
        await this.showCompleteStar();
    },

    async showCompleteStar() {

    },

    async showCloud() {

    },

    createStar(endNode) {
        if (this.starNode) {
            this.starNode.parent = null;
            this.starNode = null;
        }
        const starNode = cc.instantiate(this.starPreFab);
        starNode.parent = this.node;
        starNode.x = endNode.x;
        starNode.y = endNode.y;
        this.starNode = starNode;
    },

    async showStar() {
        let starAnimeNode = cc.instantiate(this.starAnimePrefab);
        starAnimeNode.parent = this.node;
        starAnimeNode.x = this.starNode.x;
        starAnimeNode.y = this.starNode.y;

        cc.tween(this.starNode)
            .to(0.1, { opacity: 0 })
            .start();

        await asyncPlayDragonBoneAnimation(starAnimeNode, 'star', 1);
        starAnimeNode.parent = null;
        starAnimeNode = null;
    },

    drawCompleteLine(strokeIdx) {
        const drawGraphyNode = cc.find('Canvas/letterNodeBase/DrawMaskNode/DrawGraphyNodeBase');
        this.drawLines(drawGraphyNode, this.getPosListByStrokeIdx(strokeIdx));
    },

    drawLines(drawNode, posList) {
        const graph = drawNode.getComponent(cc.Graphics);
        posList.forEach((pos, idx) => {
            if (idx == 0) {
                graph.moveTo(pos.x, pos.y);
                return;
            }
            graph.lineTo(pos.x, pos.y);
        });
        graph.stroke();
    },

    onDrawStart(event) {
        const drawGraphyNode = cc.find('Canvas/letterNodeBase/DrawMaskNode/DrawGraphyNodeCurrent')
        const graph = drawGraphyNode.getComponent(cc.Graphics);
        const location = event.getLocation();
        const pos = drawGraphyNode.convertToNodeSpaceAR(location);
        graph.moveTo(pos.x, pos.y);
    },

    onDrawMove(event) {
        if (!this._moveCount) {
            this._moveCount = 0;
        }
        this._moveCount++;
        if (this._moveCount < 5) {
            return;
        }
        this._moveCount = 0
        const drawGraphyNode = cc.find('Canvas/letterNodeBase/DrawMaskNode/DrawGraphyNodeCurrent')
        const graph = drawGraphyNode.getComponent(cc.Graphics);
        const location = event.getLocation();
        const pos = drawGraphyNode.convertToNodeSpaceAR(location);
        graph.lineTo(pos.x, pos.y);
        graph.stroke();
    },

    onDrawEnd(event) {
        const drawGraphyNode = cc.find('Canvas/letterNodeBase/DrawMaskNode/DrawGraphyNodeCurrent')
        const graph = drawGraphyNode.getComponent(cc.Graphics);
        graph.clear();
    },

    addDrawListeners() {
        const drawGraphyNode = cc.find('Canvas/letterNodeBase/DrawMaskNode/DrawGraphyNodeCurrent');
        drawGraphyNode.on('touchstart', this.onDrawStart, this);
        drawGraphyNode.on('touchmove', this.onDrawMove, this);
        drawGraphyNode.on('touchend', this.onDrawEnd, this);
        drawGraphyNode.on('touchcancel', this.onDrawEnd, this);
    },

    checkPointInCircle(pos, center, radius) {
        const line = cc.v2(pos.x - center.x, pos.y - center.y);
        if (line.mag() > radius) {
            return false;
        }
        return true;
    },

    checkPointInRangeList(pos, rangePosList, radius) {
        let inRange = false;
        rangePosList.forEach((rangePos, idx) => {
            if (idx == 0) {
                return;
            }
            inRange = inRange || this.checkPointInRange(pos, rangePosList[idx - 1], rangePos, radius);
        });
        return inRange;
    },

    checkPointInRange(pos, rangePos1, rangePos2, radius) {
        const linePos2 = cc.v2(rangePos2.x - rangePos1.x, rangePos2.y - rangePos1.y);
        const linePos = cc.v2(pos.x - rangePos1.x, pos.y - rangePos1.y);
        const angle = linePos2.signAngle(linePos);
        const distance = linePos.mag() * Math.sin(angle);
        // 如果pos点到pos1与pos2连线的距离大于半径，则超出
        if (distance > radius || distance < - radius) {
            return false;
        }
        const projection = linePos.mag() * Math.cos(angle);
        // 如果pos1到pos点的向量到pos1到pos2的向量投影长度超出范围
        if (projection < - radius || projection > radius + linePos2.mag()) {
            return false;
        }

        return true;
    },
});
