// data
var YY = null;
var onunload = false;
var reload = false;
var dirty_options = true;
var dirty_config = true;
var dirty_saved = true;
var visited_customize = false;
var current_product_list = null;
var combos = null;
var valid_combo = false;

// misc. functions
Object.size = function (obj)
{
    var key, len = 0;

    for (key in obj)
    {
        if (obj.hasOwnProperty(key)) len++;
    }

    return len;
}

// options functions
var bad_choice;
var validate_options;
var set_options;

// stats functions
var price_print;
var update_stats;
var update_warnings;
var update_info;

// config functions
var set_config;
var choose_config;

// page selection functions
var select_page;
var show_page;

// drag & drop helpers
var make_product;
var make_draggable;
var make_droppable;

// i/o completion handlers
var json_complete;
var restore_complete;
var options_complete;
var configurations_complete;
var products_complete;
var save_complete;
var load_complete;
var list_complete;
var submit_complete;

// i/o completion handler data
var io_ids = {
    'restore_complete' : null,
    'options_complete' : null,
    'configurations_complete' : null,
    'products_complete' : null,
    'save_complete' : null,
    'load_complete' : null,
    'list_complete' : null,
    'submitting_complete' : null
};

// load information and helpers
var load_id = null;
var delete_id = null;
var select_load_id;
var select_delete_id;

// submit information and helpers
var submit_id = null;
var save_and_submit;
var submit_for_quote;


// what happens when the user selects and invalid combination of options?
bad_choice = function()
{
    alert("That combination of options is currently unavailable.");
    show_page('options');
    return false;
}


// functions for restoring whatever configuration you had the last time you saved
restore_complete = function(Y, data)
{
    if (data != null && data.opts != undefined)
    {
        load_complete(Y, data);
        return;
    }

    select_page('options');
}


// functions for options
options_complete = function(Y, data)
{
    // set options
    var parent = Y.Node.one('div#option_groups');
    var div, children, groups, group, opt, id, i, q, x, row, html;

    // clear everything first
    parent.set('innerHTML', '');

    // demultiplex data
    groups = data.groups;
    combos = data.combos;

    // create option groups
    for (x in groups)
    {
        group = groups[x];
        row = ((x%2 == 0) ? 'even' : 'odd');
        html = '<div id="' + group.id + '" class="option_group ' + row + '"><h4>' + group.name + '</h4>';
        html += '<p><span class="description">' + group.description + '</span></p></div>';
        parent.append(html);
        children = parent.get('children');
        div = children.item(children.size() - 1);

        // generate options
        for (i in group.options)
        {
            opt = group.options[i];
            id = group.id + '_' + opt.replace(/[^A-Za-z]/g, '_');
            div.append('<p>' +
                       '<input type="radio" class="option" id="' + id +
                       '" name="' + group.id + '" value="' + opt + '"' +
                       ' onchange="dirty_options=true"></input>' +
                       '<label for="' + id + '" class="option right">' +
                       '<span class="option">' + opt + '</span></label>' +
                       '</p>');
        }

        // select default option
        id = group.id + '_' + group.select.replace(/[^A-Za-z]/g, '_');
        q = $('#' + id);
        if (q.size() == 0) q = $('div#' + group.id + ' input.option:first');
        q.attr('checked', 'checked');
    }

    // show options page
    dirty_options = true;
    show_page('options');
}

validate_options = function()
{
    var x, valid, combo, q = $('div#option_groups input.option:checked');

    for (x in combos)
    {
        combo = combos[x];
        valid = true;

        q.each(function (x) {
            var e, group;
            e = jQuery(this);
            group = e.attr('name');

            if (combo[group] != e.val())
            {
                valid = false;
                return false;   // breaks each()
            }
        });

        if (valid) return true;
    }

    return false;
}

