//
//
//

/**
* getArgument in url for passed vars
* @param {String} vars
* @returns {Array} Arguments to vars passed 
*/
function getArguments(vars) {
    var args = {};
    var split = window.location.search.substr(1).split('&');
    for (var i in split) {
        var kv = split[i].split('=');
        if ($.inArray(kv[0], vars) != -1)
            args[kv[0]] = decodeURIComponent(kv[1]);
    }
    return args;
}

/**
* parses query (f = find in bin name, q = query string to find)
* @param {String} f Field to search in ("location", "keyword", "description", "transcript" or "creator") - defaults to "all" if none supplied.
* @param {String} q Search query string
* @returns {Json} f = string(f), q=string(q)
*/
function parseQuery(f, q) {
    var find = ['location', 'keyword', 'description', 'transcript', 'creator'];
    if (typeof(f) == 'undefined' || $.inArray(f, find) == -1)
        f = 'all';
    if (q) {
        var split = q.split(':');
        if (split.length > 1 && f == 'all' && $.inArray(split[0], find)) {
            f = split.shift();
            q = split.join(':');
        }
    }
    return {'f': f, 'q': q};
}

/**
* loads template
* @param {String} template name of template
* @param {String} callback function that should be called with template
*/
function loadTemplate(template, callback) {
    var template_base = base_url + '/static/templates/';
    if(typeof(cache) == 'undefined')
      $.get(template_base + template, callback);
    else
      cache.get(template_base + template, callback);
}

//  ----------------------------------------------------------------------------
//  Requests controller prototype
//  ----------------------------------------------------------------------------

/**
* @class Requests Controller Class to cache post requests, show loadiing, etc.
* @property {Int} state Number of active requests
* @property {Array} cache Holds Array of loading images
* @property {Int} step Step in loading (1 to 11)
* @property {Int} interval Interval ID for setInterval calling  this.loading
* @constructor
*/ 
function RequestsController() {
    /*
        state: number of active requests
    */
    this.state = 0;

    // images have to be refered in a variable that stays referenced.
    // otherwise firefox removes the images from cache and they get loaded again.
    this.cache = [];
    for (var i = 0; i <= 11; i++) {
        this.cache[i] = new Image;
        this.cache[i].src = base_url + '/static/images/loading' + i + '.png';
    }
}

/**
* Add a request to queue
* @param {String} url Url to call with Post request
* @param {Json} parameters Json object with parameters to pass in post request
* @param {Function} Callback function to call after post request completed. Called with "result" from server.
*/
RequestsController.prototype.add = function(url, parameters, callback) {
    //alert(url + ' ' + parameters.toSource())
    this.startLoading();
    url = base_url + url;
    if (typeof video != 'undefined' && video.local) {
        //netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
    }
    jQuery.post(url, parameters, function(result) { requests.stopLoading(); callback(result); }, 'json');
    //jQuery.getJSON(url, parameters, function(result) { requests.stopLoading(); callback(result); });
}

/**
* Starts visual display of loading request - calls requests.loading() every 83 ms
*/
RequestsController.prototype.startLoading = function() {
    this.state++;
    if (this.state == 1) {
        this.step = 0;
        $('#loading').attr('src', this.cache[this.step].src);
        $('#loading').show();
        // fixme: do i need window?
        this.interval = window.setInterval('requests.loading()', 83);
    }
}

/**
* Increments loading image to next to show loading animation
*/
RequestsController.prototype.loading = function() {
    if (this.step < 11)
        this.step++;
    else
        this.step = 0;
    $('#loading').attr('src', this.cache[this.step].src)
}

/**
* Called when done loading request - stops loading display
*/
RequestsController.prototype.stopLoading = function() {
    this.state--;
    if (this.state == 0) {
        window.clearInterval(this.interval);
        $('#loading').hide();
    }
}

