import "../env";
import { _decorator, Component, Node, Prefab, instantiate, Camera, Label } from 'cc';
const { ccclass, property } = _decorator;

import { GateClient } from '../../shared/gateClient/gateClient';
import { ServerListItem } from "../ui/ServerListItem";
import { IGameServerInfo } from "../../shared/gateClient/GameServer";
import { GameClient } from "../../shared/gameClient/gameClient";
import { FrameSyncExecutor } from "../common/FrameSyncExecutor";
import { PlayerData } from "./PlayerData";
import { PlayerComponent } from "./PlayerComponent";
import { ConnectionInputOperate } from "../../shared/gameClient/GameSyncFrame";
import { InputType } from "../../shared/gameClient/games/DemoSimpleFrameSyncModels";
import { JoystickComponent } from "../common/ui/JoystickComponent";
import { IMoveDirection } from "../common/ui/BaseJoystick";

@ccclass('Demo1SceneManager')
export class Demo1SceneManager extends Component {
    gateClient?: GateClient;
    gameClient?: GameClient;
    @property(Label)
    DebugInfo?: Label;

    @property(Prefab)
    ServerInfoListItemTpl?: Prefab;
    @property(Node)
    ServerList?: Node;

    @property(Node)
    ViewServerList?: Node;
    @property(Node)
    ViewGameUI?: Node;
    @property(Node)
    ViewGame3D?: Node;

    @property(Node)
    PlayersContainer?: Node;
    @property(Prefab)
    PlayerPrefab?: Prefab;

    @property(Camera)
    MainCamera?: Camera;

    @property(JoystickComponent)
    Joystick?: JoystickComponent;

    frameIndex = 0;

    frameSyncExecutor?: FrameSyncExecutor;
    allPlayers: { [key: string]: PlayerData } = {};
    myPlayer?: PlayerComponent;

    start() {
        this.gateClient = globalThis.getGateClient("http://127.0.0.1:7100");
        this.refreshList();
        this.stopGame();
        this.Joystick!.onmove = (dir) => {
            this.onJoysickMoveStart(dir);
        };
        this.Joystick!.onmoveend = () => {
            this.onJoysickMoveEnd();
        };

        setInterval(() => {
            this.DebugInfo!.string = "frameIndex:" + this.frameIndex;
        }, 100);
    }

    async refreshList() {
        if (!this.ServerInfoListItemTpl || !this.ServerList) return;
        var serverListRet = await this.gateClient!.getGameServers();
        if (!serverListRet.isSucc) {
            this.gateClient!.client.logger?.error("获取游戏服务器列表失败:" + serverListRet.err);
            return;
        }
        this.ServerList.removeAllChildren();
        serverListRet.res.GameServerList.forEach(serverInfo => {
            let itemNode = instantiate(this.ServerInfoListItemTpl!);
            var siComp = itemNode.getComponent(ServerListItem);
            if (siComp) {
                siComp.serverInfo = serverInfo;
                siComp.onClick = (sv) => this.onServerItemClick(sv);
                this.ServerList?.addChild(itemNode);
            }
        });
    }

    async onServerItemClick(sv: IGameServerInfo) {
        if (!this.gateClient) return;

        //从入口服务器获取登录token,这是模拟登录的实现(正常需要输入用户名或密码,或者第三方授权,在入口服务器获得登录token)
        var ret = await this.gateClient.loginByCustomUserName(sv, "testUser");
        if (!ret.succ) {
            return this.gateClient.client.logger?.error(ret.err);
        }

        //登录到游戏服务器
        this.gameClient = globalThis.getGameClient(sv.serverWSUrl);
        this.gameClient.disconnectHandler = () => {
            return true;
        }
        this.gameClient.onReconnectResult = (succ, err) => {
            if(!succ){
                this.stopGame();
            }
            return;
        };
        var err = await this.gameClient.login(ret.data!);
        if (err) {
            return this.gateClient.client.logger?.error(err);
        }

        if (this.ViewServerList) this.ViewServerList.active = false;
        if (this.ViewGameUI) this.ViewGameUI.active = true;
        if (this.ViewGame3D) this.ViewGame3D.active = true;

        this.startGame();
    }
    onExitGameClick() {
        this.stopGame();
    }

    async stopGame() {

        await this.frameSyncExecutor?.dispose();

        if (this.ViewServerList) this.ViewServerList.active = true;
        if (this.ViewGameUI) this.ViewGameUI.active = false;
        if (this.ViewGame3D) this.ViewGame3D.active = false;
    }

