var channels = [ "5A", "5B", "5C", "5D", "6A", "6B", "6C", "6D", "7A", "7B",
"7C", "7D", "8A", "8B", "8C", "8D", "9A", "9B", "9C", "9D", "10A", "10B",
"10C", "10D", "11A", "11B", "11C", "11D", "12A", "12B", "12C", "12D", "13A",
"13B", "13C", "13D", "13E", "13F"];

var fft_window_placements = [ "StrongestPeak", "EarliestPeakWithBinning", "ThresholdBeforePeak" ];

var png_noslide = " ";

var png_alert = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAk1BMVEWgAAD/AAD/////+/v/+Pj/2tr/T0//1NT/5eX/mJj/pKT/8/P/7Oz/y8v/4eH/Dg7/19f/R0f/vb3/Xl7/eHj/Njb/6+v/sbH/fHz/iYn/QUH/cXH/nZ3/HBz/JSX/bGz/jo7/xMT/KSn/ra3/Zmb/PT3/sLD/hIT/YGD/hob/VVX/Fhb/t7f/yMj/TU3/OTn/oqILUJpfAAAAAXRSTlMAQObYZgAAAFVJREFUOMuV0EEOACEIA0Dn/5/em8kqWumxTAJhjGZI8yAEIQDugiASIIgE5uAg/IE2sAJNYAdaQAU0gBqonlwCxzyCtdlEAlKxbnUH+5lCMnh51DUfEC4CA1JxhXIAAAAASUVORK5CYII="

var png_chevron_right = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABpSURBVEiJ7c4xDoJAEEbhL8rtPICVHQ3XoaLQ2sSzcA6OQEgIrI3FVmaNbmPm1f+8NwTBX3HGgEMt+YqEa41IHki4ReQdfRZIaH8pP2HJ5A80IYcO+0t+x7H0sHQ4YsKMC7YPHwyCL3gCWFcmgwfCKVwAAAAASUVORK5CYII="

var png_chevron_down = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAB0SURBVEiJ7Y87CoAwEAVHD2khqIUgCF5HUPBzUL9NgiGoSKKN7MAW4cEMAUEQnlAAycUWA6WPPAcWdZm1RcAErEDlGmiATd3M8ZNYvfXWuQYCoLUijSXvgdA1oCO1ITRv8JXfRV6Tn0XGt+VmJP1KLgh/ZQd6AiOdJgUioQAAAABJRU5ErkJggg=="

window.onload = function() {
    var chevron_right = '<img width=24 height=24 src="data:image/png;base64,' +
        png_chevron_right + '" />';
    var chevron_down = '<img width=24 height=24 src="data:image/png;base64,' +
        png_chevron_down + '" />';

    var spectrum_block = document.getElementById('block_spectrum');
    spectrum_block.getElementsByClassName('title')[0]
        .getElementsByClassName('chevron')[0]
        .innerHTML = chevron_right;

    var cir_block = document.getElementById('block_cir');
    cir_block.getElementsByClassName('title')[0]
        .getElementsByClassName('chevron')[0]
        .innerHTML = chevron_right;

    var constellation_block = document.getElementById('block_constellation');
    constellation_block.getElementsByClassName('title')[0]
        .getElementsByClassName('chevron')[0]
        .innerHTML = chevron_right;

    var tii_block = document.getElementById('block_tii');
    tii_block.getElementsByClassName('title')[0]
        .getElementsByClassName('chevron')[0]
        .innerHTML = chevron_right;

    toggle_func = function(block) {
        var fold = block.getElementsByClassName('title')[0]
            .getElementsByClassName('chevron')[0];
        var style = block.getElementsByClassName('data')[0].style;
        if (style.display != "none") {
            style.display = 'none';
            fold.innerHTML = chevron_right;
            return false;
        }
        else {
            style.display = 'block';
            fold.innerHTML = chevron_down;
            return true;
        }
    };

    spectrum_block.onclick = function() {
        if (toggle_func(spectrum_block)) {
            populateSpectrumPlots(480);
        }
        else {
            clearInterval(plotSpectrumTimer);
        }
    };

    cir_block.onclick = function() {
        if (toggle_func(cir_block)) {
            populateCIRPlots(480);
        }
        else {
            clearInterval(plotCIRTimer);
        }
    };

    constellation_block.onclick = function() {
        if (toggle_func(constellation_block)) {
            populateConstellationPlots(480);
        }
        else {
            clearInterval(plotConstellationTimer);
        }
    };

    tii_block.onclick = function() { toggle_func(tii_block); };

    var ch = document.getElementById("channelselector");

    for (i in channels) {
        var opt = document.createElement("option");
        opt.text = channels[i];
        ch.options.add(opt, -1);
    }

    ch.onchange = function() {
        var channel = document.getElementById("channelselector").value;
        var xhr = new XMLHttpRequest();
        xhr.open("POST", '/channel', true);
        xhr.setRequestHeader("Content-type", "text/plain");
        xhr.send(channel);
    };

    var fftw = document.getElementById("fftwindowselector");

    for (i in fft_window_placements) {
        var opt = document.createElement("option");
        opt.text = fft_window_placements[i];
        fftw.options.add(opt, -1);
    }

    fftw.onchange = function() {
        var fft_window = document.getElementById("fftwindowselector").value;
        var xhr = new XMLHttpRequest();
        xhr.open("POST", '/fftwindowplacement', true);
        xhr.setRequestHeader("Content-type", "text/plain");
        xhr.send(fft_window);
    };

    document.getElementById("coarsecheckbox").onclick = function() {
        var fft_window = document.getElementById("fftwindowselector").value;
        var xhr = new XMLHttpRequest();
        xhr.open("POST", '/enablecoarsecorrector', true);
        xhr.setRequestHeader("Content-type", "text/plain");
        if (document.getElementById("coarsecheckbox").checked) {
            xhr.send(1);
        }
        else {
            xhr.send(0);
        }
    }
};

