var currentHint;
var currentModel;

var defaultURLBase = "http://musicdemo.htsql.com";
var defaultURL = defaultURLBase + "/";
var metadataURL = defaultURL + "meta().json";

var metadata = new Object();
metadata.loaded = false;

var Viewport = {
    width: function() {
        return window.innerWidth || (document.documentElement.clientWidth || document.body.clientWidth);
    },

    height: function() {
        return window.innerHeight || document.documentElement.scrollHeight;
    },

    size: function() {
        return { width: this.width(), height: this.height() };
    }
};

function hint(msg) {
    ID('hint').innerHTML = msg;
}

function doReset() {
    ID('query').value = defaultURL;
    setCaretTo(ID('query'), defaultURL.length);
    ID('embedHTML').value = "";
    initialHint();
}

function embed(service, e) {
    var url = trim(ID('query').value);
    url = encodeURIComponent(url);
    e.href = "http://www.addthis.com/bookmark.php?pub=&v=250&source=tbx-250&s=" + service + "&url=" + url + "&title=&content=";
}

function embedHTML() {
    var url = trim(ID('query').value);
    url = escape(url);
//    ID('embedHTML').value = "<iframe src='" + url + "' style='width:" + ID('embedWidth').value + "px;height:" + ID('embedHeight').value + "px'> </iframe>";
    ID('embedHTML').value =
"<div style='display:none;visibility:hidden;' id='__htsql_obj_div' title='Result'><object style='width:" + ID('embedWidth').value + "px;height:" + ID('embedHeight').value + "px' id='__htsql_obj' type='text/html'> </object></div>" +
"<div style='display:none;visibility:hidden;' id='__htsql_fr_div' title='Result'><iframe style='width:" + ID('embedWidth').value + "px;height:" + ID('embedHeight').value + "px;' id='__htsql_fr'> </iframe></div>" +
"<script language='JavaScript' type='text/javascript'>" +
"var ua= navigator.userAgent,isIE=(navigator.appName == 'Microsoft Internet Explorer');" +
"var view, url='" + url + "';" +
"if (isIE) {isIE = ua.replace(/^.*?MSIE\\s+([0-9\\.]+).*$/, '$1');}" +
"if (isIE) {view = document.getElementById('__htsql_fr');view.src = url;view = document.getElementById('__htsql_fr_div');" +
"} else {view = document.getElementById('__htsql_obj');view.data = url;view = document.getElementById('__htsql_obj_div');}" +
"view.style.display = 'block';view.style.visibility = 'visible';" +
"</script>";
}

function trim(string) {
    return string.replace(/(^\s+)|(\s+$)/g, "");
}

function doRequestURL(url) {
    ID('query').value = url;
    ID('query').focus();
    hideHint();
}

function doRequestResult(url) {
    doRequestURL(url);

    if (url.indexOf(defaultURL) != 0) {
        alert('Illegal URL');
        return;
    }

    ID('resultPopupFr').src = url;
    ID('hintPopupFr').innerHTML = url;

    $('#popupFr').dialog('option', 'width', Viewport.width() * 0.85);
    $('#popupFr').dialog('option', 'height', Viewport.height() * 0.85);
    $('#popupFr').dialog('open');

    $("#loading").dialog('open');
}

function doRequest() {
    var url = trim(ID('query').value);
    doRequestResult(url);
}

function onError(request, errorType, obj) {
    alert("Error occured while getting metadata.\n" +
          "Press 'F5' to realod the page to try again.\n\nError: " + errorType);
}

/*function selectCount() {
 jQuery.ajaxSetup(
 {
 timeout: 30000,
 async: false
 });

 var out = "";
 for (var i = 0; i < metadata.tables.length; i++) {
 var table = metadata.tables[i];
 jQuery.getJSON(defaultURL + "/{count(" + table + ")}.json", function(json) {
 var count = 0;
 if (json[0]) {
 count = json[0].count;
 }

 out += "\"" + table + "\": \"" + count + "\",\n";

 });
 };

 ID('embedHTML').value = out;
 }*/