    startGame() {
        this.frameSyncExecutor = new FrameSyncExecutor(this.gameClient!, this,
            (stateData) => this.onAfterFrames(stateData),
            (dt, frameIndex) => this.executeOneFrame(dt, frameIndex),
            () => this.allPlayers);
    }

    update(dt: number) {
    }
    lateUpdate() {
        if (this.myPlayer) {
            this.MainCamera?.node.setPosition(
                this.myPlayer.node.position.x,
                this.myPlayer.node.position.y + 10,
                this.myPlayer.node.position.z + 20);
            this.MainCamera?.node.lookAt(this.myPlayer.node.position);
        }
    }

    onAfterFrames(stateData: any) {
        this.allPlayers = stateData;
        //重新创建所有节点
        this.PlayersContainer?.removeAllChildren();
        for (var connId in this.allPlayers) {
            var p = this.allPlayers[connId];
            var newPlayer = instantiate(this.PlayerPrefab!);
            newPlayer.name = "PlayerId_" + connId;
            var playerComp = newPlayer.getComponent(PlayerComponent)!;
            playerComp.init(p);
            this.PlayersContainer?.addChild(newPlayer);

            if (connId == this.gameClient?.connectionId) {
                this.myPlayer = playerComp;
            }
        }
    }

    execInput_NewPlayer(connId: string, inputFrame: ConnectionInputOperate, dt: number): void {
        const p = new PlayerData();
        p.connId = connId;
        p.userName = inputFrame.userName;
        this.allPlayers[connId] = p;

        var newPlayer = instantiate(this.PlayerPrefab!);
        newPlayer.name = "PlayerId_" + connId;
        var playerComp = newPlayer.getComponent(PlayerComponent)!;
        playerComp.init(p);
        this.PlayersContainer?.addChild(newPlayer);

        if (connId == this.gameClient?.connectionId) {
            this.myPlayer = playerComp;
        }
    }
    execInput_RemovePlayer(connId: string, inputFrame: ConnectionInputOperate, dt: number): void {
        const p = this.allPlayers[connId];
        delete this.allPlayers[connId];

        var playerNode = this.PlayersContainer?.getChildByName("PlayerId_" + p.connId);
        if (playerNode) this.PlayersContainer?.removeChild(playerNode);
    }
    execInput_MoveDirStart(connId: string, inputFrame: ConnectionInputOperate, dt: number): void {
        const player = this.allPlayers[connId] as PlayerData;
        if (!player) return;
        player.moveDirX = inputFrame.dirX;
        player.moveDirZ = inputFrame.dirZ;
        player.moveAcuteRad = inputFrame.acuteRad;
    }
    execInput_MoveDirEnd(connId: string, inputFrame: ConnectionInputOperate, dt: number): void {
        const player = this.allPlayers[connId];
        if (!player) return;
        player.moveDirX = 0;
        player.moveDirZ = 0;
        player.moveAcuteRad = 0;
    }
    executeOneFrame(dt: number, frameIndex: number): void {
        this.frameIndex = frameIndex;
        for (var connId in this.allPlayers) {
            var p = this.allPlayers[connId];
            this.playerUpdate(p, dt);
        }
    }
    playerUpdate(player: PlayerData, dt: number) {
        if (player.moveDirX != 0 || player.moveDirZ != 0) {
            //有移动,转向直接生效,小方块,懒得转忽略
            var distance = player.speed * dt;//本帧移动的距离

            var oldX = player.x;
            var oldZ = player.z;

            var cosA = Math.cos(player.moveAcuteRad);
            var sinA = Math.sin(player.moveAcuteRad);

            var stepReC = distance;

            var stepReX = player.moveDirZ == 0 ?
                player.moveDirX * distance : cosA * stepReC * player.moveDirX;//如果Z方向没动,直接用X方向的距离
            var stepReZ = player.moveDirX == 0 ?
                player.moveDirZ * distance : sinA * stepReC * player.moveDirZ;//如果X方向没动,直接用Z方向的距离

            player.x = oldX + stepReX;
            player.z = oldZ + stepReZ;
        }
    }
    onJoysickMoveStart(move: IMoveDirection) {
        this.frameSyncExecutor?.sendInputFrame(
            {
                inputType: InputType.MoveDirStart,
                dirX: move.dirX,
                dirZ: move.dirZ,
                acuteRad: move.acuteRad,
            }
        );
    }
    onJoysickMoveEnd() {
        this.frameSyncExecutor?.sendInputFrame(
            {
                inputType: InputType.MoveDirEnd,
            }
        );
    }
}