function refreshChannel() {
    var r = new XMLHttpRequest();
    r.onreadystatechange = function () {
        if (r.readyState != 4 || r.status != 200) return;
        document.getElementById("channelselector").value = r.responseText;
    };
    r.open("GET", "/channel", true);
    r.send()
};

var channelRefreshTimer = setInterval(refreshChannel, 2000);

var ensembleInfoTimer = setInterval(populateEnsembleinfo, 1000);

function ensembleInfoTemplate() {
    var html = '';
    html += ' <h1><abbr title="Ensemble long and short labels defined in FIG1">${label} (${shortlabel})</abbr></h1>';
    html += ' <h2><abbr title="Ensemble long and short labels defined in FIG2">${fig2label}</abbr></h2>';
    html += ' <div align="right"><p><abbr title="${hw_name}, ${sw_name}">This is welle-cli build version ${version}</abbr></p></div>';
    html += ' <table id="servicetable">';
    html += ' <tr><th>Ensemble ID </th>';
    html += ' <th>ECC </th>';
    html += ' <th>SNR </th>';
    html += ' <th>RX gain </th>';
    html += ' <th>Freq corr</th>';
    html += ' <th>Date</th></th>';
    html += ' <th><abbr title="Local Time Offset">LTO</abbr></th></th>';
    html += ' <th>FIC CRC Errors</th>';
    html += ' <th>Tuned at</th>';
    html += ' </tr>';
    html += ' <tr><td>${EId}</td>';
    html += ' <td>${ecc}</td>';
    html += ' <td>${SNR}</td>';
    html += ' <td>${gain}</td>';
    html += ' <td>${FrequencyCorrection}</td>';
    html += ' <td>${year}-${month}-${day} ${hour}:${minutes} UTC</td>';
    html += ' <td>${lto}</td>';
    html += ' <td>${ficcrcerrors}</td>';
    html += ' <td>${lastchannelchange}</td></tr>';
    html += ' </table><br>    <button type=button onclick="stopPlayer()">Stop</button><br><br>';
    html += '';
    html += '<table id="servicetable">';
    html += '<tr><th>FIG1 Label (Short label)<br>FIG2 Label</th> ';
    html += '<th><abbr title="Service ID">SId</abbr></th> ';
    html += '<th>Bitrate</th> <th><abbr title="Start CU Address, used CUs">CU info</abbr></th> ';
    html += '<th><abbr title="Protection level">Prot.Lev.</abbr></th>';
    html += '<th><abbr title="Transmission Mode, rate, channels">Technical details</abbr></th>';
    html += '<th><abbr title="Programme type">PTy</abbr></th>';
    html += '<th><abbr title="Service language, Subchannel language">Languages</abbr></th> <th id="dls">DLS</th>';
    html += '<th><abbr title="Frame, Reed Solomon, AAC errors">Errors</abbr></th>';
    html += '<th><abbr title="red: right, black: left">Audio Level</abbr></th> <th></th></tr>';
    html += '${services}</table>';
    return html;
}

