﻿///<reference path="/Scripts/jquery-1.7.1.js" />
///<reference path="/Scripts/jquery-ui-1.8.16.custom.min.js" />
///<reference path="/Scripts/core.js" />
///<reference path="/Scripts/jquery.validationEngine.js" />


/******************************************************/
/********************** Controls **********************/
/******************************************************/

// .field class above indicates control will have access to .get(), .set([]), and .summary()
(function () {
    jQuery.fn.jValue = function () {
        var type = this.attr('data-fieldtype');
        if (type == undefined || type == '')
            return $(); //this.val(arguments);
        else {
            var ctrl = this.first('div');
            if (arguments.length > 0)
                FieldManager[type].Set(ctrl, arguments);
            else
                return FieldManager[type].Get(ctrl, arguments);
        }
    }

    jQuery.fn.jSummary = function () {
        var type = this.attr('data-fieldtype');
        if (type == undefined) return '';
        return FieldManager[type].Summary(this.first('div'));
    };

    jQuery.fn.jCreate = function () {
        if (arguments["type"] == undefined) return $();
        return FieldManager[type].Create(arguments.type, arguments.text, arguments.values, arguments.options);
    };
})();



var FieldManager = {
    //<div>
    //    <span>text</span>
    //    <div>
    //        $(control)
    //    </div>
    //</div>
    Create: function (type, text, values, options) {
        /// <summary>Wraps inputElem in common input field template</summary>
        /// <param name="type" type="FieldManager[type]">The type of control to create</param>
        /// <param name="text" type="string">The text to display for the control</param>
        /// <param name="values" type="Array">Optional values to populate created control with</param>
        /// <param name="options" type="Hash">Custom options for specific control variations</param>
        /// <returns>Returns entire control as jQuery element</returns>
        if (!FieldManager[type]) return $();
        text = text || '';
        values = values || [];
        options = options || {};

        var pDiv = $('<div class="field">').addClass(type.toLowerCase()).attr('data-fieldtype', type);
        if (type)
            pDiv.addClass(type); //todo: lowercase custom types?
        if (options.Tooltip)
            pDiv.attr('title', options.Tooltip);

        var ctrlDiv = $('<div>');
        var toggleSummary = function () { FieldManager.Toggle(pDiv); };
        var summary = $('<span class="summary"></span>').click(toggleSummary);
        pDiv.append($('<span>').html(text).click(toggleSummary));
        pDiv.append(summary);
        if (options.removable != false) {
            pDiv.append($('<img>', {
                src: "/Resources/Images/icon_close.png",
                alt: "Remove this critieria",
                "class": "remove"
            })
                .click(function () {
                    pDiv.remove();
                    if ($.isFunction(options.onRemove))
                        options.onRemove();
                })
            );
        }

        pDiv.append(ctrlDiv);
        FieldManager[type].Create(ctrlDiv, values, options);
        return pDiv;
    },

    Toggle: function (container, show) {
        var pDiv = container.children('div').first();
        show = (show == undefined) ? !pDiv.is(':visible') : show;
        pDiv.toggle(show);
        var sel = container.find('.summary');
        if (!show) {
            var summary = container.jSummary();
            if (summary != "")
                sel.text(" - " + summary).show();
            else {
                sel.text("");
                container.css({ 'opacity': 0.5 });
            }
        } else {
            container.css({ 'opacity': 1.0 });
        }
        sel.toggle(!show);
    },

    PostRender: function () {
        $('ul.autowidth').each(function (i, ul) {
            var maxWidth = 0;
            $(ul).find('li').each(function (i, li) {
                if ($(li).width() > maxWidth)
                    maxWidth = $(li).width();
            });

            $(ul).find('li').width(maxWidth);
        });

        $("#frmMain, #aspnetForm").validationEngine('detach');
        $("#frmMain, #aspnetForm").validationEngine('attach');
    },

    Autocomplete: {
        Get: function (container) {
            return FieldManager.Checklist.Get(container);
        },

        Set: function (container, values) {
            $.each(container.find('input'), function (i, input) {
                $.each(values, function (i, value) {
                    //                    if (value.Value == input.value) {
                    //                        if (value.Selected)
                    //                            input.attr('checked', 'checked');
                    //                        else
                    //                            input.removeAttr('checked');
                    //                    }
                });
            });
        },

        Summary: function (container) {
            return FieldManager.Checklist.Summary(container);
        },

        Create: function (container, values, options) {
            options = options || {};
            if (!options.watermark)
                options.watermark = "Press down arrow to view list";

            for (var i = 0; i < values.length; ++i) {
                values[i].label = values[i].Text;
                values[i].id = values[i].Value;
            }

            var elem = $('<input>', { type: "text", "class": "typeahead", "data-refcodes": JSON.stringify(values) });
            if (options.watermark)
                elem.attr('title', options.watermark).addClass('jq_watermark');

            container.append(elem);

            elem.autocomplete({
                minLength: 0,
                source: values,
                select: function (e, ui) {
                    ui.item.Selected = true;
                    var chklist = container.find('.checklist .typeahead');
                    var vals = JSON.parse(chklist.attr('data-refcodes') || "[]");
                    vals.push(ui.item); //todo: remove selected items from list
                    chklist.remove();
                    chklist = FieldManager.Create("Checklist", "", vals, { removable: false });
                    chklist.find('input').change(FieldManager.Autocomplete.RemoveOnCheck);
                    elem.after(chklist); //insert checklist of selected values
                    elem.val('');
                    elem.change(); //trigger change event
                    return false;
                }
            }); // this "autocomplete" data is internal to the plugin, and different than the .autocomplete class
            //            .data("autocomplete")._renderItem = function (ul, item) {
            //                if ($.inArray(item.Value, FieldManager.Autocomplete.Get(elem)) >= 0) return $();
            //                var li = $('<li>').data("item.autocomplete", item)
            //                    .append("<a>" + item.Text + "</a>");
            //                return li.appendTo(ul);
            //            };

            return container;
        },

        RemoveOnCheck: function () {
            var chklist = $(this).parents('.Checklist');
            var control = chklist.prev();
            $(this).next().remove(); // remove label
            $(this).remove(); // remove checkbox
            if (chklist.find('input').length == 0) // remove list if empty
                chklist.remove();

            control.change(); //trigger change event
        }
    },

    Boolean: {
        Get: function (container) {
            var vals = [];
            vals.push(container.find('input:checked').val());
            if (vals[0] == "all")
                vals = []; // All is a non-value, allows for an "empty" selection
            return vals;
        },

        Set: function (container, values) {
            container.find('input').removeAttr('checked');
            if (values.length > 0)
                container.find('input [value=[' + values[0] + ']').attr('checked', 'checked');
            else //todo: first value is always default?                
                container.find('input').first().attr('checked', 'checked');
        },

        Summary: function (container) {
            return container.find('input:checked').parent().text();
        },

        //<div>
        //    <span>Include Ghost</span>
        //    <div>
        //        <label>
        //              <input type="radio" value="true" checked="checked" />Yes
        //        </label>
        //        <label>
        //              <input type="radio" value="false" />No
        //        </label>
        //    </div>
        //</div>
        Create: function (container, values, options) {
            values = values || [options.AllowAll ? "all" : "true"];
            var baseId = container.attr('id') || (new Date().getTime() + ''); //todo: better unique id?
            var elem = $('<input>',
            {
                id: baseId + '_true', //todo: drive all ids from method call
                type: "radio",
                value: "true",
                name: baseId
            });
            var lbl = $('<label>');
            lbl.append(elem).append("Yes");
            container.append(lbl);

            var elem2 = $('<input>',
            {
                id: baseId + '_false',
                type: "radio",
                name: baseId,
                value: "false"
            });
            var lbl2 = $('<label>');

            lbl2.append(elem2).append("No");
            container.append(lbl2);

            if (options.AllowAll) {
                var elem3 = $('<input>',
                {
                    id: baseId + '_all',
                    type: "radio",
                    name: baseId,
                    value: "all"
                });
                var lbl3 = $('<label>');

                lbl3.append(elem3).append("All");
                container.append(lbl3);
            }

            container.find('input[value="' + values[0] + '"]').attr('checked', 'checked');

            return container.append;
        }
    },

    Checklist: {
        Get: function (container) {
            var vals = [];
            container.find('input:checked').each(function () {
                var val = $(this).attr('value');
                if (val != 'null') {
                    vals.push(val);
                }
            });
            return vals;
        },

        Set: function (container, values) {
            $.each(container.find('input'), function (i, input) {
                $.each(values, function (i, value) {
                    if (value.Value == input.value) {
                        if (value.Selected)
                            input.attr('checked', 'checked');
                        else
                            input.removeAttr('checked');
                    }
                });
            });
        },

        Summary: function (container) {
            var vals = [];
            $.each(container.find('input:checked'), function (i, elem) {
                vals.push($(elem).parent().text());
            });

            return vals.join(', ');
        },
        //<div data-criteria-id="730">
        //    <span>Status</span>
        //    <div>
        //        <label>
        //              <input type="checkbox" id="20832" value="20832" name="Active" checked="checked" />Active
        //        </label>
        //        <label>
        //              <input type="checkbox" id="29345" value="29345" name="Pending" />Pending
        //        </label>
        //    </div>
        //</div>
        Create: function (container, values, options) {
            if (!values || values.length == 0)
                return $();

            var elem = $('<ul>', {
                "class": "horz",
                "data-refcodes": JSON.stringify(values)
            });

            if (options.autowidth != false) {
                elem.addClass('autowidth');
            }

            container.append(elem);

            $.each(values, function (i, value) {
                if (value != undefined) {
                    var li = $('<li>');
                    var inpt = $('<input>',
                        {
                            type: "checkbox",
                            id: value.Value,
                            value: value.Value
                        });
                    var lbl = $('<label>');
                    li.append(lbl);

                    if (value.Selected) //faster than Checklist.Set()
                        inpt.attr('checked', 'checked');

                    lbl.append(inpt).append(value.Text);
                    elem.append(li);
                }
            });

            return container;
        }
    },

    //<select class="daterange field_mdl">
    //  <option value="1">Months Back:</option>
    //  <option value="2">Days Back:</option>
    //  <option value="3">Date Range:</option>
    //</select>
    //<div>
    //  <span class="timeBackGroup">
    //      <input type="text" class="field_xsm" />
    //  </span>
    //  <span class="rangeGroup">
    //      <input type="text" class="field_smd" />
    //      &nbsp;To&nbsp;
    //      <input type="text" class="field_smd" />
    //  </span>
    //</div>
    DateRange: {
        // matches Metrolist.Web.MVP.Search.ListingSearchModel.ComboDateMode
        // and Searchlight.SearchCriteria.comboDateMode
        Mode: { Months: 0, Days: 1, Range: 2 },
        Get: function (container) {
            var vals = [];
            var type = container.find('select option:selected').val();
            vals.push(type);
            if (type == FieldManager.DateRange.Mode.Range) {
                // ["3", "2011/10/19", ""]
                var inputs = container.find('.rangeGroup input');
                vals.push($(inputs[0]).val());
                vals.push($(inputs[1]).val());
                if (vals[1] == "" && vals[2] == "")
                    vals = [];
            } else {
                // ["1", "4"]
                var val = container.find('.timeBackGroup input').val();
                if (val != "")
                    vals.push(val);
                else
                    vals = [];
            }

            return vals;
        },

        // vals e.g.: 10/19+ Range is ["3", "2011/10/19", ""], 6 Months back is ["1", "6"]
        Set: function (container, vals) {
            if (vals == undefined || vals.length == 0 || vals[0] == "")
                vals = [FieldManager.DateRange.Mode.Months, ""]; //default value: Months Back

            container.find('select option[value=' + vals[0] + ']')
                    .attr('selected', 'selected');

            if (vals[0] == FieldManager.DateRange.Mode.Range) { //date range
                var inputs = container.find('.rangeGroup input');
                for (var i = 0; i < inputs.length; ++i) {
                    vals.push($(inputs[i]).val());
                }
            } else { //days/months back
                container.find('.timeBackGroup input').val(vals[1]);
            }
        },

        Summary: function (container) {
            var vals = FieldManager.DateRange.Get(container);
            var summary = '';
            if (vals.length > 0) {
                summary += container.find('select option[value=' + vals[0] + ']').text();
                summary += vals.slice(1).join(' to ');
            }

            return summary;
        },

        Create: function (container, values, options) {
            container.append(
                $('<select>').addClass('field_mdl')
                    .append($('<option>Months Back:</option>')
                        .attr('value', FieldManager.DateRange.Mode.Months))
                    .append($('<option>Days Back:</option>')
                        .attr('value', FieldManager.DateRange.Mode.Days))
                    .append($('<option>Date Range:</option>')
                        .attr('value', FieldManager.DateRange.Mode.Range))
                    .change(function () {
                        var showRange = $(this).find('option:selected').val()
                                            == FieldManager.DateRange.Mode.Range;
                        var timeBackGroup = $(this).parent().find('.timeBackGroup');
                        var rangeGroup = $(this).parent().find('.rangeGroup');
                        timeBackGroup.toggle(!showRange);
                        rangeGroup.toggle(showRange);

                        if (showRange)
                            timeBackGroup.find('input').val('');
                        else
                            rangeGroup.find('input').val('');
                    }));

            var tb = $('<span>').addClass('timeBackGroup indent');
            tb.append($('<input>').addClass('field_xsm'));
            container.append(tb);

            var dr = $('<span>').addClass('rangeGroup indent nowrap').hide();
            dr.append($('<input>').addClass('field_smd'));
            dr.append($('<span>&nbsp; <b>to</b> &nbsp;</span>'));
            dr.append($('<input>').addClass('field_smd'));
            dr.find('input').datepicker({
                showOn: "button",
                buttonImage: "/Resources/Images/icon_calendar.gif",
                buttonImageOnly: true,
                changeMonth: true,
                changeYear: true
            });
            container.append(dr);

            if (values)
                FieldManager.DateRange.Set(container, values);

            return container;
        }
    },

    //<div data-criteria-id="3021">
    //  <span>Baths</span>
    //  <div>
    //      <input value="1-2" />
    //  </div>
    //</div>
    Range: {
        Get: function (container) {
            var vals = [];
            var inputs = container.find('input');
            if (inputs.val() != '') {
                switch (inputs.length) {
                    //case 2:                                         
                    //  vals.push(inputs.val() || '-1', inputs[1].text() || '-1');                                         
                    //  break;                                         
                    case 1:
                        //todo: handle 100 -> 100,000 and 100,000.00
                        var nums = inputs.val().split('-');
                        if (nums.length >= 2)
                            vals.push(nums[0], nums[1]); //range
                        else {
                            if (inputs.val().indexOf('+') >= 0) //100+, i.e. 100,000 and up
                                vals.push(nums[0].replace('+', ''), -1);
                            else if (inputs.val().indexOf('<') >= 0) //<100, i.e. 100,000 or less
                                vals.push("0", nums[0].replace('<=', '').replace('<', ''));
                            else
                                vals.push(nums[0], nums[0]); //exact match
                        }
                        break;
                }

                if (inputs.attr('data-pricerange') == 'true') {
                    for (var i = 0; i < vals.length; ++i) {
                        if (vals[i] < 1000 && vals[i] != -1)
                            vals[i] = vals[i] * 1000;
                    }
                }
            }
            return vals;
        },

        Set: function (container, vals) {
            var inpt = container.find('input');
            if (inpt.attr('data-pricerange') == 'true') {
                var isReducable = vals.length > 0 && vals[0] % 1000 == 0 && vals[0] < 1000000;
                isReducable = vals.length == 2 ? (vals[1] % 1000 == 0 && vals[1] < 1000000) : isReducable;
                if (isReducable) {
                    for (var i = 0; i < vals.length; ++i) {
                        vals[i] = vals[i] / 1000;
                    }
                }
            }

            if (vals.length == 2) {
                if (vals[1] == -1)
                    inpt.val(val[0] + '+');
                else if (vals[0] == 0)
                    inpt.val('<=' + vals[1]);
            }

            inpt.val(vals.join('-'));
        },

        Summary: function (container) {
            var vals = FieldManager.Range.Get(container);
            var summary = "";
            if (vals.length > 0) {
                var isPrice = (container.find('input').attr('data-pricerange') == 'true');
                for (var i = 0; i < vals.length; ++i) {
                    if (vals[i] != -1)
                        vals[i] = (isPrice ? '$' : '') + formatNum(vals[i]);
                }

                if (vals[0] == vals[1])
                    summary = vals[0];
                else if (vals[0].replace('$', '') == '0')
                    summary = '<=' + vals[1];
                else if (vals[1] == -1)
                    summary = vals[0] + '+';
                else
                    summary = vals.join(' to ');
            }

            return summary;
        },

        // <input value="1-2" />
        Create: function (container, values, options) {
            var inpt = $('<input>').attr('id', options.id || new Date().getTime());
            container.append(inpt).addClass('range');
            if (options.watermark)
                inpt.attr('title', options.watermark).addClass('jq_watermark');
            if (options.pricerange == true)
                inpt.attr('data-pricerange', 'true');

            //http://www.position-absolute.com/articles/jquery-form-validator-because-form-validation-is-a-mess/
            if (options.validate != false) {
                //inpt.addClass('validate[custom[email]]'); //for testing
                inpt.keyup(function (e) {
                    waitForFinalEvent("validateRange",
                        function () {
                            //container.validationEngine("showPrompt", 'Testing: ' + $(e.currentTarget).attr('id'), 'load');
                            //$(e.currentTarget).parentsUntil('.field').validationEngine("validateField", '#' + $(e.currentTarget).attr('id'), 'error', true);
                            container.validationEngine("validateField", '#'+inpt.attr('id'), 'error', true);
                        });
                });
            }

            if (values)
                FieldManager.Range.Set(container, values);
            return container;
        }
    },

    Text: {
        Get: function (container) {
            var vals = [];
            var val = container.find('input').val();
            if (val != '')
                vals.push(val);
            return vals;
        },

        Set: function (container, vals) {
            container.find('input').val(vals);
        },

        Summary: function (container, vals) {
            return FieldManager.Text.Get(container).join(', ');
        },

        // <input value="1-2" />
        Create: function (container, values, options) {
            var inpt = $('<input>').addClass('textinput');
            if (options.watermark)
                inpt.attr('title', options.watermark).addClass('jq_watermark');

            container.append(inpt);

            if (values)
                FieldManager.Text.Set(container, values);
            return container;
        }
    }
}