function initialHint() {
    hint('Start typing table name or click in the box to display available choices');
}

function initialize() {
    $("#popupFr").dialog({
        bgiframe: true,
        modal: true,
        title: 'Result',
        resizable: false,
        draggable: false
    });

    $("#popupFr").dialog('close');
    show("popupFr");

    $("#loading").dialog({
        bgiframe: true,
        modal: true,
        dialogClass: 'loading',
        minHeight: 30,
        minWidth: 30,
        width: 'auto',
        height: 'auto',
        resizable: false,
        draggable: false
    });

    $("#loading").dialog('close');
    show("loading");

    // hide popup's titlebars
    $('.loading div:first-child').hide();

    var correct = false;

    switch (BrowserDetect.browser) {
        case "Explorer":
            if (BrowserDetect.version >= 6) {
                correct = true;
            }
            break;
        case "Firefox":
            if (BrowserDetect.version >= 3) {
                correct = true;
            }
            break;
        case "Chrome":
            correct = true;
            break;
        case "Safari":
            correct = true;
            break;
    }

    defaultURL = trim(ID('query').value);
    show('correctBrowser');
    getMetadata();
    ID('query').focus();
    initialHint();

    if (!correct) {
        // hide popup's titlebars
        $("#incorrectBrowser").dialog({
            dialogClass: 'warning',
            position: 'top',
            bgiframe: false,
            modal: false,
            title: 'Warning',
            minHeight: '1%',
            //height: '5%',
            minWidth: '97%',
            width: '97%',
            resizable: false,
            draggable: false
        });

        $("#incorrectBrowser").dialog('close');
        show('incorrectBrowser');

        $('#incorrectBrowser').dialog('option', 'show', 'blind');
        $('#incorrectBrowser').dialog('option', 'hide', 'blind');

        // hide popup's titlebars
        $('.warning div:first-child').hide();
        $('.warning').css('background', '#ffff99');
        $("#incorrectBrowser div:first-child").show();

        setTimeout('$(\'#incorrectBrowser\').dialog(\'open\');', 1000);
    }
}

function getMetadata() {
    doReset();

    metadata.tables = new Array();
    metadata.columns = new Array();
    metadata.sorting = new Array();

    metadata.commands = new Array();
    metadata.commands[0] = {
        "key": "Descriptor Commands",
        "value": ["version()", "meta()", "describe()", "sql()", "root()"]
    };

    metadata.commands[1] = {
        "key": "Query Processor Commands",
        "value": ["select()", "insert()", "update()", "delete()", "merge()"]
    };

    metadata.commands[2] = {
        "key": "Transformer Commands",
        "value": ["flattern()", "interweave()", "first()", "pivot()", "foreach()"]
    };

    metadata.commands[3] = {
        "key": "Formatter Commands",
        "value": ["txt()", "csv()", "tsv()", "html()", "xml()", "yaml()", "json()", "jsonp()"]
    };

    if (meta && meta[0]) {
        var catalog = meta[0];
        if (catalog.aspect) {
            var i = 0;
            var t = 0;
            while (catalog.aspect[i]) {
                var aspect = catalog.aspect[i];

                if (aspect.is_readable &&
                    aspect.field &&
                    tables[aspect.id] &&
                    parseInt(tables[aspect.id]) > 0) {

                    metadata.tables[t] = aspect.id;

                    var j = 0;
                    var k = 0;
                    var initialized = false;
                    while (aspect.field[j]) {
                        var field = aspect.field[j];
                        /*if (aspect.id == "album") {
                         alert(field.id + ": " + field.is_public + ", " + field.is_column + ", " + field.is_chain);
                         }*/
                        if (field.is_public && field.is_column) {
                            if (!initialized) {
                                metadata.columns[metadata.tables[t]] = new Array();
                                initialized = true;
                            }
                            metadata.columns[metadata.tables[t]][k++] = field.id;
                        }
                        j++;
                    }

                    t++;
                }

                i++;
            }

            metadata.loaded = true;
        }
    }
}