set_options = function(Y, data, opts)
{
    var parent, root = $('#option_groups');
    var i, x, group, id, q;

    // set option groups
    for (x in data.groups)
    {
        group = data.groups[x];
        parent = $('#' + group.id, root);
        if ((parent.size() == 0) || (opts[group.id] == null)) continue;
        id = group.id + '_' + opts[group.id].replace(/[^A-Za-z]/g, '_');
        q = $('#' + id, parent);

        if (q.size() != 0)
        {
            $(':checked', parent).removeAttr('checked');
            q.attr('checked', 'checked');
        }
    }

    // set number of terabytes
    $('input#num_tb').val(opts['num_tb']);

    // set currency
    $('#currency').val(opts['currency']);

    // set flags
    dirty_options = true;
}

// functions for configurations
configurations_complete = function(Y, data)
{
    var configurations = Y.Node.one('div#configurations');
    var x, obj;

    // clear everything first
    configurations.set('innerHTML', '');

    // check for poor choice
    if (data == null || data.length == 0)
    {
        bad_choice();
        return;
    }

    // set up new configurations
    for (x in data)
    {
        obj = data[x];
        configurations.append('<div class="configuration">' +
                              '<span class="cfg name">' + obj.name + '</span>' +
                              '<span class="cfg description">' + obj.description + '</span>' +
                              '<span class="cfg products">' + obj.products + '</span>' +
                              '<span class="cfg ports">' + obj.ports + '</span>' +
                              '<span class="cfg speed">' + obj.speed + '</span>' +
                              '<span class="cfg price">' + price_print(obj.price) + '</span>' +
                              '<a href="#" onclick="return choose_config(\'' + obj.id + '\')" ' +
                              'class="button"><span>Select</span></a>' +
                              '</div>');
    }

    // done
    show_page('pick');
    dirty_options = false;
    dirty_config = true;
}

set_config = function(id)
{
    var input = YY.Node.one('input#config_id');

    if (input != null)
    {
        input.set('value', id);
    }
    else
    {
        var configurations = YY.Node.one('div#configurations');
        configurations.append('<input type="hidden" id="config_id" name="config_id" value="' + id + '"></input>');
    }

    dirty_config = true;
}

choose_config = function(id)
{
    set_config(id);
    select_page('customize');
}

// functions for products
make_product = function(name, data)
{
    var div = '<div class="drag product">';
    div += '<input type="hidden" name="configured[]" value="' + data + '"></input>';
    div += name + '</div>';
    return div;
}

products_complete = function(Y, data)
{
    var products = Y.Node.one('div#customize div.products');
    var rack1 = Y.Node.one('div#rack1');
    var prod_data, cfg_data, x, i, obj, div;

    // clear everything first
    products.set('innerHTML', '');

    Y.each($('div.drop'), function(v, k) {
        v.innerHTML = '';
    });

    // get data
    prod_data = data.products;
    cfg_data = data.config;
    current_product_list = prod_data;

    // check for bad choice
    if (prod_data.length == 0)
    {
        bad_choice();
        return;
    }

    // set up new products
    for (x in prod_data)
    {
        obj = prod_data[x];
        div = make_product(obj['Name'], obj['Product2'].ProductCode);
        products.append(div);
        make_draggable(Y, products.get('lastChild'));
    }

    // set up initial rack
    for (x in cfg_data.config)
    {
        obj = cfg_data.config[x];

        for (i = 0; i < obj['num']; i++)
        {
            div = make_product(obj['name'], obj['code']);
            rack1.append(div);
            make_draggable(Y, rack1.get('lastChild'));
        }
    }

    // update info
    update_info();

    // clear name
    $('input#save_name').val('');

    // done
    dirty_options = ($('div#configurations > *').size() == 0);
    dirty_config = false;
    visited_customize = true;
    show_page('customize');
}


