/**
 * Javascript library for anvi'o interactive interface
 *
 *  Author: Özcan Esen <ozcanesen@gmail.com>
 *  Credits: A. Murat Eren, Doğan Can Kilment
 *  Copyright 2015, The anvio Project
 *
 * This file is part of anvi'o (<https://github.com/meren/anvio>).
 *
 * Anvi'o is a free software. You can redistribute this program
 * and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 *
 * You should have received a copy of the GNU General Public License
 * along with anvi'o. If not, see <http://opensource.org/licenses/GPL-3.0>.
 *
 * @license GPL-3.0+ <http://opensource.org/licenses/GPL-3.0>
 */

//--------------------------------------------------------------------------------------------------
//  Globals
//--------------------------------------------------------------------------------------------------
var VERSION = '0.2.0';

var VIEWER_WIDTH;
var VIEWER_HEIGHT;
var dragging = false;

var zoomBox = {};
var drawing_zoom = false;

var LINE_COLOR='#888888';

var scale = 0;

var id_to_node_map = new Array();
var label_to_node_map = {};
var order_to_node_map = {};
var leaf_count;

var angle_per_leaf;
var height_per_leaf;
var tree_type;
var margin;
var order_counter;

var total_radius = 0;
var layer_boundaries;

var SELECTED = new Array();
var newick;

var metadata;
var contig_lengths;
var parameter_count;

var bin_counter = 0; // for id
var bin_count = 0;

var layer_types;

var categorical_data_colors = {};
var stack_bar_colors = {};

var context_menu_target_id = 0;

var metadata_title = {};
var metadata_dict;
var empty_tooltip = "";

var last_settings;

var search_column;
var search_results = [];
var highlighted_splits = [];

var views = {};
var layers = {};
var current_view = '';
var layer_order;

var completeness_dict = {};

var sort_column;
var sort_order;

var bin_prefix;

var current_state_name = "";

var unique_session_id;
var ping_timer;
//---------------------------------------------------------
//  Init
//---------------------------------------------------------

$(document).ready(function() {
    waitingDialog.show('Loading the interface ...', {dialogSize: 'sm'});

    $('#tree_type').change(function() {
        if ($('#tree_type').val()=='circlephylogram') 
        {
            $('.phylogram_settings').hide();
            $('.circlephylogram_settings').show();
        }
        else
        {
            $('.phylogram_settings').show();
            $('.circlephylogram_settings').hide();
        }
    });

    var timestamp = new Date().getTime(); 

    $.when(    
        $.ajax({
            type: 'GET',
            cache: false,
            url: '/data/title?timestamp=' + timestamp,
        }),
        $.ajax({
            type: 'GET',
            cache: false,
            url: '/data/clusterings?timestamp=' + timestamp,
        }),
        $.ajax({
            type: 'GET',
            cache: false,
            url: '/data/views?timestamp=' + timestamp,
        }),
        $.ajax({
            type: 'GET',
            cache: false,
            url: '/data/contig_lengths?timestamp=' + timestamp,
        }),
        $.ajax({
            type: 'GET',
            cache: false,
            url: '/data/default_view?timestamp=' + timestamp,
        }),
        $.ajax({
            type: 'GET',
            cache: false,
            url: '/data/mode?timestamp=' + timestamp,
        }),
        $.ajax({
            type: 'GET',
            cache: false,
            url: '/data/read_only?timestamp=' + timestamp,
        }),
        $.ajax({
            type: 'GET',
            cache: false,
            url: '/data/bin_prefix?timestamp=' + timestamp,
        }),
        $.ajax({
            type: 'GET',
            cache: false,
            url: '/data/session_id?timestamp=' + timestamp,
        }))
    .then(
        function (titleResponse, clusteringsResponse, viewsResponse, contigLengthsResponse, defaultViewResponse, modeResponse, readOnlyResponse, prefixResponse, sessionIdResponse) 
        {
            unique_session_id = sessionIdResponse[0];
            ping_timer = setInterval(checkBackgroundProcess, 5000);

            if (modeResponse[0] == 'refine')
            {
                $('.full-mode').hide();
                $('.refine-mode').show();

                $('#fixed-navbar-div').css('background-color', '#F3A2AD');
            }

            if (readOnlyResponse[0] == true)
            {
                alert('It seems that this is a read-only instance, therefore the database-writing functions will be inaccessible.');

                $('[disabled-in-read-only=true]').addClass('disabled').prop('disabled', true);
            }

            bin_prefix = prefixResponse[0];

            document.title = titleResponse[0];
            contig_lengths = eval(contigLengthsResponse[0]);

            /*
                Get metadata and create layers table
            */

            metadata = eval(defaultViewResponse[0]);
            parameter_count = metadata[0].length;

            // since we are painting parent layers odd-even, 
            // we should remove single parents (single means no parent)
            removeSingleParents(); // in utils.js

            layer_order = Array.apply(null, Array(parameter_count-1)).map(function (_, i) {return i+1;}); // range(1, parameter_count)
            layer_types = {};

            //  Edit Attributes For Multiple Layers

            $('#select_layer').on('change', function() {
                var layer_name = $('#select_layer').val();

                // clean prior selections
                $('.layer_selectors').prop('checked', false);

                if(layer_name){ // if layer_name is empty, there is nothing to select, move on.
                    $('.titles').each(
                        function(){
                            if (this.title.indexOf(layer_name) > -1)
                            {
                                $('#select_this_' + getNumericPart(this.id)).prop('checked','checked');
                            }
                        }
                    );
                }
            });

            $('#picker_multiple').colpick({
                layout: 'hex',
                submit: 0,
                colorScheme: 'light',
                onChange: function(hsb, hex, rgb, el, bySetColor) {
                    $(el).css('background-color', '#' + hex);
                    $(el).attr('color', '#' + hex);
                    if (!bySetColor) $(el).val(hex);

                    $('.layer_selectors:checked').each(
                        function(){
                            $('#' + 'picker' + getNumericPart(this.id)).attr('color', '#' + hex);
                            $('#' + 'picker' + getNumericPart(this.id)).css('background-color', '#' + hex);
                        }
                    );
                }
            }).keyup(function() {
                    $(this).colpickSetColor(this.value);
            });

            $('#picker_start_multiple').colpick({
                layout: 'hex',
                submit: 0,
                colorScheme: 'light',
                onChange: function(hsb, hex, rgb, el, bySetColor) {
                    $(el).css('background-color', '#' + hex);
                    $(el).attr('color', '#' + hex);
                    if (!bySetColor) $(el).val(hex);

                    $('.layer_selectors:checked').each(
                        function(){
                            $('#' + 'picker_start' + getNumericPart(this.id)).attr('color', '#' + hex);
                            $('#' + 'picker_start' + getNumericPart(this.id)).css('background-color', '#' + hex);
                        }
                    );
                }
            }).keyup(function() {
                    $(this).colpickSetColor(this.value);
            });

            $('#min_multiple').on('change', function(){
                var intend_value = $('#min_multiple').val();
                $('.layer_selectors:checked').each(
                    function(){
                        $('#' + 'min' + getNumericPart(this.id)).attr('value', intend_value);
                    }
                );
            });

            $('#max_multiple').on('change', function(){
                var intend_value = $('#max_multiple').val();
                $('.layer_selectors:checked').each(
                    function(){
                        $('#' + 'max' + getNumericPart(this.id)).attr('value', intend_value);
                    }
                );
            });

            $('#height_multiple').on('change', function(){
                var intend_value = $('#height_multiple').val();
                $('.layer_selectors:checked').each(
                    function(){
                        $('#' + 'height' + getNumericPart(this.id)).attr('value', intend_value);
                    }
                );
            });

            $('#margin_multiple').on('change', function(){
                var intend_value = $('#margin_multiple').val();
                $('.layer_selectors:checked').each(
                    function(){
                        $('#' + 'margin' + getNumericPart(this.id)).attr('value', intend_value);
                    }
                );
            });

            $('#type_multiple').on('change', function(){
                var intend_value = $('#type_multiple').val();
                $('.layer_selectors:checked').each(
                    function(){
                        $('#type' + getNumericPart(this.id)).val(intend_value).trigger('change');
                    }
                );
            });

            $('#normalization_multiple').on('change', function(){
                var intend_value = $('#normalization_multiple option:selected').val();

                $('.layer_selectors:checked').each(
                    function(){
                        $('#normalization' + getNumericPart(this.id)).val(intend_value).trigger('change');
                    }
                );
            });

            $('#select_all').on('click', function() {
                if(this.checked) {
                    $('.layer_selectors').each(
                        function() {
                            this.checked = true;
                        }
                    );

                }else{
                    $('.layer_selectors').each(
                        function() {
                            this.checked = false;
                        }
                    );
                }
            });


            /* 
            //  Clusterings
            */
            var default_tree = clusteringsResponse[0][0];
            var available_trees = clusteringsResponse[0][1];
            var available_trees_combo = getComboBoxContent(default_tree, available_trees);
            var first_run = true;

            $('#trees_container').append(available_trees_combo);

            $('#trees_container').change(function() {

                $('#trees_container').prop('disabled', true);
                $('#btn_draw_tree').prop('disabled', true);

                waitingDialog.show('Requesting the tree data ...', {dialogSize: 'sm'});

                $.ajax({
                    type: 'GET',
                    cache: false,
                    url: '/tree/' + $('#trees_container').val() + '?timestamp=' + new Date().getTime(),
                    success: function(data) {
                        newick = data;
                        $('#trees_container').attr('disabled', false);
                        $('#btn_draw_tree').attr('disabled', false); 

                        if (!first_run){
                            waitingDialog.hide();
                        }
                        first_run = false;
                    }
                });
            });

            $('#trees_container').trigger('change'); // load default newick tree

            /* 
            //  Views
            */
            var default_view = viewsResponse[0][0];
            var available_views = viewsResponse[0][1];
            var available_views_combo = getComboBoxContent(default_view, available_views);

            $('#views_container').append(available_views_combo);

            $('#views_container').change(function() {

                $('#views_container').prop('disabled', false);
                $('#btn_draw_tree').prop('disabled', true);

                waitingDialog.show('Requesting view data from the server ...', {dialogSize: 'sm'});

                $.ajax({
                    type: 'GET',
                    cache: false,
                    url: '/data/view/' + $('#views_container').val() + '?timestamp=' + new Date().getTime(),
                    success: function(data) {
                        metadata = eval(data);
                        removeSingleParents(); // in utils.js
                        
                        $('#views_container').attr('disabled', false);
                        $('#btn_draw_tree').attr('disabled', false);

                        if (current_view != '') {
                            // backup current layer order and layers table to global views object
                            syncViews();
                        }
                        current_view = $('#views_container').val();

                        $("#tbody_layers").empty();

                        buildLayersTable(layer_order, views[current_view]);

                        // make layers table sortable
                        $("#tbody_layers").sortable({helper: fixHelperModified, handle: '.drag-icon', items: "> tr:not(:first)"}).disableSelection(); 

                        
                    }
                });
            });

            $('#views_container').trigger('change'); // load default view


            /*
            //  Add bins
            */
            newBin();


            // add metadata columns to search window
            for (var i=0; i < metadata[0].length; i++)
            {
                $('#searchLayerList').append(new Option(metadata[0][i],i));
            }
        } // response callback
    ).fail(function() {
        alert('One or more ajax request has failed, See console logs for details.');
        console.log(arguments);
    }); // promise

    // initialize colorpicker for search result highlight color.
    $('#picker_highlight').colpick({
        layout: 'hex',
        submit: 0,
        colorScheme: 'dark',
        onChange: function(hsb, hex, rgb, el, bySetColor) {
            $(el).css('background-color', '#' + hex);
            $(el).attr('color', '#' + hex);

            if (!bySetColor) $(el).val(hex);
        }
    }).keyup(function() {
        $(this).colpickSetColor(this.value);
    });

    $('#grid_color').colpick({
        layout: 'hex',
        submit: 0,
        colorScheme: 'dark',
        onChange: function(hsb, hex, rgb, el, bySetColor) {
            $(el).css('background-color', '#' + hex);
            $(el).attr('color', '#' + hex);

            if (!bySetColor) $(el).val(hex);
        },
        onHide: function() {
            redrawBins();
        }
    }).keyup(function() {
        $(this).colpickSetColor(this.value);
    });

    document.body.addEventListener('click', function() {
        $('#control_contextmenu').hide();
    }, false);

    $("li[role='presentation']").click(function (e) {
        if ($(this).hasClass('disabled')) {
            e.preventDefault();
            return false;
        }  
    });

}); // document ready