/*function getMetadata() {
 doReset();

 jQuery.ajaxSetup(
 {
 timeout: 30000,
 error: onError
 });

 message("Loading metadata...");
 jQuery.getJSON(metadataURL, function(meta) {
 metadata.tables = new Array();
 metadata.columns = new Array();
 message("Storing metadata...");
 if (meta && meta[0]) {
 var catalog = meta[0];
 if (catalog.aspect) {
 var i = 0;
 while (catalog.aspect[i]) {
 var aspect = catalog.aspect[i];
 if (aspect.is_readable && aspect.field) {
 metadata.tables[i] = aspect.id;

 var j = 0;
 var k = 0;
 var initialized = false;
 while (aspect.field[j]) {
 var field = aspect.field[j];
 if (field.is_public && field.is_column && !field.is_chain) {
 if (!initialized) {
 metadata.columns[metadata.tables[i]] = new Array();
 initialized = true;
 }
 metadata.columns[metadata.tables[i]][k++] = field.id;
 }
 j++;
 }

 i++;
 }
 }

 metadata.loaded = true;
 message("Metadata loaded.");
 setTimeout("message('&nbsp;');", 3000);
 }
 }
 });
 }*/

function insightImpl(e) {
    var selected = getSelectedText(ID('query'));
    if (selected && selected.length > 0) {
        return;
    }

    var ret = handleEvent(e);

    if (ret == "dismiss") {
        hideHint();
        return;
    }

    if (ret == "enter") {
        if (currentHint) {
            currentHint.onclick(e);
        } else {
            doRequest();
        }
        return;
    }

    var focus = 0;
    if (ret == "focus") {
        focus = 1;
    }

    if (ret != "focus" && currentHint) {
        match();
    }

    var val = parse();
    if (val) {
        if (handlerMap.get(val)) {
            handlerMap.get(val).handle(focus);
        } else {
            handlerMap.getUniversal().handle(focus);
        }
    }

    if (currentHint && focus) {
        currentHint.focus();
    }
}

function insight(e) {
    insightImpl(e);

    var url = trim(ID('query').value);
    if (url == defaultURL && !currentHint) {
        initialHint();
    }
}

function match() {
    var name = parseInput(false, -1);

    currentHint.innerHTML = "";

    if (name) {
        var match = false;
        for (var i = 0; i < currentModel.length; i++) {
            var value = currentModel[i];
            if (value.indexOf(name) == 0) {
                match = true;
                var option = document.createElement("option");
                option.value = value;
                option.innerHTML = value;
                currentHint.appendChild(option);
            }
        }

        if (!match) {
            hideHint();
        } else {
            currentHint.selectedIndex = 0;
            currentHint.options[0].selected = true;

            currentHint.onclick = function(e) {
                applyHint(e, 1, name.length);
            };
            currentHint.onkeyup = function(e) {
                applyHint(e, 0, name.length);
            };
        }
    } else {
        hideHint();
    }
}

function showTables() {
    var id = 'div_tables';
    if (ID(id)) {
        if (ID(id) == currentHint) {
            return;
        }

        currentHint = ID(id);
        currentModel = metadata.tables;

        var height = getStyle('query', 'height');
        var position = getPosition('query');

        height = parseInt(height.replace("px", ""));

        ID(id).style.left = position.x + "px";
        ID(id).style.top = (position.y + height) + "px";

        show(id);
        hint('Choose one of the tables listed below');
    } else {
        if (!metadata.loaded) {
            return;
        }

        if (!metadata.tables) {
            return;
        }

        // create a new element
        var select = document.createElement("select");
        select.id = id;
        select.size = 6;
        select.style.zIndex = 10000;
        select.style.position = "absolute";
        select.style.visibility = "hidden";
        select.style.display = "none";

        select.onclick = function(e) {
            applyHint(e, 1, 0);
        };

        select.onkeyup = function(e) {
            applyHint(e, 0, 0);
        };

        for (var i = 0; i < metadata.tables.length; i++) {
            var name = metadata.tables[i];
            var option = document.createElement("option");
            option.value = name;
            option.innerHTML = name;

            select.appendChild(option);
        }

        document.body.appendChild(select);

        showTables();
    }
}