// stats functions
price_print = function(amount)
{
    var ret = '' + Math.floor(amount);
    var i, c, m, x, currency;
    var cur = '$';
    var sep = ',';
    var dec = '.';

    // localize
    currency = $('#currency').val();

    switch (currency)
    {
    case 'EUR':
        cur = '&euro;';
        sep = '.';
        dec = ',';
        break;
    case 'GBP':
        cur = '&pound;';
        break;
    case 'JPY':
        cur = '&yen;';
        break;
    default:
        break;
    }

    // how many thousands separators?
    c = Math.floor((ret.length - 1) / 3);
    m = (ret.length % 3);
    if (m == 0) m = 3;

    for (i=c; i>0; i--)
    {
        x = m + 3 * (i - 1);
        ret = ret.substr(0, x) + sep + ret.substr(x);
    }

    // add currency symbol
    ret = cur + ret;

    // add decimals
    // var cents = Math.floor(amount*100) % 100;
    // ret = ret + dec + (cents < 10 ? '0' : '') + cents;

    // done
    return ret;
}

update_stats = function()
{
    var id, x, code, product;

    var stats = {
        tb : 0,
        min_tb : 1000000,
        max_tb : 0,
        ports : 0,
        speed : 0,
        price : 0,
        price_per_tb : 0
    }

    // compute stats from products on racks
    $('div#racks div.rack div.product').each(function (i) {
        code = $('input', this).val();

        for (x in current_product_list)
        {
            product = current_product_list[x];

            if (product.Product2.ProductCode == code)
            {
                stats.tb += product.Product2.Usable_TB__c;
                stats.ports += product.Product2.Ports__c;
                stats.price += product.UnitPrice;
                stats.speed += product.Product2.Throughput__c;

                if (product.Product2.Usable_TB__c > stats.max_tb)
                {
                    stats.max_tb = product.Product2.Usable_TB__c;
                }

                if (product.Product2.Usable_TB__c < stats.min_tb)
                {
                    stats.min_tb = product.Product2.Usable_TB__c;
                }
                break;
            }
        }
    });

    // compute stats
    if (stats.tb > 0) stats.price_per_tb = stats.price / stats.tb;
    if (stats.tb == 0) stats.min_tb = 0;

    // format stats
    stats.tb = Math.round(10 * stats.tb)/10;
    stats.ports = Math.round(stats.ports);
    stats.price = price_print(stats.price);
    stats.price_per_tb = price_print(stats.price_per_tb);
    stats.speed = stats.speed + ' MBps';

    // set stats spans
    $('div#stats span').each(function (i) {
        id = this.id;
        if (id == null) return;
        id = id.substr(6);

        if (stats[id] != null)
        {
            this.innerHTML = stats[id];
        }
    });

    // done
    return stats;
}

update_warnings = function(stats)
{
    var div = YY.Node.one('div#warnings');
    var id, q, x, len, html, code, product, warnings = [];
    var count = 0, has = { };
    var max_tb, total_size;

    // init
    div.set('innerHTML', '');
    valid_combo = true;

    // compute info about products on racks
    q = $('div#racks div.rack div.product');

    q.each(function (i) {
        code = $('input', this).val();
        has[code] = true;
        count++;
    });

    len = Object.size(has);

    // max tb
    if (stats.max_tb > (stats.tb / 2))
    {
        warnings.push('No single machine may have more than half of the usable TB in a cluster');
        valid_combo = false;
    }

    // too few products?
    if (count < 3)
    {
        warnings.push('Each cluster must have at least three machines');
        valid_combo = false;
    }

    // too many products?
    if (count > 20)
    {
        var dense = 0;

        for (x in current_product_list)
        {
            product = current_product_list[x];

            if (product.Product2.Usable_TB__c > dense)
            {
                dense = product.Product2.Usable_TB__c;
            }
        }

        if (stats.min_tb != dense)
        {
            warnings.push('Scale Computing recommends using fewer machines with more usable TB per machine');
        }
    }

    // multiple products?
    if (len > 1)
    {
        //warnings.push('Scale Computing recommends using the same type of machine for the entire cluster');
    }

    // valid cluster?
    if (!valid_combo)
    {
        warnings.push('The current configuration cannot be saved or submitted for a quote');
    }

    $('#save_form').css('display', (valid_combo ? 'block' : 'none'));

    // update warnings
    if (warnings.length != 0)
    {
        html = '<p><ul>';

        for (x in warnings)
        {
            html += '<li>';
            html += warnings[x];
            html += '</li>';
        }

        html += '</ul></p>';
        div.set('innerHTML', html);
    }
}