function syncViews() {
    views[current_view] = {};
    layer_order = new Array();
    $('#tbody_layers tr').each(
        function(index, layer) {
            var layer_id = $(layer).find('.input-height')[0].id.replace('height', '');
            layers[layer_id] = {};
            layer_order.push(layer_id);

            views[current_view][layer_id] = {};
            views[current_view][layer_id]["normalization"] = $(layer).find('.normalization').val();
            views[current_view][layer_id]["min"] = {'value': $(layer).find('.input-min').val(), 'disabled': $(layer).find('.input-min').is(':disabled') }; 
            views[current_view][layer_id]["max"] = {'value': $(layer).find('.input-max').val(), 'disabled': $(layer).find('.input-max').is(':disabled') };

            layers[layer_id]["color"] = $(layer).find('.colorpicker:last').attr('color');
            layers[layer_id]["height"] = $(layer).find('.input-height').val();
            layers[layer_id]["margin"] = $(layer).find('.input-margin').val();
            layers[layer_id]["type"] = $(layer).find('.type').val();
            layers[layer_id]["color-start"] = $(layer).find('.colorpicker:first').attr('color');


        }
    );    
}


function getComboBoxContent(default_item, available_items){
    var combo = '';
    var combo_item = '<option value="{val}"{sel}>{text}</option>';

    $.each(available_items, function(index, value) {
        if(index == default_item)
        {
            combo += combo_item
                        .replace('{val}', index)
                        .replace('{sel}', ' selected')
                        .replace('{text}', index);
        }
        else
        {
            combo += combo_item
                        .replace('{val}', index)
                        .replace('{sel}', '')
                        .replace('{text}', index);
        }
    });

    return combo;
}


// get numeric part from id
function getNumericPart(id){
    var $num = id.replace(/[^\d]+/, '');

    return $num;
}

