import * as electron from 'electron';
import * as os from 'os';
import * as fs from 'fs';
import { join, resolve } from 'path';
import * as CiderReceiver from '../base/castreceiver';
import fetch from 'electron-fetch';
import {Stream} from "stream";
import {spawn} from 'child_process';
import {Worker} from 'worker_threads';
import { Blob } from 'buffer'; 


export default class RAOP {

    /**
     * Private variables for interaction in plugins
     */
    private _utils: any;
    private _win: any;
    private _app: any;
    private _store: any;
    private _cacheAttr: any;
    private u: any;
    private ipairplay: any = "";
    private portairplay: any = "";
    
    private airtunes: any;
    private device: any;
    private mdns = require('mdns-js');
    private ok: any = 1;
    private devices: any = [];
    private castDevices: any = [];
    private i: any = false;
    private audioStream: any = new Stream.PassThrough();
    private ffmpeg: any = null;
    private worker: any = null;
    

    private processNode = `
    import {parentPort, workerData} from "worker_threads";
    function getAudioConv (buffers) {
        
        function interleave16(leftChannel, rightChannel) {
            var length = leftChannel.length + rightChannel.length;
            var result = new Int16Array(length);

            var inputIndex = 0;

            for (var index = 0; index < length;) {
                result[index++] = leftChannel[inputIndex];
                result[index++] = rightChannel[inputIndex];
                inputIndex++;
            }
            return result;
        }



        function convert(n) {
            var v = n < 0 ? n * 32768 : n * 32767;       // convert in range [-32768, 32767]
            return Math.max(-32768, Math.min(32768, v)); // clamp
        }

        function bitratechange(e) {
            var t = e.length;
            let sampleRate = 96.0;
            let outputSampleRate = 44.1;
            var s = 0,
                o = sampleRate / outputSampleRate,
                u = Math.ceil(t * outputSampleRate / sampleRate),
                a = new Int16Array(u);
            for (let i = 0; i < u; i++) {
                a[i] = e[Math.floor(s)];
                s += o;
            }

            return a;
        }

        let newaudio = buffers;

        let pcmData = new Int8Array(interleave16(bitratechange(Int16Array.from(newaudio[0], x => convert(x))), bitratechange(Int16Array.from(newaudio[1], x => convert(x)))).buffer);
        return pcmData;
    }
    parentPort.on("message", data => {
        parentPort.postMessage({buffer: data.buffer, outbuffer: getAudioConv(data.buffer)});
    });

`;

    private ondeviceup(name: any, host: any, port: any, addresses: any, text: any) {
        if (this.castDevices.findIndex((item: any) => item.name == host.replace(".local","") && item.port == port && item.addresses == addresses) === -1) {
            this.castDevices.push({
                name: host.replace(".local",""),
                host: addresses ? addresses[0] : '',
                port: port,
                addresses: addresses,
                txt: text
            });
            if (this.devices.indexOf(host) === -1) {
                this.devices.push(host);
            }
            if (name) {
                this._win.webContents.executeJavaScript(`console.log('deviceFound','ip: ${host} name:${name}')`).catch((err: any) => console.error(err));
                console.log("deviceFound", host, name);
            }
        } else {
            this._win.webContents.executeJavaScript(`console.log('deviceFound (added)','ip: ${host} name:${name}')`).catch((err: any) => console.error(err));
            console.log("deviceFound (added)", host, name);
        }
    }

    /**
     * Base Plugin Details (Eventually implemented into a GUI in settings)
     */
    public name: string = 'RAOP';
    public description: string = 'RAOP Plugin';
    public version: string = '0.0.1';
    public author: string = 'vapormusic / Cider Collective';

    /**
     * Runs on plugin load (Currently run on application start)
     */
    constructor(utils: { getStore: () => any; getApp: () => any; }) {
        this._utils = utils;
        console.debug(`[Plugin][${this.name}] Loading Complete.`);
        this._app = utils.getApp();

    }

