var

esPhinx;

(function($, $module) {

"use strict";

var
    CSS_CLASS = "esphinx ui",
    CSS_CLASS_QUERY = "." + CSS_CLASS.replace(/ +/g, ".");

$module.extend({
    selectors: {Select: {}}
});

$.Extender.extend($module.selectors.Select, true, {
    new: function(select, options) {
        var
            ConstructorReference = $module.selectors.Select.new,
            wrapper = $("<div></div>"),
            caption = $("<div></div>"),
            optionsPanel = $("<div></div>"),
            searchTextBox = $("<input></input>"),
            abstractSelect = $("<ul></ul>"),
            abstractOptions = [],
            captionText = $("<div></div>"),
            selectElement = $(select),
            optionElements = selectElement.find("option"),

            resolveArguments = function(options) {
                if(!options) {
                    options = {};
                }

                if(!options.developmentMode ||
                   typeof options.developmentMode != "boolean") {
                    options.developmentMode = false;
                }
            },

            selected = function(select) {
                return $(select).find("option:checked");
            },

            toggleCaptionClass = function(caption) {
                caption.toggleClass("expanded-arrow", "retracted-arrow");
            },

            reattachFoundListEventListener = function(abstractOptions) {
                var
                    callback = function(abstractOption) {
                        $(abstractOption.element()).off("click");
                        $(abstractOption.element()).on("click", function() {
                            abstractOption.select();
                        });
                    };

                abstractOptions.forEach(callback);
            },

            composeOptions = function(optionElements) {
                var
                    callback = function(option) {
                        option = Option.new({
                            option: option,
                            select: selectElement,
                            caption: caption,
                            captionText: captionText,
                            optionsPanel: optionsPanel,
                            abstractSelect: abstractSelect
                        });

                        abstractOptions.push(option);
                    };

                $(optionElements).each(callback);

                reattachFoundListEventListener(abstractOptions);
                abstractOptions = [];
            },

            compose = function() {
                if (!options.developmentMode) {
                    selectElement.hide();
                }

                wrapper.insertAfter(selectElement);
                captionText.text(selected(selectElement).text());
                caption.append(captionText);
                wrapper.prepend(caption);
                wrapper.append(optionsPanel);
                optionsPanel.append(abstractSelect);

                var

                    composeSearchTextBox = function() {
                        searchTextBox.attribute("type", "text");
                        optionsPanel.prepend(searchTextBox);

                        $.ui.panels.autocomplete.new(searchTextBox,
                                                     abstractSelect,
                                                     options.autocomplete,
                            function(found) {
                                composeOptions(found);
                            }
                        );
                    };

                wrapper.addClass(CSS_CLASS + " select");
                caption.addClass("caption retracted-arrow");
                optionsPanel.addClass("options-panel");
                captionText.addClass("text");

                // toggle on click on caption
                caption.on("click", function() {
                    optionsPanel.toggle();
                    toggleCaptionClass($(this));
                });

                if (typeof options.searchTextBox == "boolean" &&
                    options.searchTextBox) {
                    composeSearchTextBox();
                }

                composeOptions(optionElements);
            },

            attachClickFromOutsideEventListener = function() {
                var
                    wrapper = $(CSS_CLASS_QUERY + ".select"),
                    optionsPanels = wrapper.find(".options-panel"),
                    captions = wrapper.find(".caption");

                $(window.document).on("click", function(e) {

                    if ($(e.target).parent(CSS_CLASS_QUERY + ".select")
                        .empty()) {
                        if (optionsPanels.visible()) {
                            optionsPanels.hide();
                            captions.removeClass("expanded-arrow");
                            captions.addClass("retracted-arrow");
                        }
                    }
                });
            },

            addOptions = function(select, optionElements) {
                var
                    callback = function(optionElement) {
                        select.append(optionElement);
                    };

                $(optionElements).each(callback);
            },

            replaceSelectOptions = function(select, optionElements) {
                select.clean();
                addOptions(select, $(optionElements));
            },

            Option = {
                new: function(data) {
                    var
                        option,
                        abstractOption,
                        ConstructorReference = Option.new,
                        select = $(data.select),

                        buildAbstractOption = function(li, option) {
                            li = $(li);
                            option = $(option);
                            li.text(option.text());
                            li.attribute("data-value", option
                                         .attribute("value"));
                        },

                        append = function(abstractOption) {
                            if (abstractOption.tagName() != "option") {
                                data.abstractSelect.append(abstractOption);
                            }
                        };

                    if (!(this instanceof ConstructorReference)) {
                        return new ConstructorReference(data);
                    }

                    if ($(data.option).tagName() == "option") {
                        option = $(data.option);
                        abstractOption = $("<li></li>");
                        buildAbstractOption(abstractOption, option);
                        append(abstractOption);
                    } else {
                        abstractOption = $(data.option);
                        option = select.find("option[value='" +
                                  abstractOption.data("value") + "']");
                    }

                    this.select = function() {
                        $(data.captionText).text($(abstractOption)
                                                 .textContent());
                        select.find("option:checked").unselect();
                        option.select();
                        $(data.optionsPanel).hide();
                        toggleCaptionClass(data.caption);
                    };

                    this.element = function() {
                        return abstractOption;
                    };
                }
            };

        if (!(this instanceof ConstructorReference)) {
            return new ConstructorReference(select, options);
        }

        resolveArguments(options);
        select = $(select);
        compose();
        attachClickFromOutsideEventListener();

        this.addOptions = function(optionElements) {
            addOptions(select, optionElements);
            composeOptions(optionElements);
        };

        this.replaceOptions = function(optionElements) {
            abstractSelect.clean();
            replaceSelectOptions(select, optionElements);
            composeOptions(optionElements);
        };

        return this;
    }
});

}(esPhinx, esPhinx.ui));