function buildLayersTable(order, settings)
{
    for (var i = 0; i < order.length; i++) 
    {
        // common layer variables
        var layer_id = order[i];
        var layer_name = metadata[0][layer_id];
        var short_name = (layer_name.length > 10) ? layer_name.slice(0,10) + "..." : layer_name;

        var hasViewSettings = false;
        if (typeof settings !== 'undefined') {
            var view_settings = settings[layer_id];
            var hasViewSettings = true;
        }

        var hasLayerSettings = false;
        if (typeof layers[layer_id] !== 'undefined')
        {
            var layer_settings = layers[layer_id];
            hasLayerSettings = true;
        }

        //
        //  parent layer
        //
        if (layer_name == '__parent__')
        {
           layer_types[layer_id] = 0;

            if (hasLayerSettings) 
            {
                var height = layer_settings['height'];
                var margin = layer_settings['margin'];
            }
            else 
            {
                var height = '50';
                var margin = '15';
            }

            var template = '<tr>' +
                '<td><img src="images/drag.gif" /></td>' +
                '<td>Parent</td>' +
                '<td>n/a</td>' +
                '<td>n/a</td>' +
                '<td>n/a</td>' +
                '<td><input class="input-height" type="text" size="3" id="height{id}" value="{height}"></input></td>' +
                '<td class="column-margin"><input class="input-margin" type="text" size="3" id="margin{id}" value="{margin}"></input></td>' +
                '<td>n/a</td>' +
                '<td>n/a</td>' +
                '<td><input type="checkbox" id="select_this_{id}" class="layer_selectors"></input></td>' +
                '</tr>';

            template = template.replace(new RegExp('{id}', 'g'), layer_id)
                               .replace(new RegExp('{height}', 'g'), height)
                               .replace(new RegExp('{margin}', 'g'), margin);

            $('#tbody_layers').prepend(template);
        }
        //
        // stack bar layer
        //
        else if (layer_name.indexOf(';') > -1) 
        {
            layer_types[layer_id] = 1;

            if (hasLayerSettings)
            {
                var height = layer_settings['height'];
                var margin = layer_settings['margin'];
            }
            else
            {
                var height = '30';
                var margin = '15';

                // pick random color for stack bar items
                if (!(layer_id in stack_bar_colors))
                {
                    stack_bar_colors[layer_id] = new Array();
                    for (var j=0; j < layer_name.split(";").length; j++)
                    {
                        stack_bar_colors[layer_id].push(randomColor());
                    } 
                }             
            }

            if (hasViewSettings)
            {
                var norm = view_settings['normalization'];
            }
            else
            {
                var norm = 'log';
            }

            var template = '<tr>' +
                '<td><img class="drag-icon" src="images/drag.gif" /></td>' +
                '<td title="{name}" class="titles" id="title{id}">{short-name}</td>' +
                '<td>n/a</td>' +
                '<td>n/a</td>' +
                '<td>' +
                '    <select id="normalization{id}" onChange="clearMinMax(this);" class="normalization">' +
                '        <option value="none"{option-none}>none</option>' +
                '        <option value="sqrt"{option-sqrt}>sqrt</option>' +
                '        <option value="log"{option-log}>log</option>' +
                '    </select>' +
                '</td>' +
                '<td><input class="input-height" type="text" size="3" id="height{id}" value="{height}"></input></td>' +
                '<td class="column-margin"><input class="input-margin" type="text" size="3" id="margin{id}" value="{margin}"></input></td>' +
                '<td>n/a</td>' +
                '<td>n/a</td>' +
                '<td><input type="checkbox" id="select_this_{id}" class="layer_selectors"></input></td>' +
                '</tr>';

            template = template.replace(new RegExp('{id}', 'g'), layer_id)
                               .replace(new RegExp('{name}', 'g'), layer_name)
                               .replace(new RegExp('{short-name}', 'g'), short_name)
                               .replace(new RegExp('{option-' + norm + '}', 'g'), ' selected')
                               .replace(new RegExp('{option-([a-z]*)}', 'g'), '')
                               .replace(new RegExp('{height}', 'g'), height)
                               .replace(new RegExp('{margin}', 'g'), margin);

            $('#tbody_layers').append(template);
        }
        //
        // categorical layer
        //
        else if (metadata[1][layer_id] === null || !isNumber(metadata[1][layer_id]))
        { 
            layer_types[layer_id] = 2;

            if (hasLayerSettings)
            {
                var height = layer_settings['height'];
                var margin = layer_settings['margin'];
            }
            else
            {
                var height = '30';
                var margin = '15';

                if (!(layer_id in categorical_data_colors))
                {
                    categorical_data_colors[layer_id] = {};
                }
            }
            
            var template = '<tr>' +
                '<td><img class="drag-icon" src="images/drag.gif" /></td>' +
                '<td title="{name}" class="titles" id="title{id}">{short-name}</td>' +
                '<td>n/a</td>' +
                '<td>n/a</td>' +
                '<td>n/a</td>' +
                '<td><input class="input-height" type="text" size="3" id="height{id}" value="{height}"></input></td>' +
                '<td class="column-margin"><input class="input-margin" type="text" size="3" id="margin{id}" value="{margin}"></input></td>' +
                '<td>n/a</td>' +
                '<td>n/a</td>' +
                '<td><input type="checkbox" id="select_this_{id}" class="layer_selectors"></input></td>' +
                '</tr>';

            template = template.replace(new RegExp('{id}', 'g'), layer_id)
                               .replace(new RegExp('{name}', 'g'), layer_name)
                               .replace(new RegExp('{short-name}', 'g'), short_name)
                               .replace(new RegExp('{height}', 'g'), height)
                               .replace(new RegExp('{margin}', 'g'), margin);

            $('#tbody_layers').append(template);
        } 
        //
        // numerical layer
        //
        else
        {
            layer_types[layer_id] = 3;

            if (hasViewSettings)
            {
                var norm   = view_settings['normalization'];
                var min    = view_settings['min']['value'];
                var max    = view_settings['max']['value'];
                var min_disabled = view_settings['min']['disabled'];
                var max_disabled = view_settings['max']['disabled'];
            }
            else
            {
                var norm   = getNamedLayerDefaults(layer_name, 'norm', 'log');
                var min    = 0;
                var max    = 0;
                var min_disabled = true;
                var max_disabled = true;
            }

            if (hasLayerSettings)
            {
                var height = layer_settings['height'];
                var color  = layer_settings['color'];
                var margin = layer_settings['margin'];
                var color_start = layer_settings['color-start'];
                var type = layer_settings['type'];
            }
            else
            {
                var height = getNamedLayerDefaults(layer_name, 'height', '180');
                var color  = getNamedLayerDefaults(layer_name, 'color', '#000000');
                var margin = '15';
                var color_start = "#FFFFFF";
                var type = "bar";
            }

            /* Some ad-hoc manipulation of special hmmx_ split hmm layers */ 
            if (layer_name.substring(0, 5) == "hmmx_"){
                var height = '30';
                var norm   = 'none';
                var color  = '#882222';
            }

            /* Some ad-hoc manipulation of special hmms_ single hmm layers */ 
            if (layer_name.substring(0, 5) == "hmms_"){
                var height = '150';
                var norm   = 'sqrt';
                var color  = '#882222';
            }

            var template = '<tr>' +
                '<td><img class="drag-icon" src="images/drag.gif" /></td>' +
                '<td title="{name}" class="titles" id="title{id}">{short-name}</td>' +
                '<td><div id="picker_start{id}" class="colorpicker picker_start" color="{color-start}" style="background-color: {color-start}; {color-start-hide}"></div><div id="picker{id}" class="colorpicker" color="{color}" style="background-color: {color}"></div></td>' +
                '<td style="width: 50px;">' +
                '    <select id="type{id}" style="width: 50px;" class="type" onChange="if(this.value==\'intensity\') {  $(\'#picker_start\' + getNumericPart(this.id)).css(\'visibility\', \'visible\'); } else { $(\'#picker_start\' + getNumericPart(this.id)).css(\'visibility\', \'hidden\'); }  ">' +
                '        <option value="bar"{option-type-bar}>Bar</option>' +
                '        <option value="intensity"{option-type-intensity}>Intensity</option>' +
                '    </select>' +
                '</td>' +
                '<td>' +
                '    <select id="normalization{id}" onChange="clearMinMax(this);" class="normalization">' +
                '        <option value="none"{option-none}>none</option>' +
                '        <option value="sqrt"{option-sqrt}>sqrt</option>' +
                '        <option value="log"{option-log}>log</option>' +
                '    </select>' +
                '</td>' +
                '<td><input class="input-height" type="text" size="3" id="height{id}" value="{height}"></input></td>' +
                '<td class="column-margin"><input class="input-margin" type="text" size="3" id="margin{id}" value="{margin}"></input></td>' +
                '<td><input class="input-min" type="text" size="4" id="min{id}" value="{min}"{min-disabled}></input></td>' +
                '<td><input class="input-max" type="text" size="4" id="max{id}" value="{max}"{min-disabled}></input></td>' +
                '<td><input type="checkbox" id="select_this_{id}" class="layer_selectors"></input></td>' +
                '</tr>';

            template = template.replace(new RegExp('{id}', 'g'), layer_id)
                               .replace(new RegExp('{name}', 'g'), layer_name)
                               .replace(new RegExp('{short-name}', 'g'), short_name)
                               .replace(new RegExp('{option-' + norm + '}', 'g'), ' selected')
                               .replace(new RegExp('{option-([a-z]*)}', 'g'), '')
                               .replace(new RegExp('{option-type-' + type + '}', 'g'), ' selected')
                               .replace(new RegExp('{option-type-([a-z]*)}', 'g'), '')
                               .replace(new RegExp('{color}', 'g'), color)
                               .replace(new RegExp('{color-start}', 'g'), color_start)
                               .replace(new RegExp('{color-start-hide}', 'g'), (type!='intensity') ? '; visibility: hidden;' : '')
                               .replace(new RegExp('{height}', 'g'), height)
                               .replace(new RegExp('{min}', 'g'), min)
                               .replace(new RegExp('{max}', 'g'), max)
                               .replace(new RegExp('{min-disabled}', 'g'), (min_disabled) ? ' disabled': '')
                               .replace(new RegExp('{max-disabled}', 'g'), (max_disabled) ? ' disabled': '')
                               .replace(new RegExp('{margin}', 'g'), margin)


            $('#tbody_layers').append(template);
        }

        if($('#custom_layer_margin').is(':checked'))
        {
            $('.column-margin').show();
        }
        else
        {
            $('.column-margin').hide();
        }

        $('#picker'+ layer_id + ', #picker_start' + layer_id).colpick({
            layout: 'hex',
            submit: 0,
            colorScheme: 'dark',
            onChange: function(hsb, hex, rgb, el, bySetColor) {
                $(el).css('background-color', '#' + hex);
                $(el).attr('color', '#' + hex);

                if (!bySetColor) $(el).val(hex);
            }
        }).keyup(function() {
            $(this).colpickSetColor(this.value);
        });
    }


    waitingDialog.hide();
}

