//
// bandcalc.js
//
// Copyright (C) 2008
// Packetizer, Inc.
// All Rights Reserved
//

var codecs;
var rtps;
var links;

// Object constructor for codec
function codec(name, rate, frameDuration, frameSize, lookAhead, mos, mips) { 
    this.name = name;
    this.rate = rate;
    this.frameDuration = frameDuration;
    this.frameSize = frameSize;
    this.lookAhead = lookAhead;
    this.mos = mos;
    this.mips = mips;
} 

// Object constructor for rtp
function rtp(name, headerSize) { 
    this.name = name;
    this.headerSize = headerSize;
} 

// Object constructor for link
function link(name, headerSize) { 
    this.name = name;
    this.headerSize = headerSize;
} 

function initializeVariables() {
    // Create new codec array.
    codecs = new Array()

    // Stuff codec array entries with objects. 
    // (G.721 and G.726 32kbps are the same thing)
    // (G.723 also fed into the G.726 work)
    //                      codec       rate    frame   bytes   look    MOS         MIPS
    codecs[0] = new codec("G.711",      64,     .125,   1,      0,      "4.3 - 4.7",    ".52");
    codecs[1] = new codec("G.711",      56,     .125,   .875,   0,      "?",        "?");
    codecs[2] = new codec("G.711",      48,     .125,   .75,    0,      "?",        "?");
    codecs[3] = new codec("G.721",      32,     .125,   .5,     0,      "4.0 - 4.2",    "7.2");
    codecs[4] = new codec("G.722",      48,     .125,   .75,    1.5,    "3.16 - 4.5",   "10 - 13.9");
    codecs[5] = new codec("G.722",      56,     .125,   .875,   1.5,    "4.5",      "10 - 13.9");
    codecs[6] = new codec("G.722",      64,     .125,   1,      1.5,    "3.75 - 4.5",   "10 - 13.9");
    codecs[7] = new codec("G.722.1",    16,     20,     40,     20,     "?",        "?");
    codecs[8] = new codec("G.722.1",    24,     20,     60,     20,     "4.1",      "?");
    codecs[9] = new codec("G.722.1",    32,     20,     80,     20,     "4.0",      "?");
    codecs[10] = new codec("G.723.1",   5.3,    30,     20,     7.5,    "3.5 - 3.7",    "16.5");
    codecs[11] = new codec("G.723.1",   6.4,    30,     24,     7.5,    "3.8 - 4.0",    "16.9");
    codecs[12] = new codec("G.726",     16,     .125,   .25,    0,      "?",        "7.2 - 12");
    codecs[13] = new codec("G.726",     24,     .125,   .375,   0,      "3.7",      "7.2 - 12");
    codecs[14] = new codec("G.726",     32,     .125,   .5,     0,      "3.9 - 4.2",    "7.2 - 12");
    codecs[15] = new codec("G.726",     40,     .125,   .625,   0,      "3.9",      "7.2 - 12");
    codecs[16] = new codec("G.727",     16,     .125,   .25,    0,      "?",        "9.9");
    codecs[17] = new codec("G.727",     24,     .125,   .375,   0,      "3.7",      "9.9");
    codecs[18] = new codec("G.727",     32,     .125,   .5,     0,      "3.9 - 4.2",    "9.9");
    codecs[19] = new codec("G.727",     40,     .125,   .625,   0,      "3.9",      "9.9");
    codecs[20] = new codec("G.728",     12.8,   .625,   1,      0,      "?",        "16");
    codecs[21] = new codec("G.728",     16,     .625,   1.25,   0,      "3.7 - 4.3",    "16 - 35");
    codecs[22] = new codec("G.729",     8,      10,     10,     5,      "3.9 - 4.2",    "20 - 25");
    codecs[23] = new codec("G.729a",    8,      10,     10,     5,      "3.7 - 4.2",    "10 - 11.4");
    codecs[24] = new codec("G.729b",    8,      10,     10,     5,      "3.9 - 4.2",    "G.729 + .7");
    codecs[25] = new codec("G.729ab",   8,      10,     10,     5,      "3.7 - 4.2",    "G.729a + .8");
    codecs[26] = new codec("G.729d",    6.4,    10,     8,      5,      "?",        "< G.729");
    codecs[27] = new codec("G.729e",    8,      10,     10,     5,      "4.0 - 4.2",    "30");
    codecs[28] = new codec("G.729e",    11.8,   10,     14.75,  5,      "?",        "30");
    codecs[29] = new codec("iLBC",      15.2,   20,     38,     0,      "4.14",     "~ G.729a");
    codecs[30] = new codec("iLBC",      13.33,  30,     50,     0,      "4",        "~ G.729a");

    // Create new RTP array.
    rtps = new Array()

    // Stuff codec array entries with objects. 
    rtps[0] = new rtp("RTP", 12);
    rtps[1] = new rtp("cRTP (compressed RTP)", 2);
    rtps[2] = new rtp("cRTP w/UDP checksums", 4);

    // Create new link array.
    links = new Array()

    // Stuff codec array entries with objects. 
    links[0] = new link("ethernet 802.3", 38);
    links[1] = new link("ethernet 802.3 w/802.1q", 42);
    links[2] = new link("Frame Relay 7-byte", 7);
    links[3] = new link("Frame Relay 8-byte", 8);
    links[4] = new link("Frame Relay 9-byte", 9);
    links[5] = new link("PPP", 6);
    links[6] = new link("MLPPP short seq. no.", 8);
    links[7] = new link("MLPPP long seq. no.", 10);
}