update_info = function()
{
    var stats;
    stats = update_stats();
    update_warnings(stats);
}


// functions for save/load
save_complete = function(Y, data)
{
    if (data != null && data.redirect != undefined)
    {
        window.location.assign(data.redirect);
        return;
    }

    dirty_saved = true;

    if (data != null && data.id != undefined && submit_id == 'save_id')
    {
        submit_id = data.id;
        select_page('submitting');
    }
    else
    {
        submit_id = null;
        select_page('saved_list');
    }
}

load_complete = function(Y, data)
{
    options_complete(Y, data.options);
    set_options(Y, data.options, data.opts);
    configurations_complete(Y, data.configurations);
    set_config(data.opts.config_id);
    products_complete(Y, data);
    Y.one('input#save_name').set('value', data.opts.save_name);
    show_page('customize');
}

list_complete = function(Y, data)
{
    var parent = Y.one('ul#saved');
    var x, id, obj, elem, date = new Date();

    // clear list
    parent.set('innerHTML', '');

    // create list
    for (x in data)
    {
        obj = data[x];
        id = 'item_' + x + '_';
        elem = '<li class="saved"><div class="' + ((x%2) == 0 ? 'even' : 'odd') + '">';

        elem += '<label for="' + id + '_name" class="saved"><span>Name</span></label>';
        elem += '<span id="' + id + '_name" class="saved name">' + obj.Name + '</span><br/>';

        elem += '<label for="' + id + '_tb" class="saved"><span>Usable TB Request</span></label>';
        elem += '<span id="' + id + '_tb" class="saved tb">' + obj.Usable_TB__c + '</span><br/>';

        elem += '<label for="' + id + '_chassis_type" class="saved"><span>Hardware Platform</span></label>';
        elem += '<span id="' + id + '_chassis_type" class="saved chassis_type">';
        elem += obj.Chassis_Type__c + '</span><br/>';

        elem += '<label for="' + id + '_hard_drive_type" class="saved"><span>Hard Drive Type</span></label>';
        elem += '<span id="' + id + '_hard_drive_type" class="saved hard_drive_type">';
        elem += obj.Hard_Drive_Type__c + '</span><br/>';

        elem += '<label for="' + id + '_submitted" class="saved"><span>Requested a Quote?</span></label>';
        elem += '<span id="' + id + '_submitted" class="saved submitted">';
        elem += (obj.Submitted_For_Quote__c ? 'Yes' : 'No') + '</span><br/>';

        date.setTime(obj.LastModifiedDate * 1000);
        elem += '<label for="' + id + '_date" class="saved"><span>Saved At</span></label>';
        elem += '<span id="' + id + '_date" class="saved date">' + date.toLocaleString() + '</span><br/>';

        elem += '<a href="#" class="button" onclick="return select_load_id(\'' + obj.Id;
        elem += '\')"><span>Load</span></a>';

        elem += '<a href="#" class="button" onclick="return select_delete_id(\'' + obj.Id;
        elem += '\')"><span>Delete</span></a>';

        if (!obj.Submitted_For_Quote__c)
        {
            elem += '<a href="#" class="button" onclick="return submit_for_quote(\'' + obj.Id;
            elem += '\')"><span>Request a Quote</span></a>';
        }

        elem += '</div></li>';

        parent.append(elem);
    }

    // empty list?
    if (data.length == 0)
    {
        parent.append('<li><p>There are no items to display</p></li>');
    }

    // show page
    delete_id = null;
    dirty_saved = false;
    show_page('saved_list');
}

select_load_id = function(id)
{
    load_id = id;
    select_page('loading');
}

select_delete_id = function(id)
{
    dirty_saved = true;
    reload = true;
    delete_id = id;
    select_page('saved_list');
}


