import { CHECK_FRAGMENT, CHECK_TOUCH, DROP_ALL_FRAGMENT, DROP_TOP_FRAGMENT, eventTarget, HAMMER, PLAY_DROP_SOUND, RESET_FRAGMENT, SHAKE_FRAGMENT, SHOW_FOG } from "../Events";

const { ccclass, property } = cc._decorator;

@ccclass
export default class FragmentJarController extends cc.Component {
    @property(cc.Node)
    private fragmentLink: cc.Node = null;
    @property
    private isBottom: boolean = false;

    public isDropped: boolean = false;

    private _physicsCollider: cc.PhysicsPolygonCollider = null;
    private _physicsColliderFragmentLink: cc.PhysicsPolygonCollider = null;
    private _rg: cc.RigidBody = null;
    private _siblingIndex: number = 0;
    private _numberOfBeats = 3;
    private _slots: cc.Node[] = [];

    onLoad() {
        this._physicsCollider = this.node.getComponent(cc.PhysicsPolygonCollider);
        this._physicsColliderFragmentLink = this.fragmentLink && this.fragmentLink.getComponent(cc.PhysicsPolygonCollider);
        this._rg = this.node.getComponent(cc.RigidBody);
        this._siblingIndex = this.node.getSiblingIndex();
        this._slots = this.node.children;
        this._slots.forEach(slot => slot.active = false);

        this.reset();

        eventTarget.on(CHECK_TOUCH, this.checkTouch, this);
        eventTarget.on(RESET_FRAGMENT, this.reset, this);
        eventTarget.on(DROP_ALL_FRAGMENT, this.dropFragment, this);
        eventTarget.on(DROP_TOP_FRAGMENT, this.dropFragmentTop, this);
        eventTarget.on(SHAKE_FRAGMENT, this.shakeFragment, this);
    }

    public reset() {
        this._physicsCollider.enabled = false;
        this._rg.type = cc.RigidBodyType.Static;
        this.node.angle = 0;
        this.node.setSiblingIndex(this._siblingIndex);
        this.isDropped = false;
        this._numberOfBeats = 3;
        this.node.children.forEach(slot => slot.active = false);
    }

    public shakeFragment() {
        if (this.isDropped) {
            return;
        }

        cc.tween(this.node)
            .repeat(2, cc.tween(this.node)
                .by(0.05, { position: cc.v3(-10, -10) })
                .by(0.05, { position: cc.v3(10, 10) })
            )
            .start();
    }

    private checkTouch(point: cc.Vec2) {
        if (this.isDropped || this.isBottom) {
            return;
        }
        const pos = point.clone().subtract(cc.v2(this.node.position.x, this.node.position.y));

        const isInside = cc.Intersection.pointInPolygon(pos, this._physicsCollider.points);
        let isInsideFragmentLink = false;

        if (this._physicsColliderFragmentLink) {
            const posFragmentLink = point.clone().subtract(cc.v2(this.fragmentLink.position.x, this.fragmentLink.position.y));
            isInsideFragmentLink = cc.Intersection.pointInPolygon(posFragmentLink, this._physicsColliderFragmentLink.points);
        }

        if (!isInside && !isInsideFragmentLink) {
            return;
        }

        this._numberOfBeats--;

        if (this._slots.length > 0) {
            cc.tween(this)
                .delay(0.5)
                .call(() => {
                    this._slots[Math.min(2 - this._numberOfBeats, 1)].active = true
                })
                .start();
        }

        eventTarget.emit(HAMMER, point);

        if (this._numberOfBeats == 1) {
            eventTarget.emit(SHOW_FOG, this.node.position);
        }

        if (this._numberOfBeats > 0) {
            return;
        }

        this.dropFragment();
    }

    private dropFragmentTop() {
        if (this.isBottom) {
            return;
        }
        this.dropFragment();
    }

    private dropFragment() {
        if (this.isDropped) {
            return;
        }

        this.isDropped = true;
        const pos = this.node.position;
        eventTarget.emit(CHECK_FRAGMENT, this.node);

        cc.tween(this.node)
            .delay(0.5)
            .call(() => {
                eventTarget.emit(PLAY_DROP_SOUND);
                this.node.setSiblingIndex(-1);
            })
            .to(0.1, { scale: 0.9 })
            .to(0.1, { scale: 1.05 })
            .to(0.2, { position: pos.clone().multiplyScalar(1.2) })
            .call(() => {
                this._physicsCollider.enabled = true;
                this._rg.type = cc.RigidBodyType.Dynamic;
                this._rg.gravityScale = 10;
            })
            .delay(1)
            .to(0, { scale: 1 })
            .start();
    }
}