function RoundToNearestTenths(n)
{
    return Math.round(n*10)/10;
}

function addEmUp()
{
    var channels;
    var frameCount;
    var codecIndex;
    var linkTotal;
    var nonLinkTotal;
    var total;
    var packetRate;

    // selected codec
    codecIndex = window.document.calc.payloadSelect.selectedIndex;
    if (codecIndex < 0 || codecIndex > codecs.length) {
        codecIndex = 0;
    }

    // frames per packet
    frameCount = parseInt(window.document.calc.framesField.value);
    if (frameCount != window.document.calc.framesField.value || frameCount <= 0) {
        frameCount = 1;
    }

    // start off always counting payload size
    nonLinkTotal = codecs[codecIndex].frameSize * frameCount;
    linkTotal = 0;
    if (!window.document.calc.payload.checked) {
        var rtpIndex;

        // selected rtp protocol
        rtpIndex = window.document.calc.rtpSelect.selectedIndex;
        if (rtpIndex < 0 || rtpIndex > rtps.length) {
            rtpIndex = 0;
        }
        nonLinkTotal += rtps[rtpIndex].headerSize;
        if (!window.document.calc.rtp.checked) {

            if (rtpIndex == 0) {        // only include udp if rtp is regular RTP
                nonLinkTotal += 8;      // udp header size
            }
            if (!window.document.calc.udp.checked) {

                if (rtpIndex == 0) {        // only include ip if rtp is regular RTP
                    nonLinkTotal += 20;     // ip header size
                }
                if (!window.document.calc.ip.checked) { // Link assumed to be checked
                    var linkIndex;

                    // selected link protocol
                    linkIndex = window.document.calc.linkSelect.selectedIndex;
                    if (linkIndex < 0 || linkIndex > links.length) {
                        linkIndex = 0;
                    }
                    linkTotal = links[linkIndex].headerSize;    // link-level protocol header size
                }
            }
        }
    }

    // If checked, apply RTCP increment just to bandwidth not including link overhead
    if (window.document.calc.rtcp.checked) {
        nonLinkTotal *= 1.05;
    }

    // Add together the non-link and link totals
    total = nonLinkTotal + linkTotal;

    // convert bytes to bits
    total *= 8;

    // Multiple by number of channels.
    channels = parseInt(window.document.calc.channelsField.value);
    if (channels != window.document.calc.channelsField.value|| channels <= 0) {
        channels = 1;
    }   
    total *= channels;

    total /= codecs[codecIndex].frameDuration * frameCount;

    window.document.calc.maxBandwidth.value = RoundToNearestTenths(total);

    // Apply silence-suppression discount?
    if (window.document.calc.ss.checked) {
        total *= .50;
    }

    window.document.calc.totalBandwidth.value = RoundToNearestTenths(total);

    packetRate = 1000 / window.document.calc.packetFrameDelay.value;
    // If checked, apply RTCP increment to packet rate
    if (window.document.calc.rtcp.checked) {
        packetRate *= 1.05;
    }
    packetRate *= channels;
    window.document.calc.packetRateMaximum.value = RoundToNearestTenths(packetRate);
    // Apply silence-suppression discount?
    if (window.document.calc.ss.checked) {
        packetRate *= .50;
    }
    window.document.calc.packetRateAverage.value = RoundToNearestTenths(packetRate);
}