function showCommands(table, includeTables) {
    var id = "div_table_" + table;
    if (ID(id)) {
        if (ID(id) == currentHint) {
            return;
        }

        currentHint = ID(id);

        var height = getStyle('query', 'height');
        var position = getPosition('query');


        height = parseInt(height.replace("px", ""));

        ID(id).style.left = position.x + "px";
        ID(id).style.top = (position.y + height) + "px";

        show(id);
        if (includeTables) {
            hint('Choose one of the columns of \'' + table + '\' table or commands listed below');
        } else {
            hint('Choose one of the commands listed below');
        }
    } else {
        if (!metadata.loaded) {
            return;
        }

        if (!metadata.columns[table] && includeTables) {
            includeTables = false;
        }

        // create a new element
        var select = document.createElement("select");
        select.id = id;
        select.size = 6;

        select.style.zIndex = 10000;
        select.style.position = "absolute";
        select.style.visibility = "hidden";
        select.style.display = "none";

        select.onclick = function(e) {
            applyHint(e, 1, 0);
        };
        select.onkeyup = function(e) {
            applyHint(e, 0, 0);
        };

        var elem;
        
        currentModel = new Array();
        var m = 0;

        if (includeTables) {
            elem = document.createElement("optgroup");
            elem.label = "Table Columns:";
            select.appendChild(elem);

            for (var i = 0; i < metadata.columns[table].length; i++) {
                var name = metadata.columns[table][i];
                var option = document.createElement("option");
                option.value = name;
                option.innerHTML = name;

                elem.appendChild(option);

                currentModel[m++] = name;
            }
        }

        for (var i = 0; i < metadata.commands.length; i++) {
            var command = metadata.commands[i];
            elem = document.createElement("optgroup");
            elem.label = command["key"];
            select.appendChild(elem);

            var commands = command["value"];
            for (var j = 0; j < commands.length; j++) {
                var name = commands[j];
                var option = document.createElement("option");
                option.value = name;
                option.innerHTML = name;

                elem.appendChild(option);

                currentModel[m++] = name;
            }
        }

        document.body.appendChild(select);

        showCommands(table, includeTables);
    }
}

function showTable(table) {
    var id = "div_table_" + table;
    if (ID(id)) {
        if (ID(id) == currentHint) {
            return;
        }

        currentHint = ID(id);
        currentModel = metadata.columns[table];

        var height = getStyle('query', 'height');
        var position = getPosition('query');


        height = parseInt(height.replace("px", ""));

        ID(id).style.left = position.x + "px";
        ID(id).style.top = (position.y + height) + "px";

        show(id);
        hint('Choose one of the columns of \'' + table + '\' table listed below');
    } else {
        if (!metadata.loaded) {
            return;
        }

        if (!metadata.columns[table]) {
            return;
        }

        // create a new element
        var select = document.createElement("select");
        select.id = id;
        select.size = 6;

        select.style.zIndex = 10000;
        select.style.position = "absolute";
        select.style.visibility = "hidden";
        select.style.display = "none";

        select.onclick = function(e) {
            applyHint(e, 1, 0);
        };
        select.onkeyup = function(e) {
            applyHint(e, 0, 0);
        };

        for (var i = 0; i < metadata.columns[table].length; i++) {
            var name = metadata.columns[table][i];
            var option = document.createElement("option");
            option.value = name;
            option.innerHTML = name;

            select.appendChild(option);
        }

        document.body.appendChild(select);

        showTable(table);
    }
}