//  ----------------------------------------------------------------------------
//  Box prototype
//  ----------------------------------------------------------------------------
/**
* @class Box
* @property {jQuery Object} parent Parent jQ obj. to append box to
* @property {String} name Name of box
* @property {String} className Name of CSS class to be applied to box
* @property {Boolean} hasTitlebar
* @property {Boolean} hasEditbar
* @property {Boolean} hasToolbar
* @property {Int} height Height of box (in px)
* @property {jQuery Object} content jQuery element with content area of box.
* @property {Array} button Array of button objects belonging to box
* @property {Int} state States: 0 (collapsed), 1 (expanded)
* @constructor
* @param {jQuery Object} parent Parent jQ obj. to append box to
* @param {String} name Name of box
* @param {String} className Name of CSS class to be applied to box
* @param {Boolean} hasTitlebar
* @param {Boolean} hasEditbar
* @param {Boolean} hasToolbar
* @param {Int} height Height of box (in px)
*/
function Box(parent, name, className, hasTitlebar, hasEditbar, hasToolbar, height) {
    /*
        state: 0 (collapsed), 1 (expanded)
    */
    this.parent = parent;
    this.name = name;
    this.className = className
    // this.id = this.parent.attr('id') + this.name.capitalize() + 'Box';
    this.hasTitlebar = hasTitlebar;
    this.hasEditbar = hasEditbar;
    this.hasToolbar = hasToolbar;
    this.height = height;
    this.button = [];
    this.state = 1;
}

/**
* Loads Box CSS, appends to parent
*/
Box.prototype.load = function() {
    var element = $(parseTemplate(htmlBox, {'name': this.name}));
    // fixme: this.title, this.titlebar etc...
    this.content = element.find('#' + this.name + 'Content');
    if (this.height > 0) {
        this.content.css('height', (this.height - 38) + 'px');
        this.content.css('overflow', 'auto');
    }
    element.addClass(this.className);
    if (!this.hasTitlebar)
        element.find('#' + this.name + 'Titlebar').css('display', 'none');
    if (!this.hasEditbar)
        element.find('#' + this.name + 'Editbar').css('display', 'none');
    if (!this.hasToolbar)
        element.find('#' + this.name + 'Toolbar').css('display', 'none');
    this.parent.append(element);
}

/**
* Sets Box title ('#' + this.name + 'Title') to title
* @param {String} title New title for box
*/
Box.prototype.setTitle = function(title) {
    this.parent.find('#' + this.name + 'Title').html(title);
}

/**
* Sets Box content to content passed
* @param {jQuery Object} element jQuery element to replace current content with
*/
Box.prototype.replaceContent = function(element) {
    this.content.replaceWith(element);
    this.content = element;
}

/**
* Set content html to element passed
* @param {jQuery Object} element
*/
Box.prototype.setContent = function(element) {
    this.content.html(element);
}

/**
* Prepend element to current box content
* @param {jQuery Object} element
*/
Box.prototype.prependContent = function(element) {
    this.content.prepend(element);
}

/**
* Append element to current box content
* @param {jQuery Object} element
*/
Box.prototype.appendContent = function(element) {
    this.content.append(element);
}

/**
* Clear box.content
*/
Box.prototype.clearContent = function() {
    this.content.html('');
}

/**
* Adds a button to box
* @param {String} corner Corner to add button to ("TopLeft", "TopRight", "BottomLeft" or "BottomRight")
* @param {String} name Name of button
* @param {String} title Helper title for button
* @param {Function} action Onclick function for button
*/
Box.prototype.addButton = function(corner, name, title, action) {
    var parent = this.parent.find('#' + this.name + 'Buttons' + corner);
    this.button[name] = new Button(parent, name, title, action);
}

/**
* Adds a switch to box
* @param {String} corner Corner to add switch to ("TopLeft", "TopRight", "BottomLeft" or "BottomRight")
* @param {String} name0 Name of switch in off position
* @param {String} title0 Title of switch in off position
* @param {Function} action0 Action for switch in off position
* @param {String} name1 Name for switch in on position
* @param {String} title1 Title for switch in on position
* @param {Function} action1 Action for switch in on position
*/
Box.prototype.addSwitch = function(corner, name0, title0, action0, name1, title1, action1) {
    var parent = this.parent.find('#' + this.name + 'Buttons' + corner);
    this.button[name0] = new Switch(parent, name0, title0, action0, name1, title1, action1);
}

/**
* Remove button with name passed
* @param {String} name Name of button to remove
*/
Box.prototype.removeButton = function(name) {
    this.button[name].remove();
}

/**
* Remove switch with name passed (calls removeButton with switch name) --fixme to remove this function as it is redundant
* @param {String} name Name of Switch to remove
*/
Box.prototype.removeSwitch = function(name) {
    // fixme: remove removeSwitch
    this.removeButton(name);
}