// submission functions
submit_complete = function(Y, data)
{
    dirty_saved = true;
    reload = true;
    submit_id = null;
    select_page('saved_list');
}

save_and_submit = function()
{
    submit_id = 'save_id';
    select_page('saving');
}

submit_for_quote = function(id)
{
    submit_id = id;
    select_page('submitting');
}


// page selection functions
show_page = function(x)
{
    // show page
    $('#configurator > div').css('display', 'none');
    $('#' + x).css('display', 'block');

    // set classes on li
    $('#subnav > li > a').removeClass('selected');
    $('#subnav > li.' + x + ' > a').addClass('selected');
    return false;
}

select_page = function(x)
{
    // have YY?
    if (YY == null) return false;

    // set up io config
    var cfg = {
        method: 'POST',
        form: {
            id: 'form',
            useDisabled: true
        }
    };

    // determine active page
    var visible = $('#configurator > div:visible').attr('id');

    // page change?
    if (x != visible || reload)
    {
        // reset reload flag
        reload = false;

        // validate options?
        if (visible == 'options' && !validate_options()) return bad_choice();

        // restore previous configuration?
        if (x == 'restore')
        {
            show_page('loading');
            io_ids.restore_complete = YY.io('/products/configure-json/?what=restore', cfg).id;
            return false;
        }

        // changing to options page?
        if (x == 'options')
        {
            // need to load options?
            if ($('#option_groups > div').size() == 0)
            {
                show_page('loading');
                io_ids.options_complete = YY.io('/products/configure-json/?what=options', cfg).id;
                return false;
            }
        }

        // changing to pick page?
        if (x == 'pick' && dirty_options)
        {
            show_page('loading');
            io_ids.configurations_complete = YY.io('/products/configure-json/?what=configurations', cfg).id;
            return false;
        }

        // changing to customize page?
        if (x == 'customize' && (dirty_options || dirty_config))
        {
            show_page('loading');
            io_ids.products_complete = YY.io('/products/configure-json/?what=products', cfg).id;
            return false;
        }

        // changing to save page?
        if (x == 'saving')
        {
            show_page('saving');
            io_ids.save_complete = YY.io('/products/configure-json/?what=save', cfg).id;
            return false;
        }

        // changing to save page?
        if (x == 'submitting')
        {
            show_page('submitting');
            io_ids.submit_complete = YY.io('/products/configure-json/?what=submit&id=' + submit_id, cfg).id;
            return false;
        }

        // changing to load page?
        if (x == 'loading')
        {
            show_page('loading');
            io_ids.load_complete = YY.io('/products/configure-json/?what=load&id=' + load_id, cfg).id;
            return false;
        }

        // changing to load page?
        if (x == 'saved_list' && dirty_saved)
        {
            show_page('loading');
            io_ids.list_complete = YY.io('/products/configure-json/?what=list&del=' + delete_id, cfg).id;
            return false;
        }

        // leaving page?
        if (x == 'leaving')
        {
            // save in session only if we have gotten all the way to customize
            if (visited_customize)
            {
                show_page('saving');
                io_ids.save_complete = YY.io('/products/configure-json/?what=save&temp=1', cfg).id;
            }

            // allow leaving
            return true;
        }
    }

    // if no loading needed, just switch pages
    return show_page(x);
}