function showColumnSorting() {
    var id = "div_sorting_";
    if (ID(id)) {
        if (ID(id) == currentHint) {
            return;
        }

        currentHint = ID(id);
        currentModel = metadata.sorting;

        var height = getStyle('query', 'height');
        var position = getPosition('query');

        height = parseInt(height.replace("px", ""));

        ID(id).style.left = position.x + "px";
        ID(id).style.top = (position.y + height) + "px";

        show(id);
        hint('add a &#43; or &#45; to sort in ascending or descending order');
    } else {
        if (!metadata.loaded) {
            return;
        }

        if (!metadata.sorting) {
            return;
        }

        // create a new element
        var select = document.createElement("select");
        select.id = id;
        select.size = 6;

        select.style.zIndex = 10000;
        select.style.position = "absolute";
        select.style.visibility = "hidden";
        select.style.display = "none";

        select.onclick = function(e) {
            applyHint(e, 1, 0);
        };
        select.onkeyup = function(e) {
            applyHint(e, 0, 0);
        };

        var option;
        option = document.createElement("option");
        option.value = "+";
        option.innerHTML = "add &#43; to sort in ascending order";
        select.appendChild(option);

        option = document.createElement("option");
        option.value = "-";
        option.innerHTML = "add &#45; to sort in descending order";
        select.appendChild(option);

        document.body.appendChild(select);

        showColumnSorting();
    }
}

function hideHint() {
    if (currentHint) {
        currentHint.style.visibility = "hidden";
        currentHint.style.display = "none";
        document.body.removeChild(currentHint);
        currentHint = null;
        currentModel = null;
        hint('&nbsp;');
    }
}

function handleEvent(e) {
    if (!e) {
        e = window.event;
    }

    if (!e) {
        return "dismiss";
    }

    if (e.keyCode == 27) {
        // escape
        return "dismiss";
    }

    if (e.keyCode == 38 || e.keyCode == 40) {
        // up or down
        return "focus";
    }

    if (e.keyCode == 13) {
        // enter (return)
        return "enter";
    }

    if (e.keyCode == 189 ||
        (e.keyCode >= 48 && e.keyCode <= 57) ||
        (e.keyCode >= 65 && e.keyCode <= 90)) {
        return "input";
    }

    return "parse";
}

function parse() {
    var pos = caretAt(ID('query'));
    if (pos) {
        var val = ID('query').value.substr(pos - 1, 1);
        return val;
    }
}

function findSymbol(symbol, decreasePos) {
    if (!symbol) {
        return -1;
    }

    var pos = caretAt(ID('query'));

    if (pos) {
        if (decreasePos) {
            pos--;
        }

        while (pos >= 0) {
            var val = ID('query').value.substr(pos - 1, 1);
            if (val == symbol) {
                return pos - 1;
            }
            pos--;
        }
    }

    return -1;
}

function parseInput(decreasePos, initialPos) {
    var pos = initialPos;
    if (initialPos == -1) {
        pos = caretAt(ID('query'));
    }

    if (pos) {
        if (decreasePos) {
            pos--;
        }
        initialPos = pos;
        var matcher = new RegExp("\\w+[\\d\\w]*");

        while (pos >= 0) {
            var val = ID('query').value.substr(pos - 1, 1);
            if (!val.match(matcher)) {
                break;
            }
            pos--;
        }

        return ID('query').value.substring(pos, initialPos);
    }
}

function parseName() {
    return parseInput(true, -1);
}

function applyHint(e, forse, length) {
    if (!forse) {
        var ret = handleEvent(e);
        if (ret == "dismiss") {
            hideHint();
            ID('query').focus();
            return;
        }
        if (ret != "enter") {
            return;
        }
    }

    var index = currentHint.selectedIndex;
    var value = currentHint.options[index].value;

    hideHint();

    if (length > 0) {
        value = value.substring(length);
        if (value.length < 1) {
            ID('query').focus();
            return;
        }
    }

    insertAtCaret(ID('query'), value);
}

function hide(id) {
    var element = ID(id);
    element.style.visibility = "hidden";
    element.style.display = "none";
}

function show(id) {
    var element = ID(id);
    element.style.visibility = "visible";
    element.style.display = "block";
}