//---------------------------------------------------------
//  ui callbacks
//---------------------------------------------------------
function getLayerName(layer_id)
{
    return metadata[0][layer_id];
}

function getLayerId(layer_name) 
{
    for (var i=0; i < parameter_count; i++)
    {
        if (layer_name == metadata[0][i])
            return i;
    }
    return -1;
}

function serializeSettings(use_layer_names) {

    if (typeof use_layer_names === 'undefined')
        use_layer_names = false;

    var state = {};
    state['version'] = VERSION;
    state['tree-type'] = $('#tree_type').val();
    state['order-by'] = $('#trees_container').val();
    state['current-view'] = $('#views_container').val();
    state['angle-min'] = $('#angle-min').val();
    state['angle-max'] = $('#angle-max').val();
    state['tree-radius'] = $('#tree-radius').val();
    state['tree-height'] = $('#tree_height').val();
    state['tree-width'] = $('#tree_width').val();
    state['layer-margin'] = $('#layer-margin').val();
    state['outer-ring-height'] = $('#outer-ring-height').val();
    state['edge-normalization'] = $('#edge_length_normalization').is(':checked');
    state['custom-layer-margin'] = $('#custom_layer_margin').is(':checked');
    state['show-grid-for-bins'] = $('#show_grid_for_bins').is(':checked');
    state['grid-color'] = $('#grid_color').attr('color');
    state['grid-width'] = $('#grid_width').val();


    // sync views object and layers table
    syncViews();

    if (use_layer_names)
    {
        // save state file with layer name instead of id.

        state['layer-order'] = [];
        for (var i=0; i < layer_order.length; i++)
        {
            state['layer-order'].push(getLayerName(layer_order[i]));
        }

        state['categorical_data_colors'] = {};
        for (var key in categorical_data_colors)
        {
            state['categorical_data_colors'][getLayerName(key)] = categorical_data_colors[key];
        }

        state['stack_bar_colors'] = {};
        for (var key in stack_bar_colors)
        {
            state['stack_bar_colors'][getLayerName(key)] = stack_bar_colors[key];
        }

        state['layers'] = {};
        for (var key in layers)
        {
            state['layers'][getLayerName(key)] = layers[key];
        }

        state['views'] = {};
        for (var view_key in views)
        {
            state['views'][view_key] = {};
            for (var key in views[view_key])
            {
                state['views'][view_key][getLayerName(key)] = views[view_key][key];
            }
        }


    }
    else
    {
        state['views'] = views;
        state['layer-order'] = layer_order;
        state['layers'] = layers;

        state['categorical_data_colors'] = categorical_data_colors;
        state['stack_bar_colors'] = stack_bar_colors;
    }

    return state;
}

function drawTree() {
    // get current client size
    VIEWER_WIDTH = document.getElementById('svg').clientWidth;
    VIEWER_HEIGHT = document.getElementById('svg').clientHeight;

    var settings = serializeSettings();
    tree_type = settings['tree-type'];

    $('#draw_delta_time').html('');
    $('#btn_draw_tree').prop('disabled', true);
    $('#bin_settings_tab').removeClass("disabled"); // enable bins tab
    $('#sample_settings_tab').removeClass("disabled"); // enable bins tab
    $('#mouse_tooltips_tab').removeClass("disabled"); // enable bins tab
    $('#search_panel_tab').removeClass("disabled"); // enable bins tab

    waitingDialog.show('Drawing ...', {dialogSize: 'sm'});

    setTimeout(function () 
        { 
            draw_tree(settings); // call treelib.js where the magic happens

            // last_settings used in export svg for layer information,
            // we didn't use "settings" sent to draw_tree because draw_tree updates layer's min&max
            // running serializeSettings() twice costs extra time but we can ignore it to keep code simple.
            last_settings = serializeSettings();

            redrawBins();

            waitingDialog.hide();
            $('#btn_draw_tree').prop('disabled', false);

            if (settings['tree-radius'] == 0)
            {
                $('#tree-radius-container').show();
                $('#tree-radius').val(Math.max(VIEWER_HEIGHT, VIEWER_WIDTH));
            }
        }, 500); 
}


function getContigNames(bin_id) {
    var names = new Array();

    for (var j = 0; j < SELECTED[bin_id].length; j++) {
        if (label_to_node_map[SELECTED[bin_id][j]].IsLeaf()) {
            names.push(SELECTED[bin_id][j]);
        }
    }

    return names;
}


