// created 21.09.2022 by Gifer

function McAudioConf($rootScope){
    "use strict";

    const commands  = {
        sendOffer      : '0001',
        sendAnswer     : '0002',
        sendICE        : '0003',
        receiveOffer   : '0004',
        receiveAnswer  : '0005',
        receiveICE     : '0006',

        connect        : '0007',
        disconnect     : '0008',
        setID          : '0009',
        newClient      : '0010',

        nm_ready       : '0011',
        nm_create_room : '0012',
        nm_remove_room : '0013',

        mn_create_room : '0014',
        mn_remove_room : '0015',
        mn_ready       : '0016',
    };
    const MIXER     = "MIXER";

    const broadcast = {
        speech_start     : "0001",
        speech_stop      : "0002",
        mic_on           : "0003",
        mic_off          : "0004",
    }

    var MID         = null;
    var isMyConf    = false;
    var localStream = null;
    var myClientID  = null;
    var _config     = {};
    var peerConn    = null;

    function initTrack(track, callID){
        if (track.kind === "audio"){
            var audioStreams   = new MediaStream();
            var audioContainer = document.createElement("audio");
            audioContainer.srcObject = audioStreams;

            audioStreams.addTrack(track);

            audioContainer.play();

            // window.AudioContext = window.AudioContext || window.webkitAudioContext;
            // var audioContext = new AudioContext();
            // var source = audioContext.createMediaStreamSource(audioStreams);

            // Setup options
            // var options = {
            //     source: source,
            //     voice_stop: function() {console.log('voice_stop');},
            //     voice_start: function() {console.log('voice_start');}
            // };

            // Create VAD
            // var vad = new VAD(options);
        }

        // callID && saveTrackID(callID, track.id);
    }

    function getLocalStream() {
        console.log('Requesting local stream');

        return navigator.mediaDevices.getUserMedia({
            audio: true,
            video: false
        }).then(function (stream){
            console.log('Received local stream');

            localStream = stream;

            // initTrack(localStream.getAudioTracks()[0]);
        });
    }

    function call(incomingOffer) {
        if (!peerConn) {
            peerConn = new RTCPeerConnection({
                iceTransportPolicy: _config.iceTransportPolicy, //"relay",
                iceTransport      : _config.iceTransportPolicy, //"relay",
                iceServers        : _config.iceServers,
                sdpSemantics      :'unified-plan'
            });

            // peerConn.clientID = callID;

            console.log('Created local peer connection');

            peerConn.addEventListener('icecandidate', function (e){
                if (e.candidate) {
                    __SendData(commands.sendICE, {
                        ice: e.candidate,
                        uin: mcConst.UserInfo.UIN
                    });
                }
            });

            peerConn.addEventListener('iceconnectionstatechange', function (evt) {
                console.log("ICE connection state change: " + evt.target.iceConnectionState);

                if (peerConn.iceConnectionState === 'disconnected') {

                } else
                if (peerConn.iceConnectionState === 'connected' || peerConn.iceConnectionState === 'completed') {


                } else
                if (peerConn.iceConnectionState === 'failed' || peerConn.iceConnectionState === 'closed') {
                    // removeStreamUser(callID);

                    peerConn = null;
                }
            });

            peerConn.onerror = function(err) {
                console.log(err);
            };

            peerConn.ontrack = function (str){
                console.log("Get " + str.track.kind + " track id: " + str.track.id);
                console.log("Get " + str.track.kind + " track label: " + str.track.label);

                initTrack(str.track/*, callID*/);
            };

            peerConn.onsignalingstatechange = function (ev) {
                console.log(ev.type + ": " + peerConn.signalingState);
            };

            peerConn.icecandidateerror = function (event){
                console.log("peerConnection.icecandidateerror: " + event.errorCode);
            };

            // peerConn.onnegotiationneeded = function (event){
            //     console.log("peerConnection.onnegotiationneeded");
            // };

            console.log('Added local stream');

            peerConn.addStream(localStream);
        }

        // ====================================================
        // === Send Offer | Answer ============================
        // ====================================================
        if (incomingOffer){
            console.log('Create Answer');

            peerConn.setRemoteDescription(new RTCSessionDescription(incomingOffer));

            peerConn.createAnswer().then(function (outgoingAnswer){
                peerConn.setLocalDescription(outgoingAnswer);

                console.log('setLocalDescription complete and Send Answer');

                __SendData(commands.sendAnswer, {
                    answer: outgoingAnswer,
                    uin   : mcConst.UserInfo.UIN
                });
            }).catch(function (err){
                console.log(err.message);
            });
        } else {
            console.log('Create Offer');

            peerConn.createOffer({
                voiceActivityDetection: 1,
                iceRestart            : 1,
                offerToReceiveAudio   : 1
            }).then(function (outgoingOffer){
                peerConn.setLocalDescription(outgoingOffer);

                console.log('setLocalDescription complete and Send Offer');

                __SendData(commands.sendOffer, {
                    offer: outgoingOffer,
                    uin  : mcConst.UserInfo.UIN
                });
            }).catch(function (err){
                console.log(err.message);
            });
        }
    }

    function onReceiveOffer({offer}) {
        call(/*id,*/ offer);
    }

    function onReceiveAnswer({answer}) {
        // pcList[id] && pcList[id].setRemoteDescription(new RTCSessionDescription(answer));
        peerConn && peerConn.setRemoteDescription(new RTCSessionDescription(answer));
    }

    function onReceiveICE({ice}) {
        // pcList[id].addIceCandidate(new RTCIceCandidate(ice));
        peerConn && peerConn.addIceCandidate(new RTCIceCandidate(ice));
    }

    function __SendData(cmd, args) {
        $rootScope.$broadcast("SendCMDToServer", [
            mcConst._CMD_.cs_media_conf,
            mcConst.SessionID,
            
            JSON.stringify({
                UID : _config.conf_uid,
                cmd : cmd,
                Data: args
            })
        ]);
    }

    function start(mid) {
        myClientID = _config.SelfUIN;

        MID = mid;

        getLocalStream().then(function (){
            call();
        }).catch(function (err){
            console.log(err.message);
        });
    }

    // ===== Client Start =====

    $rootScope.$on("sc_media_conf", function (e, args){
        var info = args[0];

        console.log('sc_media_conf: ' + info.cmd);

        switch (info.cmd){
            case commands.receiveOffer:
                onReceiveOffer(info.Data);
            break;

            case commands.receiveAnswer:
                onReceiveAnswer(info.Data);
            break;

            case commands.receiveICE:
                onReceiveICE(info.Data);
            break;

            case commands.disconnect:
                console.log("--- Client disconnected: " + info.Data);

                // removeStreamUser(info.data);
            break;
        }
    });

    this.joinToConf = function (uid, cb){
        if (!_config.conf_uid) {
            $rootScope.SendCMDToServer([
                mcConst._CMD_.cs_media_conf_join,
                mcConst.SessionID,

                uid,

                function (info) {
                    _config = {
                        iceTransportPolicy: $rootScope.isWebClient ? "relay" : mcConst.ClientSettings.IceTransportPolicy,
                        conf_uid          : uid,
                        SelfUIN           : mcConst.UserInfo.UIN,
                        iceServers        : mcService.makeIceServers({
                            IPList    : info.IPList,
                            TURNPort  : info.TURNPort,
                            UserName  : info.UserName,
                            Credential: info.Credential
                        }, $rootScope.isWebClient)
                    }

                    start(_config.conf_uid);

                    cb && cb(info);
                }
            ]);
        }
    };

    this.isLive = function (){
        return !!peerConn;
    };

    this.getMID = function (){
        return MID;
    };

    this.setMID = function (mid, myConf) {
        MID      = mid;
        isMyConf = !!myConf;
    }

    this.closeConf = function (mid) {
        if (mid == MID && isMyConf){
            $rootScope.SendCMDToServer([
                mcConst._CMD_.cs_media_conf_close,
                mcConst.SessionID,

                MID
            ]);
        }
    }

    this.closeCall = function (mid){
        if (mid == MID){
            localStream && localStream.getTracks().forEach(function(track){
                track.stop();
            });

            peerConn && peerConn.close();
            peerConn    = null;
            MID         = null;
            myClientID  = null;
            localStream = null;
            isMyConf    = false;
            _config     = {};
        }
    };
    
    this.leaveConf = function (cb){
        if (MID) {
            $rootScope.SendCMDToServer([
                mcConst._CMD_.cs_media_conf_leave,
                mcConst.SessionID,

                MID,

                function () {
                    localStream && localStream.getTracks().forEach(function(track){
                        track.stop();
                    });

                    peerConn && peerConn.close();
                    peerConn    = null;
                    myClientID  = null;
                    localStream = null;
                    _config     = {};

                    if (!isMyConf) {
                        MID = null;
                    }

                    cb && cb();
                }
            ])
        }
    };
}