function ID(id) {
    return document.getElementById(id);
}

function getStyle(id, name) {
    var e = ID(id);
    // IE & Opera
    if (e.currentStyle) {
        return e.currentStyle[name];
    }
    // Gecko
    if (document.defaultView) {
        return document.defaultView.getComputedStyle(e, null).getPropertyValue(
                name.replace(/[A-Z]/g, function(a) {
                    return '-' + a;
                }));
    }
    return e.style[name];
}

function getPosition(id) {
    var x = 0, y = 0, r = ID(id);
    while (r) {
        x += r.offsetLeft || 0;
        y += r.offsetTop || 0;
        r = r.offsetParent;
    }
    return {x : x, y : y};
}

function getTopPos(inputObj)
{

    var returnValue = inputObj.offsetTop + inputObj.offsetHeight;
    while ((inputObj = inputObj.offsetParent) != null)returnValue += inputObj.offsetTop;
    return returnValue + calendar_offsetTop;
}

function getleftPos(inputObj)
{
    var returnValue = inputObj.offsetLeft;
    while ((inputObj = inputObj.offsetParent) != null)returnValue += inputObj.offsetLeft;
    return returnValue + calendar_offsetLeft;
}

function isCaretAtEnd(obj) {
    var pos = caretAt(obj);
    if (pos) {
        return pos == obj.value.length;
    } else {
        return false;
    }
}

function getSelectedText(obj) {
    if (document.selection) {
        var range = document.selection.createRange();
        return range.text;
    } else if (obj.selectionStart) {
        var start = obj.selectionStart;
        var end = obj.selectionEnd;
        if (start > -1 && end > -1 && end > start) {
            return obj.value.substr(start, end - start);
        }
    }

    return "";
}

function caretAt(obj) {
    if (document.selection) {
        obj.focus();
        var orig = obj.value.replace(/\r\n/g, "\n");
        var range = document.selection.createRange();

        if (range.parentElement() != obj) {
            return false;
        }

        var text = "sometext";
        range.text = text;

        var actual = tmp = obj.value.replace(/\r\n/g, "\n");

        for (var diff = 0; diff < orig.length; diff++) {
            if (orig.charAt(diff) != actual.charAt(diff)) break;
        }

        for (var index = 0, start = 0;
             tmp.match(text)
                     && (tmp = tmp.replace(text, ""))
                     && index <= diff;
             index = start + text.length
                ) {
            start = actual.indexOf(text, index);
        }

        obj.value = orig;
    } else if (obj.selectionStart) {
        var start = obj.selectionStart;
    }

    if (start != null) {
        setCaretTo(obj, start);
        return start;
    } else {
        return obj.value.length;
    }
}

function insertAtCaret(obj, text) {
    if (document.selection) {
        obj.focus();
        var orig = obj.value.replace(/\r\n/g, "\n");
        var range = document.selection.createRange();

        if (range.parentElement() != obj) {
            return false;
        }

        range.text = text;

        var actual = tmp = obj.value.replace(/\r\n/g, "\n");

        for (var diff = 0; diff < orig.length; diff++) {
            if (orig.charAt(diff) != actual.charAt(diff)) break;
        }

        for (var index = 0, start = 0;
             tmp.match(text)
                     && (tmp = tmp.replace(text, ""))
                     && index <= diff;
             index = start + text.length
                ) {
            start = actual.indexOf(text, index);
        }
    } else if (obj.selectionStart) {
        var start = obj.selectionStart;
        var end = obj.selectionEnd;

        obj.value = obj.value.substr(0, start)
                + text
                + obj.value.substr(end, obj.value.length);
    }

    if (start != null) {
        setCaretTo(obj, start + text.length);
    } else {
        obj.value += text;
    }
}

function setCaretTo(obj, pos) {
    if (obj.createTextRange) {
        var range = obj.createTextRange();
        range.move('character', pos);
        range.select();
    } else if (obj.selectionStart) {
        obj.focus();
        obj.setSelectionRange(pos, pos);
    }
}