    /**
     * Runs on app ready
     */
    onReady(win: any): void {
        this.u = require('airtunes2');
        this._win = win;

        electron.ipcMain.on('getKnownAirplayDevices', (event) => {
            event.returnValue = this.castDevices
        });
        
        electron.ipcMain.on("getAirplayDevice", (event, data) => {
            this.castDevices = [];
            console.log("scan for airplay devices");
            
            const browser = this.mdns.createBrowser(this.mdns.tcp('raop'));
            browser.on('ready', browser.discover);

            browser.on('update', (service: any) => {
                 if (service.addresses && service.fullname && (service.fullname.includes('_raop._tcp') ||  service.fullname.includes('_airplay._tcp'))) {
                    // console.log(service.txt)
                this._win.webContents.executeJavaScript(`console.log(
                    "${service.name} ${service.host}:${service.port} ${service.addresses}"
                )`);
                this.ondeviceup(service.name, service.host, service.port, service.addresses, service.txt);
             }
            });
            
            // const browser2 = this.mdns.createBrowser(this.mdns.tcp('airplay'));
            // browser2.on('ready', browser2.discover);

            // browser2.on('update', (service: any) => {
            //      if (service.addresses && service.fullname && (service.fullname.includes('_raop._tcp') ||  service.fullname.includes('_airplay._tcp'))) {
            //         // console.log(service.txt)
            //     this._win.webContents.executeJavaScript(`console.log(
            //         "${service.name} ${service.host}:${service.port} ${service.addresses}"
            //     )`);
            //     this.ondeviceup(service.name, service.host, service.port, service.addresses, service.txt);
            //  }
            // });
    
        });



        electron.ipcMain.on("performAirplayPCM", (event, ipv4, ipport, sepassword, title, artist, album, artworkURL,txt) => {

            if (ipv4 != this.ipairplay || ipport != this.portairplay) {
                if (this.airtunes == null) { this.airtunes = new this.u()}
                this.ipairplay = ipv4;
                this.portairplay = ipport;
                this.device = this.airtunes.add(ipv4, {
                    port: ipport,
                    volume: 50,
                    password: sepassword,
                    txt: txt
                });
                // console.log('lol',txt)
                this.device.on('status', (status: any) => {
                    console.log('device status', status);
                    if (status == "ready"){
                        this._win.webContents.setAudioMuted(true);
                        this._win.webContents.executeJavaScript(`CiderAudio.sendAudio()`).catch((err: any) => console.error(err));
                    }
                    if (status == "need_password"){
                        this._win.webContents.executeJavaScript(`app.setAirPlayCodeUI()`)
                    }
                    if (status == "pair_success"){
                        this._win.webContents.executeJavaScript(`app.sendAirPlaySuccess()`)
                    }
                    if (status == "pair_failed"){
                        this._win.webContents.executeJavaScript(`app.sendAirPlayFailed()`)
                    }
                    if (status == 'stopped') {
                        this.airtunes.stopAll(() => {
                            console.log('end');
                        });
                        this.airtunes = null;
                        this.device = null;
                        this.ipairplay = '';
                        this.portairplay = '';
                        this.ok = 1;
                       
                    } else {
                        setTimeout(() => {
                            if (this.ok == 1) {
                                console.log(this.device.key, title ?? '', artist ?? '', album ?? '');
                                this.airtunes.setTrackInfo(this.device.key, title ?? '', artist?? '', album?? '');
                                this.uploadImageAirplay(artworkURL);
                                console.log('done');
                                this.ok == 2
                            }
                        }, 1000);
                    }


                });

            }




        });

        electron.ipcMain.on('setAirPlayPasscode', (event, passcode) => {
            if (this.device){
                this.device.setPasscode(passcode)
            }
        })

        electron.ipcMain.on('writeWAV', (event, leftbuffer, rightbuffer) => {
            if (this.airtunes != null) {
                if (this.worker == null) {
                try{
                const toDataUrl = (js: any) => new URL(`data:text/javascript,${encodeURIComponent(js)}`);
                // let blob = new Blob([this.processNode], { type: 'application/javascript' });
                //Create new worker
                this.worker = new Worker(toDataUrl(this.processNode));

                //Listen for a message from worker
                this.worker.on("message", (result: any) => {
                // fs.writeFile(join(electron.app.getPath('userData'), 'buffer.raw'), Buffer.from(Int8Array.from(result.outbuffer)),{flag: 'a+'}, function (err) {
                //     if (err) throw err;
                //     console.log('It\'s saved!');
                // });
                this.airtunes.circularBuffer.write(Buffer.from(Int8Array.from(result.outbuffer)));
                });

                this.worker.on("error", (error: any) => {
                    console.log("bruh",error); 
                });
                this.worker.postMessage({buffer: [leftbuffer, rightbuffer]});   
                } catch (e){console.log(e)}
                

                // this.ffmpeg != null ? this.ffmpeg.kill() : null;
                // this.ffmpeg = spawn(this._utils.getStoreValue("advanced.ffmpegLocation"), [
                //     '-f', 's16le',        // PCM 16bits, little-endian
                //     '-ar', '48000', 
                //     '-ac', "2",  
                //     '-err_detect','ignore_err',
                //     '-i', "http://localhost:9000/audio.wav",
                //     '-acodec', 'pcm_s16le',
                //     '-f', 's16le',        // PCM 16bits, little-endian
                //     '-ar', '44100',       // Sampling rate
                //     '-ac', "2",             // Stereo
                //     'pipe:1'              // Output on stdout
                //   ]);
                
                //   // pipe data to AirTunes
                // this.ffmpeg.stdout.pipe(this.airtunes);
                // this.i = true;
                } else {
                 this.worker.postMessage({buffer: [leftbuffer, rightbuffer]});
                }
            }

        });
        


        electron.ipcMain.on('disconnectAirplay', (event) => {
            this._win.webContents.setAudioMuted(false);
            this.airtunes.stopAll(function () {
                console.log('end');
            });
            this.airtunes = null;
            this.device = null;
            this.ipairplay = '';
            this.portairplay = '';
            this.ok = 1;
            this.i = false;
        });

        electron.ipcMain.on('updateAirplayInfo', (event, title, artist, album, artworkURL) => {
            if (this.airtunes && this.device) {
                console.log(this.device.key, title, artist, album);
                this.airtunes.setTrackInfo(this.device.key, title, artist, album);
                this.uploadImageAirplay(artworkURL)
            }
        });

        electron.ipcMain.on('updateRPCImage', (_event, imageurl) => {
            this.uploadImageAirplay(imageurl)
        })



    }