/******************************************************/
/********************** Requests **********************/
/******************************************************/
var lastRequestId = {};
//deserializes to Metrolist.Web.FieldData
function FieldData(id, values) {
    if(values == undefined)
        values = [];

    this.ID = id;
    if (!(values instanceof Array)) //single values still need to be a list
        this.Values = [(values + '')]; //always a string here.  These are decompressed with tryParsing for now.
    else
        this.Values = values;
}

//serializes to Metrolist.Web.JsonRequest
function JsonRequest(functionName) {
    this.ID = functionName + ':' + new Date().getTime();
    this.Function = functionName;
    this.Data = {};

    lastRequestId[functionName] = this.ID; //track it globally, cancellable

    this.Add = function (id, values) {        
        this.Data[id] = new FieldData(id, values);
    };

    this.Remove = function (id) {
        if (id in this.Data)
            delete this.Data[id];
    };

    this.RemoveAll = function () {
        for (var key in this.Data) {
            Remove(key);
        }
    };

    this.Serialize = function () {
        return JSON.stringify(this);
    };
}

var RequestManager = {
    trackedRequestIDs: {},

    Send: function (request, callback, uri) {        
        RequestManager.Track(request.ID);
        $.ajax({
            type: "POST",
            url: uri || document.URL, //always assume calling page unless specified
            // data needs to be a string to be passed as the body of the post (jQuery nuance)
            data: "'" + JSON.stringify(request) + "'",
            dataType: 'json',
            contentType: "application/json; charset=utf-8",
            success: function (jsonResponse) {
                if (!jsonResponse
                        || !RequestManager.IsValid(jsonResponse.RequestID))
                    return;
                RequestManager.Cancel(jsonResponse.RequestID);
                callback(jsonResponse);
            },
            fail: function (xhr) {
                RequestManager.Cancel(request.ID);
                alert("Error performing request - todo: global error handling here");
                //                var errResp = new jsonResponse();
                //                errResp.ErrorMessage = xhr;
                //                callback(errResp);
            }
        });
    },

    Track: function (id) {
        this.trackedRequestIDs[id] = new Date();
    },

    Cancel: function (id) {
        if (this.IsValid(id))
            delete this.trackedRequestIDs[id];
    },

    IsValid: function (id) {
        return id in this.trackedRequestIDs;
    }
};