function serviceTemplate() {
    var html = '<tr><td>${label} (${shortlabel})<br>${fig2label}</td> <td>${SId}</td> <td>${bitrate}&nbsp;kbps</td> <td>${sad_cu}</td> <td>${protection}</td>';
    html += '<td>${techdetails}</td>';
    html += '<td>${pty}</td> <td>${language}<br>${subchannel_language}</td> <td></i>${dls}</i></td>';
    html += '<td>${errorcounters}</td>';
    html += '<td><canvas id="${canvasid}" width="64" height="12"></canvas></td>';
    html += '<td><button type=button ${buttondisabled} class="${buttonclass}" onclick="setPlayerSource(${SId})">Play</button></td>';
    html += '</tr>';
    return html;
}

function tiiTemplate() {
    var html = '<li>MainId ${pattern} SubId ${comb} ${delay} samples = <b>${delay_km} km</b>';
    html += ' error: ${error}</li>';
    return html;
}

function playerLoad() {
    document.getElementById("player").load();
    document.getElementById("player").play();
}

function setPlayerSource(sid) {
    document.getElementById("player").src = "/mp3/" + sid;
    playerLoad();
}

function stopPlayer() {
    document.getElementById("player").src = "";
    playerLoad();
}

var slide_modal = document.getElementById('mySlideModal');
var slideimg = document.getElementById("slideimg");
var slidecaption = document.getElementById("slidecaption");

function showSlide(sid, last_update_time) {
    slideimg.src = "slide/" + sid + "?cachebreak=" + last_update_time;
    var last_update = new Date(last_update_time * 1000);
    slidecaption.innerHTML = last_update;
    slide_modal.style.display = "block";
}

var slideclose = document.getElementsByClassName("slideclose")[0];
slideclose.onclick = function() {
    slide_modal.style.display = "none";
}

function parseTemplate(template, data) {
   return template.replace(/\$\{(\w+)\}/gi, function(match, parensMatch) {
     if (data[parensMatch] !== undefined) {
       return data[parensMatch];
     }

     return match;
   });
}

function drawCIRPeaks(cirs) {
    var canvas = document.getElementById("cirpeaks");
    var ctx = canvas.getContext("2d");
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    ctx.fillStyle="#BBFF77";
    ctx.font="8px Helvetica";

    var i = 0;
    for (cir in cirs) {
        var h = 10 + 3*(i % 3);
        ctx.fillText(i, cirs[cir].index/4, h);
        i++;
    }
}

function drawAudiolevels(services) {
    for (key in services) {
        var service = services[key];
        var id = "canvas" + service.sid;
        var canvas = document.getElementById(id);
        var ctx = canvas.getContext("2d");

        var level_db_l = -90;
        var level_db_r = -90;
        if (service.audiolevel) {
            if (service.audiolevel.left > 0) {
                level_db_l = Math.round(20 * Math.log10(service.audiolevel.left / 0x7fff));
            }
            if (service.audiolevel.right > 0) {
                level_db_r = Math.round(20 * Math.log10(service.audiolevel.right / 0x7fff));
            }

            var last_update = new Date(service.audiolevel.time * 1000);
            canvas.setAttribute("title", "Updated " + last_update);
        }

        var width_l = (level_db_l + 90) * 64/90;
        var width_r = (level_db_r + 90) * 64/90;

        var height = canvas.height / 2;

        ctx.fillStyle = "#444444";
        ctx.fillRect(0,0,width_l,height);
        ctx.fillStyle = "#DD4444";
        ctx.fillRect(0,height,width_r,2*height);
    }
};