function showContigNames(bin_id) {
    var msg = '<h4>Splits in "' + $('#bin_name_' + bin_id).val() + '" <a href="#" onclick="$(\'#bins-bottom\').html(\'\');">(hide)</a></h4><table class="table table-striped">';
    var names = getContigNames(bin_id);

    for (var i in names)
        msg += "<tr><td><a href='#' class='no-link' onclick='highlightSplit(\"" + names[i] + "\");'>" + names[i] + "</a></td></tr>";

    $('#bins-bottom').html(msg + '</table>');
}

function newBin(id, binState) {

    bin_count++;

    if (typeof id === 'undefined')
    {
        bin_counter++;
        var from_state = false;
        var id = bin_counter;
        var name = bin_prefix + id;
        var color = '#000000';
        var contig_count = 0;
        var contig_length = 0;
        var completeness = '---';
        var redundancy = '---';

        SELECTED[bin_counter] = [];
    }
    else
    {
        // we are adding bins from collection
        var from_state = true;
        var name = binState['name'];
        var color = binState['color'];
        var contig_count = 0;
        var contig_length = 0;
        var completeness = "---";
        var redundancy = "---";
    }

    var template = '<tr bin-id="{id}" id="bin_row_{id}">' +
                   '    <td><input type="radio" name="active_bin" value="{id}" checked></td>' +
                   '    <td><div id="bin_color_{id}" class="colorpicker" color="{color}" style="background-color: {color}"></td>' +
                   '    <td data-value="{name}"><input type="text" size="21" id="bin_name_{id}" value="{name}"></td>' +
                   '    <td data-value="{count}"><input id="contig_count_{id}" type="button" value="{count}" title="Click for contig names" onClick="showContigNames({id});"></td> ' +
                   '    <td data-value="{length}"><span id="contig_length_{id}">{length}</span></td>' +
                   '    <td data-value="{completeness}"><input id="completeness_{id}" type="button" value="{completeness}" title="Click for completeness table" onClick="showCompleteness({id});"></td> ' +
                   '    <td data-value="{redundancy}"><input id="redundancy_{id}" type="button" value="{redundancy}" title="Click for redundant hits" onClick="showRedundants({id});"></td> ' +
                   '    <td><center><span class="glyphicon glyphicon-trash" aria-hidden="true" alt="Delete this bin" title="Delete this bin" onClick="deleteBin({id});"></span></center></td>' +
                   '</tr>';

    template = template.replace(new RegExp('{id}', 'g'), id)
                       .replace(new RegExp('{name}', 'g'), name)
                       .replace(new RegExp('{color}', 'g'), color)
                       .replace(new RegExp('{count}', 'g'), contig_count)
                       .replace(new RegExp('{completeness}', 'g'), completeness)
                       .replace(new RegExp('{redundancy}', 'g'), redundancy)
                       .replace(new RegExp('{length}', 'g'), contig_length);

    $('#tbody_bins').append(template);

    if(!from_state){
        $('#completeness_' + id).attr("disabled", true);
        $('#redundancy_' + id).attr("disabled", true);
    }

    $('#bin_color_' + id).colpick({
        layout: 'hex',
        submit: 0,
        colorScheme: 'dark',
        onChange: function(hsb, hex, rgb, el, bySetColor) {
            $(el).css('background-color', '#' + hex);
            $(el).attr('color', '#' + hex);

            if (!bySetColor) $(el).val(hex);
        },
        onHide: function() {
            redrawBins();
        }
    }).keyup(function() {
        $(this).colpickSetColor(this.value);
    });
}

function deleteBin(id) {
    if (confirm('Are you sure?')) {

        $('#bin_row_' + id).remove();
        $('#tbody_bins input[type=radio]').last().prop('checked', true);
        bin_count--;

        for (var i = 0; i < SELECTED[id].length; i++) {
            var node_id = label_to_node_map[SELECTED[id][i]].id;
            $("#line" + node_id).css('stroke-width', '1');
            $("#arc" + node_id).css('stroke-width', '1');
            $("#line" + node_id).css('stroke', LINE_COLOR);
            $("#arc" + node_id).css('stroke', LINE_COLOR);
        }

        SELECTED[id] = [];
        delete completeness_dict[id];

        if (bin_count==0)
        {
            newBin();
        }

        redrawBins();
    }
}

function showGenSummaryWindow() {
    $.ajax({
        type: 'GET',
        cache: false,
        url: '/data/collections?timestamp=' + new Date().getTime(),
        success: function(data) {
            $('#summaryCollection_list').empty();

            for (source in data) {
                var read_only = data[source]["read_only"];

                if (read_only) {
                    var _name = source + ' (read only)';
                }
                else
                {
                    var _name = source;
                }

                $('#summaryCollection_list').append('<option value="' + source + '">' + _name + '</option>');
            }

            showCollectionDetails('');
            $('#modGenerateSummary').modal('show');
        }
    });
}


function updateBinsWindow(bin_list) {
    if (typeof bin_list === 'undefined')
    {
        var bin_list = [];
        $('#tbody_bins tr').each(
        function(index, bin) {
            bin_list.push(parseInt($(bin).attr('bin-id')));
        });
    }

    for (var _i = 0; _i < bin_list.length; _i++) {
        var bin_id = bin_list[_i];
        var contigs = 0;
        var length_sum = 0;

        for (var j = 0; j < SELECTED[bin_id].length; j++) {
            if (label_to_node_map[SELECTED[bin_id][j]].IsLeaf())
            {
                contigs++;
                length_sum += parseInt(contig_lengths[SELECTED[bin_id][j]]);
            }
        }

        $('#contig_count_' + bin_id).val(contigs).parent().attr('data-value', contigs);
        $('#contig_length_' + bin_id).html(readableNumber(length_sum)).parent().attr('data-value', length_sum);

        updateComplateness(bin_id);
    }

    $('#bin_settings_tab:not(.active) a').css('color', "#ff0000");
}

function updateComplateness(bin_id) {
    $.ajax({
        type: "POST",
        url: "/data/completeness",
        cache: false,
        data: {split_names: JSON.stringify(getContigNames(bin_id)), bin_name: JSON.stringify($('#bin_name_' + bin_id).val())},
        success: function(data){
            completeness_info_dict = JSON.parse(data);

            stats = completeness_info_dict['stats'];
            refs = completeness_info_dict['refs'];

            completeness_dict[bin_id] = completeness_info_dict;

            // it is sad that one liner with a list comprehension takes this much code in JS:
            sum_completeness = 0.0;
            sum_redundancy = 0.0;
            for(source in stats){
                sum_completeness += stats[source]['percent_complete'];
                sum_redundancy += stats[source]['percent_redundancy'];
            }
            average_completeness = sum_completeness / Object.keys(stats).length;
            average_redundancy = sum_redundancy / Object.keys(stats).length;

            $('#completeness_' + bin_id).val(average_completeness.toFixed(1) + '%').parent().attr('data-value', average_completeness);
            $('#redundancy_' + bin_id).val(average_redundancy.toFixed(1) + '%').parent().attr('data-value', average_redundancy);

            $('#completeness_' + bin_id).attr("disabled", false);
            $('#redundancy_' + bin_id).attr("disabled", false);
        },
    });
}