/******************************************************/
/********************** Functions *********************/
/******************************************************/

function numericOnlyKeyDownHandler(e) {
    if (!e)
        e = window.event;
    var elem = e.currentTarget || e.srcElement;
    var code = e.keyCode || e.which;
    //only numerics, backspace, etc. allowed
    if (!(((code >= 48) && (code <= 57)) ||
        ((code >= 96 && code <= 105)) ||
        (code == 8) ||
        (code == 9) ||
        (code == 12) ||
        (code == 27) ||
        (code == 37) ||
        (code == 39) ||
        (code == 46))) {
//        if (elem)
//            $(elem).showInfo('Only numbers allowed', null, true);

        e.returnValue = false;
        if (e.preventDefault)
            e.preventDefault();
    }
}

function handleMaxSelectedChkBoxList() {
    //alert($(this).is(':checked'));
    var list = $(this).parentsUntil("table").parent().first();
    var maxItems = list.data('max-selected-items');
    if (list.find(':checked').length > maxItems) {
        list.showError('Please select ' + maxItems + ' items or less');
    }else{
        list.hideError();
    }
}

$(function () {
//    var form = $('#aspnetForm, #frmMain');
//    $.each(form.find("[class*=validate][class*=date]"),
//        function (i, val) {
//            $(this).blur(function () {                
//                if (form.validationEngine('validateField', $(this)))
//                    $(this).hideError();
//            });
    //        });
    $('.jq_watermark').watermark();
});
