1/*
2 * Copyright (C) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15import { Utils, MessageParam } from "./Util"
16import { Constants, TypeConstants } from "./Constants";
17
18export class WebSocketManager {
19    static instance: WebSocketManager | null | undefined = null;
20    url: string = 'ws://localhost:18080';//8080后期需要修改
21    private websocket: WebSocket | null | undefined = null;
22    private ready: boolean = false;
23    private distributeMap: Map<number, Function> = new Map<number, Function>();
24    private sessionId: number | null | undefined;
25    private session: bigint | null | undefined;
26    private heartbeatInterval: number | null | undefined;
27
28    constructor() {
29        if (WebSocketManager.instance) {
30            return WebSocketManager.instance;
31        }
32        WebSocketManager.instance = this;
33        //连接websocket
34        this.connectWebSocket();
35    }
36    //连接WebSocket
37    connectWebSocket(): void {
38        this.websocket = new WebSocket(this.url);
39        this.websocket.binaryType = "arraybuffer";
40        this.websocket.onopen = () => {
41            // 设置心跳定时器  
42            this.sendHeartbeat();
43            // 连接后登录
44            this.login();
45        };
46
47        //接受webSocket的消息
48        this.websocket.onmessage = (event) => {
49            // 先解码
50            let decode: MessageParam = Utils.decode(event.data);
51            if (decode.type === TypeConstants.HEARTBEAT_TYPE) {
52                return;
53            }
54            this.onmessage(decode!);
55        };
56
57        this.websocket.onerror = (error) => {
58            console.error('error:', error);
59        };
60
61        this.websocket.onclose = (event) => {
62            this.initLoginInfor();
63            this.clearHeartbeat();
64            this.reconnect(event);
65        };
66    }
67
68    /**
69     * 接收webSocket返回的buffer数据
70     * 分别处理登录、其他业务的数据
71     * 其他业务数据分发
72     */
73    onmessage(decode: MessageParam): void {
74        // 解码event  调decode
75        if (decode.type === TypeConstants.LOGIN_TYPE) {// 登录
76            if (decode.cmd === Constants.LOGIN_CMD) {
77                this.ready = true;
78                this.sessionId = decode.session_id;
79                this.session = decode.session;
80            }
81        } else {// type其他
82            for (let [key, callback] of this.distributeMap.entries()) {
83                if (key === decode.type) {
84                    callback(decode.cmd, decode.data);
85                }
86            };
87        }
88    }
89
90    // 登录
91    login(): void {
92        this.websocket!.send(Utils.encode(Constants.LOGIN_PARAM));
93    }
94
95    // 模块调
96    static getInstance(): WebSocketManager | null | undefined {
97        if (!WebSocketManager.instance) {
98            new WebSocketManager();
99        }
100        return WebSocketManager.instance
101    }
102
103    // WebSocket是否登录成功 
104    isReady(): boolean {
105        return this.ready
106    }
107
108    /** 
109     * 消息监听器
110     * listener是不同模块传来接收数据的函数
111     * 模块调用
112    */
113    registerMessageListener(type: number, callback: Function): void {
114        if (!this.distributeMap.has(type)) {
115            this.distributeMap.set(type, callback);
116        }
117    }
118
119    /**
120     * 传递数据信息至webSocket
121     * 模块调
122     */
123    sendMessage(type: number, cmd?: number, data?: Uint8Array): void {
124        // 检查WebSocket是否登录成功 
125        if (!this.ready) {// 改判断条件 ready
126            return;
127        }
128        let message: MessageParam = {
129            type: type,
130            cmd: cmd,
131            session_id: this.sessionId!,
132            session: this.session!,
133            data_lenght: data ? data.byteLength : undefined,
134            data: data
135        }
136        let encode = Utils.encode(message);
137        this.websocket!.send(encode!);
138    }
139
140    /**
141     * 传递数据信息至webSocket
142     * 模块调
143    */
144    reconnect(event: unknown) {
145        //@ts-ignore
146        if (event.wasClean) {// 正常关闭
147            return
148        }
149        // 未连接成功打开定时器
150        setTimeout(() => {
151            this.connectWebSocket();
152        }, Constants.INTERVAL_TIME)
153    }
154
155    // 定时检查心跳  
156    sendHeartbeat() {
157        this.heartbeatInterval = window.setInterval(() => {
158            this.sendMessage(TypeConstants.HEARTBEAT_TYPE)
159        }, Constants.INTERVAL_TIME)
160    }
161
162    /**
163     * 重连时初始化登录信息
164     * 在异常关闭时调用
165     */
166    initLoginInfor() {
167        this.ready = false;
168        this.sessionId = null;
169        this.session = null;
170    }
171
172    // 连接关闭时,清除心跳
173    clearHeartbeat() {        
174        if (this.heartbeatInterval) {
175            clearInterval(this.heartbeatInterval);
176            this.heartbeatInterval = null;
177        }
178    }
179}
180