function showCompleteness(bin_id) {
    if (!completeness_dict.hasOwnProperty(bin_id))
        return;

    var refs = completeness_dict[bin_id]['refs'];
    var stats = completeness_dict[bin_id]['stats'];

    var msg = '<h4>Completeness of "' + $('#bin_name_' + bin_id).val() + '" <a href="#" onclick="$(\'#bins-bottom\').html(\'\');">(hide)</a></h4>' +
        '<table class="table table-striped sortable">' +
        '<thead><tr><th data-sortcolumn="0" data-sortkey="0-0">Source</th><th data-sortcolumn="1" data-sortkey="1-0">Percent complenetess</th></tr></thead><tbody>';

    for (var source in stats)
        msg += "<tr><td data-value='" + source  + "'><a href='" + refs[source] + "' class='no-link' target='_blank'>" + source + "</a></td><td data-value='" + stats[source]['percent_complete'] + "'>" + stats[source]['percent_complete'].toFixed(2) + "%</td></tr>";

    $('#bins-bottom').html(msg + '</tbody></table>');
}

function showRedundants(bin_id, updateOnly) {
    if (!completeness_dict.hasOwnProperty(bin_id))
        return;

    $('#bins-bottom').html(buildRedundantsTable(completeness_dict[bin_id], bin_id));

}

function buildRedundantsTable(info_dict, bin_id) {
    var stats = info_dict['stats'];

    var output = '<h4>Redundants of "' + $('#bin_name_' + bin_id).val() + '" <a href="#" onclick="$(\'#bins-bottom\').html(\'\');">(hide)</a></h4>';

    output += '<div class="col-md-12">'
    var oddeven=0;

    for(var source in stats) {
        oddeven++;
        var tabletext = '<div class="table-responsive col-md-6"><table style="margin-bottom: 10px;"><tr><td>';
        tabletext += '<h5>' + source + ' (' + Object.keys(stats[source]['redundants']).length + ')</h5></td></tr>';

        var redundants_html = '';

        for (var redundant in stats[source]['redundants']) {
            var title = '';
            var split_array = '';
            for (var i = 0; i < stats[source]['redundants'][redundant].length; i++)
            {
                var contig = stats[source]['redundants'][redundant][i];

                for (var j = 0; j < contig.length; j++) {
                    // splits
                    title += contig[j] + '\n';
                    split_array += '\'' + contig[j] + '\', ';
                }
            }

            redundants_html += '<span style="cursor:pointer;" \
                                    data-toggle="tooltip" data-placement="top" title="' + title + '" \
                                    onclick="highlighted_splits = [' + split_array + ']; redrawBins();"> \
                                    ' + redundant + ' (' + stats[source]['redundants'][redundant].length + ') \
                                  </span><br />';
        }

        tabletext += '<tr><td valign="top">' + redundants_html + '</tr></td></table></div>';
        output += tabletext;

        if (oddeven%2==0)
        {
            output += '</div><div class="col-md-12">'
        }
    }

    output += '</div>';

    return output;
}

function exportSvg() {
    // check if tree parsed, which means there is a tree on the screen
    if ($.isEmptyObject(label_to_node_map)) 
        return;

    // draw bin and layer legend to output svg
    var settings = serializeSettings();

    var bins_to_draw = new Array();
    $('#tbody_bins tr').each(
        function(index, bin) {
            var bin_id = $(bin).attr('bin-id');
            bins_to_draw.push({
                'name': $('#bin_name_' + bin_id).val(),
                'color': $('#bin_color_' + bin_id).attr('color'),
                'contig-length': $('#contig_length_' + bin_id).html(),
                'contig-count': $('#contig_count_' + bin_id).val(),
            });
        }
    );

    var left = 0 - total_radius - 400; // draw on the left top
    var top = 20 - total_radius;

    if (bins_to_draw.length > 0) {
        drawBinLegend(bins_to_draw, top, left);
        top = top + 100 + (bins_to_draw.length + 2.5) * 20
    }

    // important,
    // we used current settings because we want current bin information.
    // now we are going to use "last_settings" which updated by draw button.
    var settings = {};
    settings = last_settings; 

    drawLayerLegend(settings['views'][current_view], settings['layer-order'], top, left);

    svgCrowbar();

    $('#bin_legend').remove();
    $('#layer_legend').remove();
}

function searchContigs() 
{
    var svalue = $('#searchValue').val();

    if (svalue == "")
    {
        alert("Search value shouldn't be empty.");
        return;
    }
    var column = $('#searchLayerList').val();
    search_column = column;
    var operator = $('#searchOperator').val();
    
    if (operator < 6)
    {
        var operator_text = $('#searchOperator option:selected').text();

        // logical operator
        var _pre = "metadata[";
        var _post = "][" + column + "] " + operator_text + " \"" + svalue.trim() + "\"";

    }
    else if (operator == 6)
    {
        // contains
        var _pre = "metadata[";
        var _post = "][" + column + "].toString().indexOf(\"" + svalue + "\") != -1";
    }

    var _len = metadata.length;
    var _counter = 0;
    search_results = [];

    $('#search_result_message').html("Searching...");

    for (var row=1; row < _len; row++)
    {
        if (metadata[row][column]==null)
            continue;

        if (eval(_pre + row + _post)){
            search_results.push(row);
            _counter++;
        }
    }
    $('#search_result_message').html(_counter + " splits found.");
}

function showSearchResult() {
    var clear_link = '<a href="#" onclick="$(\'.search-results-display, #search-results-table-search-item, #search-results-table-search-name, #search-results-table-header\').html(\'\');">(clear)</a>';
    $("#search-results-table-header").html('<h4>Search results ' + clear_link + '</h4>');
    $("#search-results-table-search-name").html('Split name');
    $("#search-results-table-search-item").html(metadata[0][search_column]);

    var rows = "";
    var _len = search_results.length;
    for (var i=0; i < _len; i++)
    {
        rows = rows + "<tr><td data-value=" + metadata[search_results[i]][0] + "><a href='#' class='no-link' onclick='highlightSplit(\"" + metadata[search_results[i]][0] + "\");'>" + metadata[search_results[i]][0] + "</a></td><td data-value=" + metadata[search_results[i]][search_column] + ">" + metadata[search_results[i]][search_column] + "</td></tr>";
    }
    $(".search-results-display").html(rows);
}

function highlightResult() {
    // check if tree exists
    if ($.isEmptyObject(label_to_node_map)) {
        alert('Draw tree first.');
        return;
    }

    highlighted_splits = [];

    for (var i=0; i < search_results.length; i++) {
        var _contig_name = metadata[search_results[i]][0];
        
        highlighted_splits.push(_contig_name);
    }

    redrawBins(); 
}

function highlightSplit(name) {
    // check if tree exists
    if ($.isEmptyObject(label_to_node_map)) {
        alert('Draw tree first.');
        return;
    }

    highlighted_splits = [name];
    redrawBins();
}

function appendResult() {
    // check if tree exists
    if ($.isEmptyObject(label_to_node_map)) {
        alert('Draw tree first.');
        return;
    }

    var bin_id = getBinId();

    if (bin_id === 'undefined')
        return;

    var bins_to_update = [];
    var _len = search_results.length;
    for (var i=0; i < _len; i++) {
        _contig_name = metadata[search_results[i]][0];
        if (SELECTED[bin_id].indexOf(_contig_name) == -1) {
            SELECTED[bin_id].push(_contig_name);

            if (bins_to_update.indexOf(bin_id) == -1)
                bins_to_update.push(bin_id);
        }

        for (var bid = 1; bid <= bin_counter; bid++) {
            // don't remove nodes from current bin
            if (bid == bin_id)
                continue;

            var pos = SELECTED[bid].indexOf(_contig_name);
            if (pos > -1) {
                SELECTED[bid].splice(pos, 1);

                if (bins_to_update.indexOf(bid) == -1)
                    bins_to_update.push(bid);
            }
        }
    }

    updateBinsWindow(bins_to_update);
    redrawBins();
}