function populateEnsembleinfo() {
    var r = new XMLHttpRequest();
    r.onreadystatechange = function () {
        if (r.readyState != 4 || r.status != 200) return;
        var data = JSON.parse(r.responseText);

        var start_addresses = [];
        for (key in data.services) {
            var service = data.services[key];
            var sad_ix = {};
            if (service.components) {
                sad_ix["sad"] = service.components[0].subchannel.sad;
            }
            else {
                // Place them at the end
                sad_ix["sad"] = 864;
            }
            sad_ix["key"] = key;
            start_addresses.push(sad_ix);
        }

        start_addresses.sort(function(a, b) {
            return a.sad - b.sad;
        });

        var servicehtml = "";
        for (ix in start_addresses) {
            var key = start_addresses[ix].key;
            var service = data.services[key];
            var s = {};
            s["label"] = service.label.label;
            s["fig2label"] = service.label.fig2label;
            s["shortlabel"] = service.label.shortlabel;
            s["SId"] = service.sid;
            s["buttondisabled"] = "disabled";
            s["buttonclass"] = "disabled";
            if (service.components) {
                var sc = service.components[0];
                var sub = sc.subchannel;
                s["bitrate"] = sub.bitrate;
                s["sad_cu"] = sub.sad + ", " + sub.cu;
                s["protection"] = sub.protection;
                s["subchannel_language"] = sub.languagestring;

                if (sc.transportmode == "audio") {
                    s["techdetails"] = sc.ascty + ", " +
                        service.samplerate + " Hz, " +
                        service.mode + ", " +
                        service.channels;
                    s["buttondisabled"] = "";
                    s["buttonclass"] = "";
                }
                else {
                    s["techdetails"] = sc.transportmode + ", DSCTy=" + sc.dscty;
                }
            }
            else {
                s["bitrate"] = 0;
                s["sad"] = -1;
                s["protection"] = "?";
                s["techdetails"] = "";
            }

            s["dls"] = "";

            if (service.mot && service.mot.time > 0) {
                s["dls"] += '<button type=button onclick="showSlide(';
                s["dls"] += service.sid + ', ' + service.mot.time;
                s["dls"] += ')">SLS</button>';
            }

            if (service.dls) {
                var last_update = new Date(service.dls.time * 1000);
                s["dls"] += ' <span title="Updated ' + last_update + '">' + service.dls.label + '</span>';
            }

            if (service.xpaderror && service.xpaderror.haserror) {
                var alerthtml = ' <img width=16 height=16 src="data:image/png;base64,' + png_alert + '" ';
                var tooltip = "X-PAD Length error, expected " + service.xpaderror.announcedlen +
                    " got " + service.xpaderror.len;
                alerthtml += 'title="' + tooltip + '" ';
                alerthtml += 'alt="' + tooltip + '">';
                s["dls"] += alerthtml;
            }
            s["dls"] += "</td>";

            s["pty"] = service.ptystring;
            s["language"] = service.languagestring;
            s["canvasid"] = "canvas" + service.sid;

            if (service.errorcounters) {
                s["errorcounters"] = service.errorcounters.frameerrors + "," +
                                     service.errorcounters.rserrors + "," +
                                     service.errorcounters.aacerrors;
            }
            else {
                s["errorcounters"] = "";
            }

            servicehtml += parseTemplate(serviceTemplate(), s)
        }

        var ens = {};
        ens["label"] = data.ensemble.label.label;
        ens["fig2label"] = data.ensemble.label.fig2label;
        ens["shortlabel"] = data.ensemble.label.shortlabel;
        ens["EId"] = data.ensemble.id;
        ens["ecc"] = data.ensemble.ecc;

        ens["year"] = data.utctime.year;
        ens["month"] = data.utctime.month;
        ens["day"] = data.utctime.day;
        ens["hour"] = data.utctime.hour;
        ens["minutes"] = data.utctime.minutes;
        ens["lto"] = data.utctime.lto;

        ens["gain"] = data.receiver.hardware.gain.toFixed(1);
        document.getElementById("fftwindowselector").value = data.receiver.software.fftwindowplacement;
        document.getElementById("coarsecheckbox").checked = data.receiver.software.coarsecorrectorenabled;

        ens["version"] = data.receiver.software.version;
        ens["hw_name"] = data.receiver.hardware.name;
        ens["sw_name"] = data.receiver.software.name;
        ens["SNR"] = data.demodulator.snr.toFixed(1);
        ens["FrequencyCorrection"] = data.demodulator.frequencycorrection;
        ens["services"] = servicehtml;
        ens["ficcrcerrors"] = data.demodulator.fic.numcrcerrors;
        var lcc = new Date(data.receiver.software.lastchannelchange * 1000);
        ens["lastchannelchange"] = lcc.toISOString();

        var ei = document.getElementById('ensembleinfo');
        ei.innerHTML = parseTemplate(ensembleInfoTemplate(), ens);

        tiihtml = "<ul>";
        for (key in data.tii) {
            tiihtml += parseTemplate(tiiTemplate(), data.tii[key])
        }
        tiihtml += "</ul>";

        var tii_el = document.getElementById('tiiinfo');
        tii_el.innerHTML = tiihtml;

        drawCIRPeaks(data.cir_peaks);

        drawAudiolevels(data.services);
    };
    r.open("GET", "/mux.json", true);
    r.send()
};