/**
* Collapse box - removes css class "expanded" from Titlebar, hides boxBody, fires bin onCollapse function
*/
Box.prototype.collapse = function() {
    this.state = 0;
    $('#' + this.name + 'Titlebar').removeClass('expanded');
    $('#' + this.name + 'Body').hide();
    if (typeof(bin) != 'undefined' && typeof(bin[this.name]) != 'undefined')
        bin[this.name].onCollapse();
}

/**
* Expand box - adds css class "expanded" to Titlebar, shows boxBody, fires bin onExpand function
*/
Box.prototype.expand = function() {
    this.state = 1;
    $('#' + this.name + 'Titlebar').addClass('expanded');
    $('#' + this.name + 'Body').show();
    if (typeof(bin) != 'undefined' && typeof(bin[this.name]) != 'undefined')
        bin[this.name].onExpand();
}

/**
* Show Editbar $('#' + this.name + 'Editbar')
*/
Box.prototype.showEditbar = function() {
    this.hasEditbar = true;
    $('#' + this.name + 'Editbar').show();
}

/**
* Hide Editbar
*/
Box.prototype.hideEditbar = function() {
    this.hasEditbar = false;
    $('#' + this.name + 'Editbar').hide();
}

/**
* Show Toolbar $('#' + this.name + 'Toolbar')
*/
Box.prototype.showToolbar = function() {
    this.hasToolbar = true;
    $('#' + this.name + 'Toolbar').show();
}

/**
* Hide Toolbar
*/
Box.prototype.hideToolbar = function() {
    this.hasToolbar = false;
    $('#' + this.name + 'Toolbar').hide();
}

//  ----------------------------------------------------------------------------
//  Button and Switch prototypes
//  ----------------------------------------------------------------------------
/**
* @class Button
* @property {jQuery Object} parent Parent element to append button to
* @property {String} name Name of button
* @property {String} title Helpful title for button
* @property {Function} action Function that is called on button click
* @property {jQuery Object} element jQuery image element for button
* @property {String} id Formed by parentId + this.name.capitalize() + "Button"
* @constructor
* @param {jQuery object} parent
* @param {String} name
* @param {String} title
* @param {Function} action
*/
function Button(parent, name, title, action) {
    if (arguments.length > 0)
        this.init(parent, name, title, action);
}

/**
* Inits button, calls this.load()
*/
Button.prototype.init = function(parent, name, title, action) {
    this.parent = parent;
    this.name = name;
    this.id = this.parent.attr('id') + this.name.capitalize() + 'Button';
    this.title = title;
    this.action = action;
    // fixme: why is the load function here?
    this.load();
}

/**
* Loads Button.element, calls this.enable()
*/
Button.prototype.load = function() {
    this.element = $('<img />');
    this.element.addClass('button');
    this.element.attr('id', this.id);
    this.element.attr('src', base_url + '/static/images/button' + this.name.capitalize() + '.png');
    this.enable();
    this.parent.append(this.element);
}

/**
* Removes Button
*/
Button.prototype.remove = function() {
    $('#' + this.id).remove();
}

/**
* Enables button, adds "enabled" class and event handler for onclick
*/
Button.prototype.enable = function() {
    this.element.removeClass('disabled');
    this.element.addClass('enabled');
    this.element.attr('title', this.title);
    var t = this;
    this.element.unbind('click');
    this.element.click(function() { t.action() });
}

/**
* Disables button
*/
Button.prototype.disable = function() {
    this.element.removeClass('enabled');
    this.element.addClass('disabled');
    this.element.attr('title', '')
    this.element.unbind('click');
}

/**
* Sets title for button to title passed
* @param {String} title New title for button
*/
Button.prototype.setTitle = function(title) {
    this.title = title;
    this.element.attr('title', this.title);
}

/**
* @augments Button
* @class Switch Switch class - for toggle buttons
* @property {jQuery Object} parent
* @property {String} name0
* @property {String} title0
* @property {Function} action0
* @property {String} name1
* @property {String} title1
* @property {Function} action1
* @property {Int} state 0 or 1
* @property {jQuery Object} element jQuery element of switch
* @constructor
* @param {jQuery Object} parent jQuery element of parent to add switch to.
* @param {String} name0 Name of switch in off position
* @param {String} title0 Title of switch in off position
* @param {Function} action0 Action for switch in off position
* @param {String} name1 Name for switch in on position
* @param {String} title1 Title for switch in on position
* @param {Function} action1 Action for switch in on position
*/
function Switch(parent, name0, title0, action0, name1, title1, action1) {
    this.state = 0;
    this.name0 = name0;
    this.title0 = title0;
    this.action0 = action0;
    this.name1 = name1;
    this.title1 = title1;
    this.action1 = action1;
    this.init(parent, name0, title0, action0);
}
Switch.prototype = new Button();