function removeResult() {
    // check if tree exists
    if ($.isEmptyObject(label_to_node_map)) {
        alert('Draw tree first.');
        return;
    }

    var bin_id = getBinId();

    if (bin_id === 'undefined')
        return;

    var bins_to_update = [];
    var _len = search_results.length;
    for (var i=0; i < _len; i++) {
        _contig_name = metadata[search_results[i]][0];
        var _id = label_to_node_map[_contig_name].id;

        var pos = SELECTED[bin_id].indexOf(_contig_name);
        if (pos > -1) {
            SELECTED[bin_id].splice(pos, 1);
            
            if (bins_to_update.indexOf(bin_id) == -1)
                bins_to_update.push(bin_id);
        }
    }

    updateBinsWindow(bins_to_update);
    redrawBins();
}

function showStoreCollectionWindow() {
    $.ajax({
        type: 'GET',
        cache: false,
        url: '/data/collections?timestamp=' + new Date().getTime(),
        success: function(data) {
            $('#storeCollection_list').empty();

            for (source in data) {
                var read_only = data[source]["read_only"];

                if (read_only) {
                    var _disabled = ' disabled="true"';
                    var _name = source + ' (read only)';
                }
                else
                {
                    var _disabled = '';
                    var _name = source;
                }

                $('#storeCollection_list').append('<option value="' + source + '"' + _disabled + '>' + _name + '</option>');
            }

            $('#modStoreCollection').modal('show');
        }
    });
}


function storeRefinedBins() {
    data = {};
    colors = {};

    $('#tbody_bins tr').each(
        function(index, bin) {
            var bin_id = $(bin).attr('bin-id');
            var bin_name = $('#bin_name_' + bin_id).val();

            colors[bin_name] = $('#bin_color_' + bin_id).attr('color');
            data[bin_name] = new Array();

            for (var i=0; i < SELECTED[bin_id].length; i++)
            {
                if (label_to_node_map[SELECTED[bin_id][i]].IsLeaf())
                {
                    data[bin_name].push(SELECTED[bin_id][i]);
                }
            }
        }
    );

    $.post("/store_refined_bins", {
        data: JSON.stringify(data, null, 4),
        colors: JSON.stringify(colors, null, 4),
    },
    function(server_response, status){
          alert("Server: " + server_response + "\n\n(status: " + status + ")");
    });
}

function storeCollection() {
    var collection_name = $('#storeCollection_name').val();

    if (collection_name.length==0) {
        $('#storeCollection_name').focus();
        return;
    }
         
    data = {};
    colors = {};

    $('#tbody_bins tr').each(
        function(index, bin) {
            var bin_id = $(bin).attr('bin-id');
            var bin_name = $('#bin_name_' + bin_id).val();

            colors[bin_name] = $('#bin_color_' + bin_id).attr('color');
            data[bin_name] = new Array();

            for (var i=0; i < SELECTED[bin_id].length; i++)
            {
                if (label_to_node_map[SELECTED[bin_id][i]].IsLeaf())
                {
                    data[bin_name].push(SELECTED[bin_id][i]);
                }
            }
        }
    );

    $.post("/store_collection", {
        source: collection_name,
        data: JSON.stringify(data, null, 4),
        colors: JSON.stringify(colors, null, 4),
    },
    function(server_response, status){
          alert("Server: " + server_response + "\n\n(status: " + status + ")");
    });

    $('#modStoreCollection').modal('hide');    
}

function generateSummary() {
    var collection = $('#summaryCollection_list').val();

    if (collection === null)
        return;

    waitingDialog.show('Generating summary...', {dialogSize: 'sm'});

    $.ajax({
        type: 'GET',
        cache: false,
        url: '/summarize/' + collection + '?timestamp=' + new Date().getTime(),
        success: function(data) {
            if ('error' in data){
                alert(data['error']);
            } else {
                $('#modGenerateSummary').modal('hide');
                waitingDialog.hide();
                // data['url']

                $('#summary_link').html("Summary link: <a href='" + data['url'] + "' target='_blank'>" + data['url'] + "</a>");
                $('#modSummaryResult').modal('show');
            }
        }
    });
}


function showLoadCollectionWindow() {
    $.ajax({
        type: 'GET',
        cache: false,
        url: '/data/collections?timestamp=' + new Date().getTime(),
        success: function(data) {
            $('#loadCollection_list').empty();

            for (source in data) {
                var read_only = data[source]["read_only"];

                if (read_only) {
                    var _name = source + ' (read only)';
                }
                else
                {
                    var _name = source;
                }

                $('#loadCollection_list').append('<option value="' + source + '">' + _name + '</option>');
            }

            $('#loadCollection_list, #btn-load-collection').prop('disabled', false);
            showCollectionDetails('');
            $('#modLoadCollection').modal('show');
        }
    });
}

function showCollectionDetails(list) {

    var cname = $(list).val();

    if (cname=='' || typeof cname === 'undefined')
    {
        // clear details
        var tbl = '<div class="col-md-12">Collection Details</div><hr>' +
            '<div class="col-md-8">Number of Splits:</div><div class="col-md-4"><b>n/a</b></div>' +
            '<div class="col-md-8">Number of Clusters:</div><div class="col-md-4"><b>n/a</b></div>';

        $('.collection-details').html(tbl);

        return;
    }

    $.ajax({
        type: 'GET',
        cache: false,
        url: '/data/collections?timestamp=' + new Date().getTime(),
        success: function(data) {
            var tbl = '<div class="col-md-12">Collection Details</div><hr>' +
                '<div class="col-md-8">Number of Splits:</div><div class="col-md-4"><b>' + data[cname]['num_splits'] + '</b></div>' +
                '<div class="col-md-8">Number of Clusters:</div><div class="col-md-4"><b>' + data[cname]['num_clusters'] + '</b></div>';

            $('.collection-details').html(tbl);
        }
    });
}

function loadCollection() {
    if ($.isEmptyObject(label_to_node_map)) {
        alert('You should draw tree before load collection.');
        return;
    }

    var collection = $('#loadCollection_list').val();
    if (collection === null) {
        alert('Please select a collection.');
        return;
    }

    $('#loadCollection_list, #btn-load-collection').prop('disabled', true);
    
    var bin_list = [];
    var total_selection = 0;
    
    $('#tbody_bins tr').each(
    function(index, bin) {
        bin_list.push(parseInt($(bin).attr('bin-id')));
    });
    for (var _i = 0; _i < bin_list.length; _i++) {
        var bin_id = bin_list[_i];
        for (var j = 0; j < SELECTED[bin_id].length; j++) {
            if (label_to_node_map[SELECTED[bin_id][j]].IsLeaf())
                total_selection++;
        }
    }

    if (total_selection > 0 && !confirm("You will lost current bins, please be sure you stored current bins. Do you want to continue?")) {
        showLoadCollectionWindow();
        return;
    }

    $.ajax({
        type: 'GET',
        cache: false,
        url: '/data/collection/' + collection + '?timestamp=' + new Date().getTime(),
        success: function(data) {
            $('#modLoadCollection').modal('hide');
            
            // clear bins tab
            var bins_cleared = false;
            SELECTED = new Array();
            bin_count = 0;
            bin_counter = 0;

            // calculate treshold.
            var threshold = parseFloat($('#loadCollection_threshold').val()) * $('#loadCollection_threshold_base').val();

            // load new bins
            var bin_id=0;
            for (bin in data['data'])
            {
                // collection may be contain unknown splits/contigs, we should clear them.
                var contigs = new Array();
                var sum_contig_length = 0;

                for (index in data['data'][bin])
                {
                    if (typeof contig_lengths[data['data'][bin][index]] !== 'undefined') {
                        contigs.push(data['data'][bin][index]);
                        sum_contig_length += contig_lengths[data['data'][bin][index]];
                    }
                    
                }

                if (sum_contig_length >= threshold)
                {
                    if (!bins_cleared)
                    {
                        $('#tbody_bins').empty();
                        bins_cleared = true;
                    }
                    bin_id++;
                    bin_counter++;
                    SELECTED[bin_id] = contigs;

                    var _color =  (data['colors'][bin]) ? data['colors'][bin] : randomColor();

                    newBin(bin_id, {'name': bin, 'color': _color});
                }
            }

            rebuildIntersections();
            updateBinsWindow();
            redrawBins();
        }
    });
}