function plot(data, id, scalefactor, shiftfactor, plot_ix) {
    var canvas = document.getElementById(id);
    var ctx = canvas.getContext("2d");
    if (plot_ix == 0) {
        ctx.fillStyle = "#111100";
        ctx.fillRect(0,0,data.length,150);
    }

    ctx.beginPath();
    colors = ["#FF4444", "#33FF55", "#778800"];
    if (plot_ix < colors.length) {
        ctx.strokeStyle=colors[plot_ix];
    }
    else {
        ctx.strokeStyle=colors[0];
    }
    var dataMax = 0;
    var dataMin = 1000;
    for (var i = 0; i < data.length; i++) {
        var dataScaled = 170-scalefactor*(data[i] + shiftfactor);

        if (dataMax < dataScaled) {
            dataMax = dataScaled;
        }

        if (dataMin > dataScaled) {
            dataMin = dataScaled;
        }

        if (i % 4 == 0) {
            ctx.moveTo(i/4, dataMin);
            ctx.lineTo(i/4, dataMax);

            dataMax = 0;
            dataMin = 1000;
        }
    }
    ctx.stroke();
};

function populateSpectrumPlots(interval) {
    populateSpectrum();
    if (interval > 0) {
        plotSpectrumTimer = setTimeout(function() { populateSpectrumPlots( interval); }, interval);
    }
};

function populateCIRPlots(interval) {
    populateCIR();
    if  (interval > 0) {
        plotCIRTimer = setTimeout(function() { populateCIRPlots( interval); }, interval);
    }
};

function populateConstellationPlots(interval) {
    populateConstellation();
    if  (interval > 0) {
        plotConstellationTimer = setTimeout(function() { populateConstellationPlots( interval); }, interval);
    }
};

function populateSpectrum() {
    var r = new XMLHttpRequest();
    r.onload = function(oEvent) {
        var arrayBuffer = r.response;
        if (arrayBuffer) {
            var spec = new Float32Array(arrayBuffer);
            plot(spec, "spectrum", 2, 20, 0);
        }

        r2 = new XMLHttpRequest();
        r2.onload = function(oEvent) {
            var arrayBuffer = r2.response;
            if (arrayBuffer) {
                var spec = new Float32Array(arrayBuffer);
                plot(spec, "spectrum", 2, 20, 1);
            }
        };
        r2.open("GET", "/nullspectrum", true);
        r2.responseType = "arraybuffer";
        r2.send(null);
    };
    r.open("GET", "/spectrum", true);
    r.responseType = "arraybuffer";
    r.send(null);
};

function populateCIR() {
    var r = new XMLHttpRequest();
    r.onload = function(oEvent) {
        var arrayBuffer = r.response;
        if (arrayBuffer) {
            var cir = new Float32Array(arrayBuffer);
            plot(cir, "cir", 4, 30, 0)
        }
    };
    r.open("GET", "/impulseresponse", true);
    r.responseType = "arraybuffer";
    r.send(null);
}

function populateConstellation() {
    var r = new XMLHttpRequest();
    r.onload = function(oEvent) {
        var arrayBuffer = r.response;
        if (arrayBuffer) {
            var data = new Float32Array(arrayBuffer);

            var squeeze = 4;

            var canvas = document.getElementById("constellation");
            var ctx = canvas.getContext("2d");
            ctx.fillStyle = "#111100";
            ctx.fillRect(0,0,data.length / squeeze,180);

            ctx.beginPath();
            ctx.strokeStyle="rgba(255, 100, 0, 0.8)";
            for (var i = 0; i < data.length; i++) {
                var x = i / squeeze;
                var y = (data[i] + 180) / 2;
                // Draw a little cross
                ctx.moveTo(x-1, y);
                ctx.lineTo(x+1, y);
                ctx.moveTo(x, y-1);
                ctx.lineTo(x, y+1);
            }
            ctx.stroke();
        }
    };
    r.open("GET", "/constellation", true);
    r.responseType = "arraybuffer";
    r.send(null);
}