    private uploadImageAirplay = (url: any) => {
        try {
            if (url != null && url != '') {
                //console.log(join(this._app.getPath('userData'), 'temp.png'), url);
                fetch(url)
                    .then(res => res.buffer())
                    .then((buffer) => {
                        this.airtunes.setArtwork(this.device.key, buffer, "image/png");
                    }).catch(err => {
                        console.log(err)
                    });
            }
        } catch (e) { console.log(e) }
    }

    /**
     * Runs on app stop
     */
    onBeforeQuit(): void {

    }

    // /**
    //  * Runs on song change
    //  * @param attributes Music Attributes
    //  */
    // onNowPlayingItemDidChange(attributes: any): void {
    //     if (this.airtunes && this.device) {
    //         let title = attributes.name ? attributes.name : '';
    //         let artist = attributes.artistName ? attributes.artistName : '';
    //         let album = attributes.albumName ? attributes.albumName : '';
    //         let artworkURL = attributes?.artwork?.url?.replace('{w}', '1024').replace('{h}', '1024') ?? null;
    //         console.log(this.device.key, title, artist, album);
    //         this.airtunes.setTrackInfo(this.device.key, title, artist, album);
    //         if (artworkURL)
    //             this.uploadImageAirplay(artworkURL)
    //     }
    // }

    /**
     * Runs on playback State Change
     * @param attributes Music Attributes (attributes.status = current state)
     */
    onPlaybackStateDidChange(attributes: any): void {
        if (this.airtunes && this.device) {
            let title = attributes?.name ?? '';
            let artist = attributes?.artistName ?? '';
            let album = attributes?.albumName ?? '';
            let artworkURL = attributes?.artwork?.url ?? null;
            console.log(this.device.key, title, artist, album);
            this.airtunes.setTrackInfo(this.device.key, title, artist, album);
            if (artworkURL != null){}
                this.uploadImageAirplay(artworkURL.replace('{w}', '1024').replace('{h}', '1024'))
        }
    }

}