function showSaveStateWindow()
{
    $.ajax({
        type: 'GET',
        cache: false,
        url: '/state/all?timestamp=' + new Date().getTime(),
        success: function(state_list) {
            $('#saveState_list').empty();

            for (state_name in state_list) {
                var _select = "";
                if (state_name == current_state_name)
                {
                    _select = ' selected="selected"'; 
                }
                $('#saveState_list').append('<option ' + _select + '>' + state_name + '</option>');
            }

            $('#modSaveState').modal('show');
            $('#saveState_list').trigger('change');
        }
    });
}

function saveState() 
{
    var name = $('#saveState_name').val();

    if (name.length==0) {
        $('#saveState_name').focus();
        return;
    }

    $.ajax({
        type: 'GET',
        cache: false,
        url: '/state/all?timestamp=' + new Date().getTime(),
        success: function(state_list) {
            for (state_name in state_list) {
                if (state_name == name)
                {
                    if (!confirm('"' + name + '" already exist, do you want to overwrite it?'))
                        return;
                }
            }

        }
    });

    $.ajax({
        type: 'POST',
        cache: false,
        url: '/state/save?timestamp=' + new Date().getTime(),
        data: {
            'name': name,
            'content': JSON.stringify(serializeSettings(true), null, 4)
        },
        success: function(response) {
            response = JSON.parse(response);

            if (response['status_code']==0)
            {
                alert("Failed, Interface running in read only mode.");
            }
            else if (response['status_code']==1)
            {
                // successfull
                $('#modSaveState').modal('hide');

                current_state_name = name;
                $('#current_state').html('[current state: ' + current_state_name + ']');
            }
        }
    });
}


function showLoadStateWindow()
{
    $.ajax({
        type: 'GET',
        cache: false,
        url: '/state/all?timestamp=' + new Date().getTime(),
        success: function(state_list) {
            $('#loadState_list').empty();

            for (state_name in state_list) {
                $('#loadState_list').append('<option lastmodified="' + state_list[state_name]['last_modified'] + '">' + state_name + '</option>');
            }

            $('#modLoadState').modal('show');
        }
    });
}

function loadState()
{
    $.ajax({
        type: 'POST',
        cache: false,
        url: '/state/get?timestamp=' + new Date().getTime(),
        data: {'name': $('#loadState_list').val() },
        success: function(response) {
            try{
                var state = JSON.parse(response);
            }catch(e){
                alert('Failed to parse state data, ' + e);
                return;
            }

            if ((state['version'] !== VERSION) || !state.hasOwnProperty('version'))
            {
                alert("Version of the given state file doesn't match with version of the interactive tree, ignoring state file.");
                return;
            }

            if (state.hasOwnProperty('layer-order')) {
                layer_order = [];
                for (var i = 0; i < state['layer-order'].length; i++)
                {
                    // remove non-exists layers.
                    var layer_id = getLayerId(state['layer-order'][i]);

                    if (layer_id != -1)
                    {
                        layer_order.push(layer_id);
                    }
                }

                // add layers that not exist in state and exist in metadata
                for (var i=1; i < parameter_count; i++)
                {
                    if ($.inArray(i, layer_order) === -1)
                    {
                        layer_order.push(i);
                    }
                }

            } else {
                layer_order = Array.apply(null, Array(parameter_count-1)).map(function (_, i) {return i+1;}); // range(1, parameter_count)
            }

            if (state.hasOwnProperty('views')) {
                views = {};
                for (var view_key in state['views'])
                {
                    views[view_key] = {};
                    for (var key in state['views'][view_key])
                    {
                        var layer_id = getLayerId(key);
                        if (layer_id != -1)
                        {
                            views[view_key][layer_id] = state['views'][view_key][key];
                        }
                    }
                }
            }

            if (state.hasOwnProperty('layers')) {
                layers = {};
                for (var key in state['layers'])
                {
                    var layer_id = getLayerId(key);
                    if (layer_id != -1)
                    {
                        layers[layer_id] = state['layers'][key];
                    }
                }
            }

            if (state.hasOwnProperty('categorical_data_colors')) {
                categorical_data_colors = {};
                for (var key in state['categorical_data_colors'])
                {
                    var layer_id = getLayerId(key);
                    if (layer_id != -1)
                    {
                        categorical_data_colors[layer_id] = state['categorical_data_colors'][key];
                    }
                }
            }

            if (state.hasOwnProperty('stack_bar_colors')) {
                stack_bar_colors = {};
                for (var key in state['stack_bar_colors'])
                {
                    var layer_id = getLayerId(key);
                    if (layer_id != -1)
                    {
                        stack_bar_colors[layer_id] = state['stack_bar_colors'][key];
                    }
                }
            }

            if (state.hasOwnProperty('tree-type'))
                $('#tree_type').val(state['tree-type']);
            if (state.hasOwnProperty('angle-min'))
                $('#angle-min').val(state['angle-min']);
            if (state.hasOwnProperty('tree-height'))
                $('#tree_height').val(state['tree-height']);
            if (state.hasOwnProperty('tree-width'))
                $('#tree_width').val(state['tree-width']);
            if (state.hasOwnProperty('angle-max'))
                $('#angle-max').val(state['angle-max']);
            if (state.hasOwnProperty('tree-radius')) {
                $('#tree-radius-container').show();
                $('#tree-radius').val(state['tree-radius']);
            }
            if (state.hasOwnProperty('layer-margin'))
                $('#layer-margin').val(state['layer-margin']);
            if (state.hasOwnProperty('outer-ring-height'))
                $('#outer-ring-height').val(state['outer-ring-height']);
            if (state.hasOwnProperty('edge-normalization'))
                $('#edge_length_normalization').prop('checked', state['edge-normalization']);
            if (state.hasOwnProperty('custom-layer-margin')) {
                $('#custom_layer_margin').prop('checked', state['custom-layer-margin']).trigger('change');
            }
            if (state.hasOwnProperty('grid-color')) {
                $('#grid_color').attr('color', state['grid-color']);
                $('#grid_color').css('background-color', state['grid-color']);
            }
            if (state.hasOwnProperty('grid-width')) {
                $('#grid_width').val(state['grid-width']);
            }
            if (state.hasOwnProperty('show-grid-for-bins')) {
                $('#show_grid_for_bins').prop('checked', state['show-grid-for-bins']).trigger('change');
                redrawBins();
            }

            // reload layers
            var current_view = $('#views_container').val();
            $("#tbody_layers").empty();
            buildLayersTable(layer_order, views[current_view]);

            current_state_name = $('#loadState_list').val();
            $('#current_state').html('[current state: ' + current_state_name + ']');
            $('#modLoadState').modal('hide');
        }
    });
}
