import * as ws from "ws";
import * as http from "http";
import * as https from "https";
import * as url from "url";
import * as fs from "fs";
import * as path from "path";
import * as electron from "electron";
const WebSocket = ws;
const WebSocketServer = ws.Server;

interface standardResponse {
    status?: Number,
    message?: String,
    data?: any,
    type?: string,
}


export class wsapi {
    static clients: any;
    port: any = 26369
    wss: any = null
    clients: any = []
    private _win : any;
    constructor(win : any) {
        this._win = win;
    }
    
    
    createId() {
        // create random guid
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            var r = Math.random() * 16 | 0,
                v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }
    public async InitWebSockets () {
        electron.ipcMain.on('wsapi-updatePlaybackState', (event :any, arg :any) => {
            this.updatePlaybackState(arg);
        })

        electron.ipcMain.on('wsapi-returnQueue', (event :any, arg :any) => {
            this.returnQueue(JSON.parse(arg));
        });

        electron.ipcMain.on('wsapi-returnSearch', (event :any, arg :any) => {
            console.log("SEARCH")
            this.returnSearch(JSON.parse(arg));
        });

        electron.ipcMain.on('wsapi-returnSearchLibrary', (event :any, arg :any) => {
            this.returnSearchLibrary(JSON.parse(arg));
        });

        electron.ipcMain.on('wsapi-returnDynamic', (event :any, arg :any, type :any) => {
            this.returnDynamic(JSON.parse(arg), type);
        });

        electron.ipcMain.on('wsapi-returnMusicKitApi', (event :any, arg :any, method :any) => {
            this.returnMusicKitApi(JSON.parse(arg), method);
        });

        electron.ipcMain.on('wsapi-returnLyrics', (event :any, arg :any) => {
            this.returnLyrics(JSON.parse(arg));
        });
        this.wss = new WebSocketServer({
            port: this.port,
            perMessageDeflate: {
                zlibDeflateOptions: {
                    // See zlib defaults.
                    chunkSize: 1024,
                    memLevel: 7,
                    level: 3
                },
                zlibInflateOptions: {
                    chunkSize: 10 * 1024
                },
                // Other options settable:
                clientNoContextTakeover: true, // Defaults to negotiated value.
                serverNoContextTakeover: true, // Defaults to negotiated value.
                serverMaxWindowBits: 10, // Defaults to negotiated value.
                // Below options specified as default values.
                concurrencyLimit: 10, // Limits zlib concurrency for perf.
                threshold: 1024 // Size (in bytes) below which messages
                // should not be compressed if context takeover is disabled.
            }
        })
        console.log(`WebSocketServer started on port: ${this.port}`);

        const defaultResponse :standardResponse = {status :0, data:{}, message:"OK", type:"generic"};


        this.wss.on('connection', (ws : any) => {
            ws.id = this.createId();
            console.log(`Client ${ws.id} connected`)
            this.clients.push(ws);
            ws.on('message', function incoming(message : any) {

            });
            // ws on message
            ws.on('message', (message : any) => {
                let data = JSON.parse(message);
                let response :standardResponse = {status :0, data:{}, message:"OK", type:"generic"};
                if (data.action) {
                    data.action.toLowerCase();
                }
                switch (data.action) {
                    default:
                        response.message = "Action not found";
                        break;
                    case "identify":
                        response.message = "Thanks for identifying!"
                        response.data = {
                            id: ws.id
                        }
                        ws.identity = {
                            name: data.name,
                            author: data.author,
                            description: data.description,
                            version: data.version
                        }
                        break;
                    case "play-next":
                        this._win.webContents.executeJavaScript(`wsapi.playNext(\`${data.type}\`,\`${data.id}\`)`);
                        response.message = "Play Next";
                        break;
                    case "play-later":
                        this._win.webContents.executeJavaScript(`wsapi.playLater(\`${data.type}\`,\`${data.id}\`)`);
                        response.message = "Play Later";
                        break;
                    case "quick-play":
                        this._win.webContents.executeJavaScript(`wsapi.quickPlay(\`${data.term}\`)`);
                        response.message = "Quick Play";
                        break;
                    case "get-lyrics":
                        this._win.webContents.executeJavaScript(`wsapi.getLyrics()`);
                        break;
                    case "shuffle":
                        this._win.webContents.executeJavaScript(`wsapi.toggleShuffle()`);
                        break;
                    case "set-shuffle":
                        if(data.shuffle == true) {
                            this._win.webContents.executeJavaScript(`MusicKit.getInstance().shuffleMode = 1`);
                        }else{
                            this._win.webContents.executeJavaScript(`MusicKit.getInstance().shuffleMode = 0`);
                        }
                        break;
                    case "repeat":
                        this._win.webContents.executeJavaScript(`wsapi.toggleRepeat()`);
                        break;
                    case "seek":
                        this._win.webContents.executeJavaScript(`MusicKit.getInstance().seekToTime(${parseFloat(data.time)})`);
                        response.message = "Seek";
                        break;
                    case "pause":
                        this._win.webContents.executeJavaScript(`MusicKit.getInstance().pause()`);
                        response.message = "Paused";
                        break;
                    case "play":
                        this._win.webContents.executeJavaScript(`MusicKit.getInstance().play()`);
                        response.message = "Playing";
                        break;
                    case "stop":
                        this._win.webContents.executeJavaScript(`MusicKit.getInstance().stop()`);
                        response.message = "Stopped";
                        break;
                    case "volume":
                        this._win.webContents.executeJavaScript(`MusicKit.getInstance().volume = ${parseFloat(data.volume)}`);
                        response.message = "Volume";
                        break;
                    case "mute":
                        this._win.webContents.executeJavaScript(`MusicKit.getInstance().mute()`);
                        response.message = "Muted";
                        break;
                    case "unmute":
                        this._win.webContents.executeJavaScript(`MusicKit.getInstance().unmute()`);
                        response.message = "Unmuted";
                        break;
                    case "next":
                        this._win.webContents.executeJavaScript(`MusicKit.getInstance().skipToNextItem()`);
                        response.message = "Next";
                        break;
                    case "previous":
                        this._win.webContents.executeJavaScript(`MusicKit.getInstance().skipToPreviousItem()`);
                        response.message = "Previous";
                        break;
                    case "musickit-api":
                        this._win.webContents.executeJavaScript(`wsapi.musickitApi(\`${data.method}\`, \`${data.id}\`, ${JSON.stringify(data.params)} , ${data.library})`);
                        break;
                    case "musickit-library-api":
                        break;
                    case "set-autoplay":
                        this._win.webContents.executeJavaScript(`wsapi.setAutoplay(${data.autoplay})`);
                        break;
                    case "queue-move":
                        this._win.webContents.executeJavaScript(`wsapi.moveQueueItem(${data.from},${data.to})`);
                        break;
                    case "get-queue":
                        this._win.webContents.executeJavaScript(`wsapi.getQueue()`);
                        break;
                    case "search":
                        if (!data.limit) {
                            data.limit = 10;
                        }
                        this._win.webContents.executeJavaScript(`wsapi.search(\`${data.term}\`, \`${data.limit}\`)`);
                        break;
                    case "library-search":
                        if (!data.limit) {
                            data.limit = 10;
                        }
                        this._win.webContents.executeJavaScript(`wsapi.searchLibrary(\`${data.term}\`, \`${data.limit}\`)`);
                        break;
                    case "show-window":
                        this._win.show()
                        break;
                    case "hide-window":
                        this._win.hide()
                        break;
                    case "play-mediaitem":
                        this._win.webContents.executeJavaScript(`wsapi.playTrackById("${data.id}", \`${data.kind}\`)`);
                        response.message = "Playing track";
                        break;
                    case "get-status":
                        response.data = {
                            isAuthorized: true
                        };
                        response.message = "Status";
                        break;
                    case "get-currentmediaitem":
                        this._win.webContents.executeJavaScript(`wsapi.getPlaybackState()`);
                        break;
                    case "quit":
                        electron.app.quit();
                    break;
                }
                ws.send(JSON.stringify(response));
            });

            ws.on('close', () => {
                // remove client from list
                this.clients.splice(wsapi.clients.indexOf(ws), 1);
                console.log(`Client ${ws.id} disconnected`);
            });
            ws.send(JSON.stringify(defaultResponse));
        });
    }
    sendToClient(id : any) {
        // replace the clients.forEach with a filter to find the client that requested
    }
    updatePlaybackState(attr : any) {
        const response : standardResponse = {status: 0, data: attr, message: "OK",  type:"playbackStateUpdate"};
        this.clients.forEach(function each(client: any) {
            client.send(JSON.stringify(response));
        });
    }
    returnMusicKitApi(results :any, method :any) {
        const response : standardResponse = {status :0, data: results, message:"OK", type:`musickitapi.${method}`};
        this.clients.forEach(function each(client :any) {
            client.send(JSON.stringify(response));
        });
    }
    returnDynamic(results :any, type :any) {
        const response : standardResponse = {status :0, data: results, message: "OK", type: type};
        this.clients.forEach(function each(client :any) {
            client.send(JSON.stringify(response));
        });
    }
    returnLyrics(results :any) {
        const response : standardResponse = {status :0, data: results, message:  "OK", type: "lyrics"};
        this.clients.forEach(function each(client :any) {
            client.send(JSON.stringify(response));
        });
    }
    returnSearch(results :any) {
        const response : standardResponse = {status :0, data: results, message: "OK", type: "searchResults"};
        this.clients.forEach(function each(client :any) {
            client.send(JSON.stringify(response));
        });
    }
    returnSearchLibrary(results :any) {
        const response: standardResponse = {status :0, data :results, message:"OK", type:"searchResultsLibrary"};
        this.clients.forEach(function each(client :any) {
            client.send(JSON.stringify(response));
        });
    }
    returnQueue(queue :any) {
        const response : standardResponse = {status :0,data :queue, message:"OK", type:"queue"};
        this.clients.forEach(function each(client :any) {
            client.send(JSON.stringify(response));
        });
    }
}