function linkButton()
{
    if (window.document.calc.link.checked == true) {
        window.document.calc.ip.checked = false;
        window.document.calc.udp.checked = false;
        window.document.calc.rtp.checked = false;
        window.document.calc.payload.checked = false;
        addEmUp();
    }
}

function ipButton()
{
    if (window.document.calc.ip.checked == true) {
        window.document.calc.link.checked = false;
        window.document.calc.udp.checked = false;
        window.document.calc.rtp.checked = false;
        window.document.calc.payload.checked = false;
        addEmUp();
    }
}

function udpButton()
{
    if (window.document.calc.udp.checked == true) {
        window.document.calc.link.checked = false;
        window.document.calc.ip.checked = false;
        window.document.calc.rtp.checked = false;
        window.document.calc.payload.checked = false;
        addEmUp();
    }
}

function rtpButton()
{
    if (window.document.calc.rtp.checked == true) {
        window.document.calc.link.checked = false;
        window.document.calc.ip.checked = false;
        window.document.calc.udp.checked = false;
        window.document.calc.payload.checked = false;
        addEmUp();
    }
}

function payloadButton()
{
    if (window.document.calc.payload.checked == true) {
        window.document.calc.link.checked = false;
        window.document.calc.ip.checked = false;
        window.document.calc.udp.checked = false;
        window.document.calc.rtp.checked = false;
        addEmUp();
    }
}

function validatePayload()
{
    var payload;

    payload = parseInt(window.document.calc.payloadField.value);
    if (payload != window.document.calc.payloadField.value || payload <= 0) {
        alert("Must be positive integer!");
        window.document.calc.payloadField.value = 1;
        window.document.calc.payloadField.focus();
    }
    addEmUp();
}

function validateChannels()
{
    var channels;

    channels = parseInt(window.document.calc.channelsField.value);
    if (channels != window.document.calc.channelsField.value || channels <= 0) {
        alert("Must be positive integer!");
        window.document.calc.channelsField.value = 1;
        window.document.calc.channelsField.focus();
    }
    addEmUp();
}

function validateFrames()
{
    var frameCount;

    frameCount = parseInt(window.document.calc.framesField.value);
    if (frameCount != window.document.calc.framesField.value || frameCount <= 0) {
        alert("Must be positive integer!");
        window.document.calc.framesField.value = 1;
        window.document.calc.framesField.focus();
    }
    addEmUp("frames");
}

function validatePacketDelay()
{
    var delay;

    delay = parseFloat(window.document.calc.packetFrameDelay.value);
    if (delay != window.document.calc.packetFrameDelay.value || delay <= 0) {
        alert("Must be positive number!");
        window.document.calc.packetFrameDelay.value = 1;
        window.document.calc.packetFrameDelay.focus();
    }
    addEmUp("delay");
}

function showCodecInfo(packetizationType)
{
    var codecIndex;
    var frameCount;

    codecIndex = window.document.calc.payloadSelect.selectedIndex;
    if (codecIndex < 0 || codecIndex > codecs.length) {
        codecIndex = 0;
    }

    window.document.calc.frameDelay.value = codecs[codecIndex].frameDuration;

    if (packetizationType == "packet") {
        var delay;

        delay = parseFloat(window.document.calc.packetFrameDelay.value);
        if (delay != window.document.calc.packetFrameDelay.value || delay <= 0) {
            delay = 1;
        }

        if (delay < codecs[codecIndex].frameDuration) {
            delay = codecs[codecIndex].frameDuration;
            window.document.calc.packetFrameDelay.value = delay;
        }
        else if (delay % codecs[codecIndex].frameDuration != 0) {
            delay = Math.round((delay / codecs[codecIndex].frameDuration) + .5) *
                    codecs[codecIndex].frameDuration;
            window.document.calc.packetFrameDelay.value = delay;
        }

        frameCount = delay / codecs[codecIndex].frameDuration;

        window.document.calc.framesField.value = frameCount;
    }
    else {
        frameCount = parseInt(window.document.calc.framesField.value);
        if (frameCount != window.document.calc.framesField.value || frameCount <= 0) {
            frameCount = 1;
        }

        window.document.calc.packetFrameDelay.value =
                codecs[codecIndex].frameDuration * frameCount;
    }

    window.document.calc.lookAhead.value = codecs[codecIndex].lookAhead;
    window.document.calc.algorithmicDelay.value =
            codecs[codecIndex].lookAhead +
            (codecs[codecIndex].frameDuration * frameCount);
    window.document.calc.mos.value = codecs[codecIndex].mos;
    window.document.calc.mips.value = codecs[codecIndex].mips;
}