/**
* Enable switch
*/
Switch.prototype.enable = function() {
    Button.prototype.enable.call(this);
    /* jQuery bug, only one click event gets unbound with unbind */
    this.element.unbind('click');
    var t = this;
    this.element.click(function() { t.action(); t.change() });    
}

/**
* Change switch state.
*/
Switch.prototype.change = function() {
    if (this.title == this.title0) {
        this.state = 1;
        this.name = this.name1;
        this.title = this.title1;
        this.action = this.action1;
    }
    else {
        this.state = 0;
        this.name = this.name0;
        this.title = this.title0;
        this.action = this.action0;
    }
    this.element.attr('src', base_url + '/static/images/button' + this.name.capitalize() + '.png');
    this.element.attr('title', this.title);
    //alert('changed to ' + this.title)
}

//  ----------------------------------------------------------------------------
//  Dialog prototype
//  ----------------------------------------------------------------------------

/**
* @class Dialog Class to load content in and show modal dialog box
* @property {String} title
* @property {String} content
* @property {Array} buttons Array of button objects
* @property {Int} width Width of dialog (in px)
* @property {Int} height Height of dialog (in px)
* @property {Int} keyboardState Keyboard State (0 no focus, 1 focus on input, 2 focus on textarea)
* @constructor
* @param {String} title
* @param {String} content
* @param {Array} buttons
* @param {Int} width
* @param {Int} height
*/
function Dialog(title, content, buttons, width, height) {
    this.title = title;
    this.content = content;
    this.buttons = buttons;
    this.width = width;
    this.height = height;
}

/**
* Load dialog.html template and set keyboard state
*/
Dialog.prototype.load = function(callback) {
    if (typeof keyboard != 'undefined') {
        this.keyboardState = keyboard.state;
        keyboard.setState(1);
        if (this.title == 'Edit Description')
            keyboard.setState(2);
    }
    var t = this;
    loadTemplate('dialog.html', function(html) {
        t.html = html;
        t.open();
        if (typeof callback == 'function') {
            callback();
        }
    });
}

/**
* Open dialog - set css, etc - element is appended to body
*/
Dialog.prototype.open = function() {
    var element = $('<div></div>');
    element.attr('id', 'background');
    $('body').append(element);
    element = $(this.html);
    element.css('left', Math.round((window.innerWidth - this.width) / 2) + 'px');
    element.css('top', Math.round((window.innerHeight - this.height) / 2) + 'px');
    element.css('width', this.width + 'px');
    element.css('height', this.height + 'px');
    element.find('.center').css('width', (this.width - 16) + 'px');
    element.find('.titlebar').html(this.title);
    element.find('.content').css('height', (this.height - 82) + 'px');
    if (this.title == 'Edit Description')
        element.find('.content').css('overflow', 'hidden');
    element.find('.content').html(this.content);
    // fixme: margin-bottom of the content is not visible
    for (var k in this.buttons) {
        var elementButton = $('<div class="element elementButton dialogButton enabled right"></div>');
        elementButton.click(this.buttons[k][1]);
        elementButton.html(this.buttons[k][0]);
        element.find('.buttonbar').append(elementButton);
    }
    $('body').append(element);
}

/**
* Close dialog - resets keyboardState and closes dialog. deletes dialog var
*/
Dialog.prototype.close = function() {
    if (typeof keyboard != 'undefined') {
        keyboard.setState(this.keyboardState);
    }
    $('#dialog').remove();
    $('#background').remove();
    delete dialog;
}

//  ----------------------------------------------------------------------------
//  Cache prototype
//  ----------------------------------------------------------------------------

/**
* @class Cache class to handle caching of url fetches
* @property cache {Object} Json object holding urls already cached
* @property pending {Object} Json object holding urls pending
*/
function Cache() {
    this.cache = {};
    this.pending = {};
}