// page initialization
YUI().use('node', 'selector-css3', 'json-parse', 'io-base', 'io-form',
          'dd-drag', 'dd-drop', 'dd-proxy', 'dd-scroll',
function(Y)
{
    YY = Y;

    var drag_start = function(e) {
        var p = this.get('dragNode'),
        n = this.get('node');
        n.setStyle('opacity', .25);
        if (!this._playerStart) {
            this._playerStart = this.nodeXY;
        }
        p.set('innerHTML', n.get('innerHTML'));
        p.setStyles({
            backgroundImage: n.getStyle('backgroundImage'),
            backgroundColor: n.getStyle('backgroundColor'),
            color: n.getStyle('color'),
            opacity: .75,
            textAlign: n.getStyle('textAlign'),
            fontWeight: n.getStyle('fontWeight'),
            fontFamily: n.getStyle('fontFamily'),
            fontSize: n.getStyle('fontSize'),
            marginTop: n.getStyle('marginTop'),
            marginBottom: n.getStyle('marginBottom'),
            marginLeft: n.getStyle('marginLeft'),
            marginRight: n.getStyle('marginRight'),
            paddingTop: n.getStyle('paddingTop'),
            paddingBottom: n.getStyle('paddingBottom'),
            paddingLeft: n.getStyle('paddingLeft'),
            paddingRight: n.getStyle('paddingRight'),
            width: n.getStyle('width'),
            height: n.getStyle('height')
        });
    }

    var drag_end = function(e) {
        var n = this.get('node');
        n.setStyle('opacity', '1');
    }
    
    var drop_hit = function(e) {
        var drag = e.drag;
        var drop = e.drop;
        var from = drag.get('node');
        var to = drop.get('node');

        if (to.hasClass('products'))
        {
            // delete drag node?
            if (from.get('parentNode') != to)
            {
                from.remove();
                update_info();
            }
        }
        else
        {
            // did the node move?
            if (from.get('parentNode') != to)
            {
                // copy drag node to drop target
                var clone = from.cloneNode();
                clone.set('innerHTML', from.get('innerHTML'));
                clone.setStyles({
                    opacity: 1
                });
                to.appendChild(clone);
                make_draggable(Y, clone);

                // delete drag node?
                if (!from.get('parentNode').hasClass('products')) from.remove();

                // update info
                update_info();
            }
        }
    }

    make_draggable = function(Y, elem)
    {
        var drag = new Y.DD.Drag({
            node: elem,
            dragMode: 'point'
        }).plug(Y.Plugin.DDProxy, {
            moveOnEnd: false  // we'll manually move it during onDrop()
        }).plug(Y.Plugin.DDWinScroll);
        drag.on('drag:start', drag_start);
        drag.on('drag:end', drag_end);
    }

    make_droppable = function(Y, elem)
    {
        var drop = new Y.DD.Drop({
            node: elem
        });
        drop.addAttr('Y', { value: Y }, false);
        drop.on('drop:hit', drop_hit);
    }

    // code that runs on page ready
    Y.each($('div.drop'), function(v, k) {
        make_droppable(Y, v);
    });

    Y.each($('div.drag'), function(v, k) {
        //make_draggable(Y, v);
    });

    json_complete = function(id, o, args)
    {
        var data, method, debug;
        debug = false;

        // parse data
        try
        {
            data = Y.JSON.parse(o.responseText);
        }
        catch (e)
        {
            errs = [ { message : 'Received invalid data from the server' } ];
            if (debug) errs.push({ message : 'Response: ' + o.responseText });
            data = { errors : errs };
        }

        // check for errors
        if (data != null && data.errors != undefined)
        {
            var x, err, div, html;

            // set errors
            div = Y.one("#errors");
            html = '<ul>';

            for (x in data.errors)
            {
                err = data.errors[x];

                if (err.message != undefined)
                {
                    html += '<li>' + err.message + '</li>';
                }
                else
                {
                    html += '<li>' + err + '</li>';
                }
            }

            html += '</ul>';
            div.set('innerHTML', html);

            // reset state
            load_id = delete_id = null;
            dirty_options = dirty_config = dirty_saved = true;

            // show errors
            show_page('errors');
            return;
        }

        // multiplex method to call
        for (method in io_ids)
        {
            if (method == null) continue;

            if (io_ids[method] == id)
            {
                this[method](Y, data);
            }
        }
    }

    // set onunload handler
    onUnload = function (e)
    {
        return select_page('leaving');
    }

    window.onunload = onUnload;

    // inital action
    Y.on('io:complete', json_complete, this, []);
    select_page('restore');
});

