const SEND_MESSAGE_ACTION = "send-message"           // 채팅 메시지
const USER_JOINED_ACTION = "user-join"               // 회원 접속
const USER_LEFT_ACTION = "user-left"                 // 회원 접속 종료
const JOIN_ROOM_ACTION = "join-room"                 // 방 입장
const LEAVE_ROOM_ACTION = "leave-room"               // 방 퇴장
const JOIN_ROOM_PRIVATE_ACTION = "join-room-private" // 방 비밀 입장
const ROOM_JOINED_ACTION = "room-joined"             // 방 입장함
const DUPLICATION_ACTION = "duplication"             // 회원 중복
const CHAT_HISTORY_ACTION = "set-history"
const SERVER_URL = "ws://localhost:7070";

const ChatClient = class {
    constructor(url) {
        this.url = url;
    }

    isConnected = null;
    socket = null;

    user = null;
    rooms = [];
    users = [];

    // 작업영역
    notice = null;
    log = null;
    message = null;
    btnPush = null;

    // 글자크기
    fontSize = 14;

    init() {
        if (!window.WebSocket) {
            this.setWarning("해당 브라우저는 채팅을 지원하지 않습니다. 최신 버전의 브라우저를 설치 후 실행 해주세요.")
        } else {
            this.isConnected = false;
            this.notice = document.getElementById("chatNotice");
            this.log = document.getElementById("chatLog");
            this.message = document.getElementById("message");
            this.btnPush = document.getElementById("btnPushMessage");
            this.connect();
            let $this = this;

            this.socket.onopen = function() {
                console.log("[Connected]");

                $this.isConnected = true;
                $this.btnPush.removeAttribute("disabled");
                $this.message.focus();

                $this.setNotice("서버와 연결 되었습니다.");
                $this.setEvent();
                $this.emit(CHAT_HISTORY_ACTION);
            };

            this.socket.onmessage = function (e) {
                let data = e.data;
                data = data.split(/\r?\n/);

                for (let i = 0; i < data.length; i++) {
                    let msg = JSON.parse(data[i]);
                    switch (msg.action) {
                        case DUPLICATION_ACTION:
                            $this.setAlert("중복접속으로 이전접속을 종료합니다.");
                            break;
                        case SEND_MESSAGE_ACTION:
                            $this.setMessage(msg)
                            break;
                        case USER_JOINED_ACTION:
                            $this.handleUserJoined(msg);
                            break;
                        case USER_LEFT_ACTION:
                            $this.handleUserLeft(msg);
                            break;
                        case ROOM_JOINED_ACTION:
                            break;
                        case CHAT_HISTORY_ACTION:
                            $this.handleSetHistory(msg);
                            break;
                        default:
                            break;
                    }
                }
            };

            this.socket.onerror = function() {
                $this.setWarning("연결 요청에 실패하였습니다.");
            };

            this.socket.onclose = function() {
                $this.setFailed("서버 연결이 종료되었습니다.");
            };
        }
    }

    // 서버 소켓 연결
    connect() {
        try {
            let query = "?";

            // 로그인 회원 정보
            if (IS_USER) {
                let userInfo = document.getElementById("userInfo").value.trim();
                if (typeof userInfo !== "undefined") {
                    this.user = JSON.parse(atob(userInfo, true));
                    query += "bearer=" + this.user.token;
                }
            }else{
                query += "name=" + generateRandomString(6);
            }

            // 웹소켓 연결
            this.socket = new WebSocket(this.url + query);
        } catch (err) {
            this.setFailed(err);
        }
    }

    // 클라이언트 기능 처리 담당
    setEvent() {
        // 메시지 전송
        document.getElementById("btnPushMessage").addEventListener("click", function (e) {
            this.emit(SEND_MESSAGE_ACTION, {
                message: this.message.value
            });
        }.bind(this));
        this.message.addEventListener("keyup", function (e) {
            if (e.key === "Enter" || e.keyCode === 13) {
                this.emit(SEND_MESSAGE_ACTION, {
                    message: e.target.value
                });
                e.preventDefault();
            }
        }.bind(this));

        // 새창으로
        document.getElementById("btnPopupChat").addEventListener("click", function() {
            window.open(window.location.href, "master",  "popup=1,toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=no,copyhistory=no");
        });

        // 청소하기
        document.getElementById("btnTxtClear").addEventListener("click", function() {
            this.clear();
        }.bind(this));

        // 글자크게
        document.getElementById("btnTxtPlus").addEventListener("click", function(e) {
            e.stopPropagation();
            this.textResize(1);
        }.bind(this));

        // 글자작게
        document.getElementById("btnTxtMinus").addEventListener("click", function(e) {
            e.stopPropagation();
            this.textResize(-1);
        }.bind(this));

        // 새로고침
        document.getElementById("btnTxtRefresh").addEventListener("click", function() {
            this.refresh();
        }.bind(this));
    }

    // 서버에 송신 담당
    emit(name, data) {
        if (this.socket.readyState === 1) {
            this.socket.send(JSON.stringify(Object.assign({action: name}, data)));
        }else{
            this.setFailed("통신이 불가합니다. 새로고침 후 다시 시도하세요.");
        }
    }

    // 송/수신 메시지 표시
    setDisplay(s) {
        $(this.log).append(s.trim());
        this.log.scrollTop = this.log.scrollHeight;
        this.message.value = "";
    }

    // 일반 메시지
    setMessage(s) {
        this.setDisplay(`<em style="font-size: ${this.fontSize}px;">${s.sender.User.Name}： ${s.message}</em>`);
    }

    // 공지사항
    setNotice(s) {
        this.setDisplay('<p class="notice">' + s + '</p>');
    }

    // 경고
    setAlert(s) {
        this.setDisplay('<p class="alert">' + s + '</p>');
    }

    // 알림
    setWarning(s) {
        this.setDisplay('<p class="warning">' + s + '</p>');
    }

    // 실패
    setFailed(s) {
        this.setDisplay('<p class="failed">' + s + '</p>');
    }

    // 글자 크게/작게
    textResize(num) {
        let n = Number(num);
        let fontSize = (Number(this.fontSize) + n);
        if (!(fontSize > 10 && fontSize < 23)) {
            return;
        }
        document.getElementById("chatLog").querySelectorAll("em").forEach(function (row) {
            row.style.fontSize = (fontSize + "px");
        });
        this.fontSize = fontSize;
    }

    // 청소하기
    clear() {
        this.message.focus();
        this.log.innerText = "";
        this.setAlert("채팅방이 청소되었습니다.");
    }

    // 새로고침
    refresh() {
        window.location.reload();
    }

    // 이미 존재하는 회원인지 확인
    userExists(user) {
        for (let i = 0; i < this.users.length; i++) {
            if (this.users[i].id == user.id) {
                return true;
            }
        }
        return false;
    }

    // 회원 입장
    handleUserJoined(msg) {
        document.getElementById("chatUserRows").innerText = msg.message;
        if (!this.userExists(msg.sender)) {
            this.users.push(msg.sender);
        }
    }

    // 회원 퇴장
    handleUserLeft(msg) {
        document.getElementById("chatUserRows").innerText = msg.message;
        for (let i = 0; i < this.users.length; i++) {
            if (this.users[i].id == msg.sender.id) {
                this.users.splice(i, 1);
                return;
            }
        }
    }

    handleSetHistory(msg) {
        let histories = JSON.parse(msg.message.replace("\x00", ""));
        if (histories.length > 0) {
            for (let row of histories) {
                this.setMessage(row);
            }
        }
    }
}

let client;
window.addEventListener("DOMContentLoaded", function() {
    client = new ChatClient(SERVER_URL);
    client.init();
});