/**
* Add a url to the cache
* @param {String} url Url to be loaded
* @param {Function} callback Callback function to be fired after url is loaded
*/
Cache.prototype.get = function(url, callback) {
    var t = this;
    if (this.cache[url])
        callback(this.cache[url])
    else if (this.pending[url])
        setTimeout(function() { t.get(url, callback)}, 1)          
    else {
        this.pending[url] = true;
        $.get(url, function(html) {
            t.pending[url] = false;
            t.cache[url] = html
            callback(html)
        });
    }
}

//  ----------------------------------------------------------------------------
//  Search History prototype
//  ----------------------------------------------------------------------------

/**
* @class Class to maintan Search History and append to selectQuery select box
* @property {Array} history Array of searches
* @property {Int} len Length of search history, default = 10
* @constructor
*/
function SearchHistory() {
    this.history = [];
    this.len = 10;
}

/**
* Add a query to the search history
* @param {String} query Query to add to search history
*/
SearchHistory.prototype.add = function(query) {
    var k = $.inArray(query, this.history);
    if (k != -1)
        this.history.splice(k, 1)
    this.history.unshift(query);
    if (this.history.length > this.len)
        this.history.pop();
    if (this.history.length) {
        for (var k in this.history)
            document.getElementById('selectQuery').options[parseInt(k) + 1] = new Option(this.history[k], this.history[k]);
        document.getElementById('selectQuery').options[this.history.length + 1] = new Option('Clear Recent Searches', '[Clear Recent Searches]');
    }
}

/**
* Clear search history
*/
SearchHistory.prototype.clear = function() {
    for (var k in this.history)
        document.getElementById('selectQuery').options[1] = null;
    document.getElementById('selectQuery').options[1] = null;
    this.history = [];
}

//
//
//
/**
* Function to parse a template - replaces pattern _(no)_ with vars passed
* @param {String} html Html template to parse
* @param {Array} vars Array of vars to replace place-holders in template
* @returns {String} html Returns template with place-holders replace with variables
*/
function parseTemplate(html, vars) {
    for (var k in vars) {
        var regexp = new RegExp('_' + k + '_', 'g')
        html = html.replace(regexp , vars[k]);
    }
    return html;
}

//
//
//
/**
* Formats bytes into something more meaningful
* @param {Int} bytes No of bytes
* @returns {String} Nicely formatted, like 20MB instead of 20000000 bytes
*/
function formatBytes(bytes) {
    var suffix = ['KB', 'MB', 'GB', 'TB', 'PB'];
    var length = suffix.length;
    for (var i = 0; i < length; i++) {
        if (bytes < Math.pow(1024, i + 2)) {
            var value = bytes / Math.pow(1024, i + 1);
            var pow = Math.pow(10, i);
            value = Math.round(value * pow) / pow;
            return value + ' ' + suffix[i];
        }
    }
}

/**
* Returns time-code in npt format from pos in miliseconds
* @param {Int} pos Position in miliseconds
* @returns {String} Time-code in npt format.
*/
function getTimecode(pos) {
    var h = Math.floor(pos / 3600000);
    var m = Math.floor(pos % 3600000 / 60000);
    var s = Math.floor(pos % 60000 / 1000);
    var ms = pos % 1000;
    return h.toString().pad('0', 2) + ':' + m.toString().pad('0', 2) + ':' + s.toString().pad('0', 2) + '.' + ms.toString().pad('0', 3);
    // return strpad(h.toString(), '0', 2, 'left') + ':' + strpad(m.toString(), '0', 2, 'left') + ':' + strpad(s.toString(), '0', 2, 'left') + '.' + strpad(ms.toString(), '0', 3, 'left');
}

/**
* Returns time-code in miliseconds from npt
* @param {String} timecode Time-code in npt format
* @returns {Int} Timecode in miliseconds
* @throws {Int} -1 if time-code is not valid npt
*/
function getPos(timecode) {
    var split = timecode.split(':');
    if (split.length > 3)
        return -1;
    while (split.length < 3)
        split.unshift('0');
    for (k in split) {
        if (!isNumber(split[k]))
            return -1;
    }
    return split[0] * 3600000 + split[1] * 60000 + Math.round(split[2] * 1000);
}

/**
* Checks if a variable is a number
* @param {String} String to check for numericality
* @returns {Boolean} True if is a number, false if is not
*/
function isNumber(str) {
    if (str.indexOf('.') != str.lastIndexOf('.'))
        return false
    var len = str.length;
    for (var i = 0; i < len; i++) {
        if ('.0123456789'.indexOf(str.substr(i, 1)) == -1)
            return false;
    }
    return true;
}
