/**
 * @author Alex Usun
 */

/*global $, Class, jscape.SizeFormat, jscape.DateFormat */

var uniqueSelectPathDialogId = (function() {
    var id = 0;
    return function() {
        return "selectPathDialog" + id++;
    };
})();


window.SelectPathDialog = Class.extend({

    DEFAULT_WIDTH: 780,
    DEFAULT_HEIGHT: 500,
    DEFAULT_NEW_DIRECTORY_WIDTH: 450,
    DEFAULT_NEW_DIRECTORY_HEIGHT: 200,

    TEMPLATE: "<div id='{dialogId}' style='display: none'>" +
        "<form action='#' accept-charset='utf-8'>" +
        "<div class='easyui-layout' style='height: 370px;'>" +
        "<div style='height: 80px;'>" +
        "<fieldset>" +
        "<div>" +
        "<label>{lookInLabel}</label>" +
        "<span><select id='{treeBoxId}' style='width:200px;'></select>&nbsp;<a id='{backButtonId}' href='#' title='{backButtonTooltip}' class='easyui-tooltip'></a>&nbsp;<a id='{reloadButtonId}' href='#' title='{reloadButtonTooltip}' class='easyui-tooltip'></a>&nbsp;<a id='{createButtonId}' href='#' title='{createButtonTooltip}' class='easyui-tooltip'>{createButton}</a>&nbsp;<a id='{detailsButtonId}' href='#' title='{detailsButtonTooltip}' class='easyui-tooltip'>{detailsButton}</a>&nbsp;<a id='{listButtonId}' href='#' title='{listButtonTooltip}' class='easyui-tooltip'>{listButton}</a></span>" +
        "</div>" +
        "</fieldset>" +
        "</div>" +
        "<div>" +
        "<div id='{fileTablePanelId}' style='margin: 0 2em 0 2em'>" +
        "<table id='{fileTableId}' style='height: 170px;'></table>" +
        "</div>" +
        "<div id='{fileListPanelId}' style='margin: 0 2em 0 2em; border: 1px solid #BCBCBC; height: 170px; display: none; overflow: auto'>" +
        "<div style='width: 100%; display: table'>" +
        "<div id='{fileListRowId}' style='height: 100%; display: table-row'></div>" +
        "</div>" +
        "</div>" +
        "</div>" +
        "<div style='height: 110px;'>" +
        "<fieldset>" +
        "<div>" +
        "<label>{fileNameLabel}</label>" +
        "<span><input id='{resultFieldId}' type='text' maxlength='1000' style='width: 523px;'/></span>" +
        "</div>" +
        "<div>" +
        "<label>{fileTypeLabel}</label>" +
        "<span><select id='{filterBoxId}' style='width:525px;'></select></span>" +
        "</div>" +
        "</fieldset>" +
        "</div>" +
        "</div>" +
        "</form>" +
        "</div>" +
        "<div id='{newDirectoryDialogId}' style='display: none'>" +
        "<form action='#' accept-charset='utf-8'>" +
        "<fieldset>" +
        "<div>" +
        "<label for='{newDirectoryNameFieldId}'>{newDirectoryNameLabel}</label>" +
        "<input id='{newDirectoryNameFieldId}' type='text' maxlength='255' style='width: 200px;'/>" +
        "</div>" +
        "</fieldset>" +
        "</form>" +
        "</div>",

    DEFAULT_RESOURCES: {
        dialogTitle: "Select Path",
        lookInLabel: "Look in",
        createButton: "Create",
        detailsButton: "Details",
        listButton: "List",
        backButtonTooltip: "Back",
        reloadButtonTooltip: "Reload",
        createButtonTooltip: "Create directory",
        detailsButtonTooltip: "View details",
        listButtonTooltip: "View list",
        submitButton: "Select",
        cancelButton: "Cancel",
        nameColumn: "Name",
        extensionColumn: "Ext",
        sizeColumn: "Size",
        dateColumn: "Date Modified",
        fileNameLabel: "File name",
        fileTypeLabel: "File of type",
        newDirectoryNameDialogTitle: "New Directory",
        newDirectoryNameLabel: "Directory name",
        newDirectoryAlreadyExists: "File or directory already exists"
    },

    ID: null,

    initialize: function(resources) {
        this.ID = {
            dialogId: uniqueSelectPathDialogId(),
            treeBoxId: uniqueSelectPathDialogId(),
            backButtonId: uniqueSelectPathDialogId(),
            reloadButtonId: uniqueSelectPathDialogId(),
            createButtonId: uniqueSelectPathDialogId(),
            detailsButtonId: uniqueSelectPathDialogId(),
            listButtonId: uniqueSelectPathDialogId(),
            fileTablePanelId: uniqueSelectPathDialogId(),
            fileTableId: uniqueSelectPathDialogId(),
            fileListPanelId: uniqueSelectPathDialogId(),
            fileListRowId: uniqueSelectPathDialogId(),
            resultFieldId: uniqueSelectPathDialogId(),
            filterBoxId: uniqueSelectPathDialogId(),
            newDirectoryDialogId: uniqueSelectPathDialogId(),
            newDirectoryNameFieldId: uniqueSelectPathDialogId()
        };

        this.resources = resources || this.DEFAULT_RESOURCES;

        $("body").append(this.TEMPLATE.supplant($.extend({}, this.ID, this.resources)));

        this.dialog = $("#" + this.ID.dialogId);
        this.dialog.show();
        this.dialog.dialog({
            title: this.resources.dialogTitle,
            width: this.DEFAULT_WIDTH,
            height: this.DEFAULT_HEIGHT,
            modal: true,
            resizable: true,
            closable: true,
            buttons: [
                { text: this.resources.submitButton, handler: $.proxy(this._onSubmit, this) },
                { text: this.resources.cancelButton, handler: $.proxy(this._onCancel, this) }
            ],
            onClose: $.proxy(function() { if (!this.closing) { this._onCancel(); }}, this),
            closed: true
        });

        this.fileTablePanel = $("#" + this.ID.fileTablePanelId);
        this.fileListPanel = $("#" + this.ID.fileListPanelId);

        this.treeBox = $("#" + this.ID.treeBoxId);
        this.treeBox.combotree({
            editable: false,
            onSelect: $.proxy(this._treeSelected, this),
            formatter: function(node) { return String(node.text).escapeXml(); }
        });

        this.backButton = $("#" + this.ID.backButtonId).linkbutton({iconCls: "icon-undo", onClick: $.proxy(function() {this.listener.onSelectParent();}, this)});
        this.reloadButton = $("#" + this.ID.reloadButtonId).linkbutton({iconCls: "icon-reload", onClick: $.proxy(function() {this.listener.onReload();}, this)});
        this.createButton = $("#" + this.ID.createButtonId).linkbutton({onClick: $.proxy(this._createDirectoryRequested, this)});
        this.detailsButton = $("#" + this.ID.detailsButtonId).linkbutton({group: "viewType", toggle: true, selected: true, onClick: $.proxy(this._detailedViewSelected, this)});
        this.listButton = $("#" + this.ID.listButtonId).linkbutton({group: "viewType", toggle: true, selected: false, onClick: $.proxy(this._listViewSelected, this)});

        this.fileTable = $("#" + this.ID.fileTableId);
        this.fileTable.datagrid({
            singleSelect: true,
            fitColumns: true,
            remoteSort: false,
            columns: [
                [
                    { field: "shortName", title: this.resources.nameColumn, sortable: true, width: 100, formatter: $.proxy(this._shortNameOf, this) },
                    { field: "extension", title: this.resources.extensionColumn, sortable: true, width: 20, formatter: $.proxy(this._extOf, this) },
                    { field: "size", title: this.resources.sizeColumn, sortable: true, width: 50, formatter: $.proxy(this._sizeOf, this) },
                    { field: "lastModificationDate", title: this.resources.dateColumn, width: 50, formatter: $.proxy(this._dateOf, this) }
                ]
            ],
            striped: true,
            onDblClickRow: $.proxy(this._rowDoubleClicked, this),
            onSelect: $.proxy(this._rowSelected, this)
        });

        this.fileListRow = $("#" + this.ID.fileListRowId);

        this.resultField = $("#" + this.ID.resultFieldId);
        this.resultField.textbox({});

        this.filterBox = $("#" + this.ID.filterBoxId);
        this.filterBox.combobox({
            valueField: "filter",
            textField: "description",
            editable: false,
            onSelect: $.proxy(this._filterSelected, this)
        });

        this.tableVisible = true;

        this.newDirectoryDialog = $("#" + this.ID.newDirectoryDialogId);
        this.newDirectoryDialog.show();
        this.newDirectoryDialog.dialog({
            title: this.resources.newDirectoryNameDialogTitle,
            width: this.DEFAULT_NEW_DIRECTORY_WIDTH,
            height: this.DEFAULT_NEW_DIRECTORY_HEIGHT,
            modal: true,
            resizable: true,
            closable: true,
            buttons: [
                { text: this.resources.createButton, handler: $.proxy(this._onNewDirectorySubmit, this) },
                { text: this.resources.cancelButton, handler: $.proxy(this._onNewDirectoryCancel, this) }
            ],
            onClose: $.proxy(this._onNewDirectoryCancel, this),
            closed: true
        });

        this.newDirectoryNameField = $("#" + this.ID.newDirectoryNameFieldId);
        this.newDirectoryNameField.textbox({validType: "vfs_path_unique", invalidMessage: this.resources.newDirectoryAlreadyExists});
    },

    show: function(listener) {
        this.listener = listener;
        this.dialog.dialog("center").dialog("open");

        if (this.tableVisible) {
            this.detailsButton.linkbutton("select");
        }
    },

    hide: function() {
        if (!this._closed()) {
            this.closing = true;
            this.dialog.dialog("close");
            this.closing = false;
        }
    },

    showNewDirectory: function() {
        this.newDirectoryDialog.dialog("center").dialog("open");

        this.newDirectoryNameField.textbox("setValue", "");
        this.newDirectoryNameField.textbox("textbox").focus();
    },

    hideNewDirectory: function() {
        if (!this._newDirectoryClosed()) {
            this.newDirectoryDialog.dialog("close");
        }
    },

    setFilters: function(filters) {
        this.filterBox.combobox("loadData", filters);
        this.filterBox.combobox("setValue", filters[0].filter).combobox("select", filters[0].filter);
    },

    setData: function(root, entry) {
        this.entry = entry;
        this._initTreeBox(root);

        // easyui lib fails to update an invisible table
        if (this.tableVisible) {
            this.fileTable.datagrid("loadData", this.entry.entries);
            this.tableDataLoaded = true;
        } else {
            this.tableDataLoaded = false;
        }

        this._loadList();

        this.createButton.linkbutton(entry.directoryCreationAllowed ? "enable" : "disable");
        this.resultField.textbox("setValue", "");
    },

    getResult: function() {
        return this.resultField.textbox("getValue");
    },

    setResult: function(value) {
        this.resultField.textbox("setValue", value);
    },

    _closed: function() {
        return !this.dialog || !!this.dialog.dialog("options").closed;
    },

    _onSubmit: function() {
        this.listener.onSubmit();
    },

    _onCancel: function() {
        this.listener.onCancel();
    },

    _newDirectoryClosed: function() {
        return !this.newDirectoryDialog || !!this.newDirectoryDialog.dialog("options").closed;
    },

    _onNewDirectorySubmit: function() {
        if (this.newDirectoryNameField.textbox("isValid")) {
            this.listener.onNewDirectorySubmit(this.newDirectoryNameField.textbox("getValue"));
        }
    },

    _onNewDirectoryCancel: function() {
        this.listener.onNewDirectoryCancel();
    },

    _treeSelected: function(node) {
        if (!this.loadingTree && this.listener && this.entry.path !== node.attributes.path) {
            this.listener.onSelect(node.attributes.path);
        }
    },

    _createDirectoryRequested: function() {
        this.listener.onCreateDirectory();
    },

    _detailedViewSelected: function() {
        this.fileListPanel.hide();
        this.fileTablePanel.show();
        this.tableVisible = true;
        if (!this.tableDataLoaded) {
            this.fileTable.datagrid("loadData", this.entry.entries);
            this.tableDataLoaded = true;
        }
    },

    _listViewSelected: function() {
        this.fileTablePanel.hide();
        this.fileListPanel.show();
        this.tableVisible = false;
    },

    _rowDoubleClicked: function(index, entry) {
        if (entry.directory) {
            this.listener.onSelect(entry.path);
        }
    },

    _rowSelected: function(index, entry) {
        this.listener.onFileSelected(entry);
    },

    _filterSelected: function(record) {
        if (this.listener) {
            this.listener.onSelectFilter(record.filter);
        }
    },

    _initTreeBox: function(root) {
        var treeNodes = this._treeNodesFor(root);
        this.loadingTree = true;
        this.treeBox.combotree("loadData", treeNodes);
        this.loadingTree = false;
        this.treeBox.combotree("setValue", this.entry.name.length ? this.entry.id : "");
    },

    _treeNodesFor: function(root) {
        this.idSequence = 0;
        var nodes = [];
        var i;
        var entry;
        var node;
        for (i = 0; i < root.entries.length; ++i) {
            entry = root.entries[i];
            node = {
                id: ++this.idSequence,
                text: entry.name,
                state: "closed",
                attributes: {
                    path: entry.path
                }
            };

            if (entry.path === this.entry.path) {
                this.entry.id = this.idSequence;
            } else if (this.entry.parents.length && entry.name === this.entry.parents[0].name) {
                this._appendChild(node);
            }

            nodes.push(node);
        }

        return nodes;
    },

    _appendChild: function(node) {
        node.state = "open";
        var i;
        var parent;
        var child;
        for (i = 1; i < this.entry.parents.length; ++i) {
            parent = this.entry.parents[i];
            child = {
                id: ++this.idSequence,
                text: parent.name,
                state: "open",
                attributes: {
                    path: parent.path
                }
            };
            node.children = [child];
            node = child;
        }

        child = {
            id: ++this.idSequence,
            text: this.entry.name,
            state: "open",
            attributes: {
                path: this.entry.path
            }
        };

        this.entry.id = this.idSequence;

        node.children = [child];
    },

    _loadList: function() {
        this.fileListRow.empty();
        this.entryDivList = [];

        var cellTemplate = "<div style='display: table-cell; padding: 0.5em 1em 0.5em 1em;'></div>";

        this.fileListRow.append(cellTemplate);
        var cell = this.fileListRow.find("div:last");

        for (var i = 0; i < this.entry.entries.length; ++i) {
            var entry = this.entry.entries[i];
            cell.append("<div style='margin: 0 0 0.4em 0; white-space: nowrap'>" + this._shortNameOf(0, entry) + "</div>");
            this._initEntryDiv(cell.find("div:last"), entry);

            if ((i + 1) % 6 === 0) {
                this.fileListRow.append(cellTemplate);
                cell = this.fileListRow.find("div:last");
            }
        }
    },

    _initEntryDiv: function(div, entry) {
        div.click($.proxy(function() {
            this._clearEntryDivListSelection();
            div.addClass("datagrid-row-selected");
            this._rowSelected(0, entry);
        }, this));

        div.dblclick($.proxy(function() {
            this._rowDoubleClicked(0, entry);
        }, this));

        this.entryDivList.push(div);
    },

    _clearEntryDivListSelection: function() {
        var i;
        for (i = 0; i < this.entryDivList.length; ++i) {
            this.entryDivList[i].removeClass("datagrid-row-selected");
        }
    },

    _shortNameOf: function(_, entry) {
        return entry.directory
                ? "<span class='tree-icon tree-folder'></span>&nbsp;<span>" + String(entry.name || "").escapeXml() + "</span>"
                : "<span class='tree-icon tree-file'></span>&nbsp;<span>" + String(entry.shortName || "").escapeXml() + "</span>";
    },

    _extOf: function(value, entry) {
        return entry.directory ? "&lt;DIR&gt;" : String(entry.extension || "").escapeXml();
    },

    _sizeOf: function(value, entry) {
        return !entry.directory ? jscape.SizeFormat.format(entry.size) : "";
    },

    _dateOf: function(value, entry) {
        return jscape.DateFormat.format(entry.lastModificationDate);
    }

});


window.SelectPathController = Class.extend({

    FILES_SELECTION: {

        selectionAllowed: function(entry) {
            return !entry.directory;
        },

        filterRequired: function() {
            return false;
        },

        allows: function() {
            return true;
        }
    },

    DIRECTORY_SELECTION: {

        selectionAllowed: function(entry) {
            return entry.directory;
        },

        filterRequired: function() {
            return true;
        },

        allows: function(entry) {
            return entry.directory;
        }
    },

    PATH_SELECTION: {

        selectionAllowed: function() {
            return true;
        },

        filterRequired: function() {
            return false;
        },

        allows: function() {
            return true;
        }
    },

    DEFAULT_FILTERS: [{
        filter: "",
        description: "All Files"
    }],

    initialize: function(load, create, filters, resources) {
        this.dialog = new window.SelectPathDialog(resources);
        this.load = load;
        this.create = create;
        this.dialog.setFilters(filters && filters.length ? filters : this.DEFAULT_FILTERS);
        this.currentFilter = filters && filters.length ? filters[0].filter : this.DEFAULT_FILTERS[0].filter;
        this.cache = {};

        $.extend($.fn.validatebox.defaults.rules, {
            "vfs_path_unique": {
                validator: $.proxy(this._pathNotExists, this),
                message: ""
            }
        });
    },

    selectFile: function() {
        this.selectionMode = this.FILES_SELECTION;
        return this._select();
    },

    selectDirectory: function() {
        this.selectionMode = this.DIRECTORY_SELECTION;
        return this._select();
    },

    selectPath: function() {
        this.selectionMode = this.PATH_SELECTION;
        return this._select();
    },

    onSelectParent: function() {
        if (this.currentEntry.parents.length) {
            this.onSelect(this.currentEntry.parents[this.currentEntry.parents.length - 1].path);
        } else {
            this._rootLoaded(this.root);
        }
    },

    onSelectRoot: function() {
        this.currentEntry = this.root;
        this._initDialog();
    },

    onReload: function() {
        this.load(this.currentEntry.path).done($.proxy(this._pathLoaded, this));
    },

    onCreateDirectory: function() {
        this.dialog.showNewDirectory();
    },

    onSelect: function(path) {
        var entry = this.cache[path];
        if (entry) {
            this._pathLoaded(entry);
        } else {
            this.load(path).done($.proxy(this._pathLoaded, this));
        }
    },

    onFileSelected: function(entry) {
        if (this.selectionMode.selectionAllowed(entry)) {
            this.selectedEntry = entry;
            this.dialog.setResult(entry.path);
        } else {
            this.selectedEntry = null;
        }
    },

    onSelectFilter: function(filter) {
        this.currentFilter = filter;
        this._initDialog();
    },

    onNewDirectorySubmit: function(name) {
        this.dialog.hideNewDirectory();
        if (name) {
            this.create(this.currentEntry.path, name).done($.proxy(this._pathLoaded, this));
        }
    },

    onNewDirectoryCancel: function() {
        this.dialog.hideNewDirectory();
    },

    onSubmit: function() {
        this.dialog.hide();

        if (this.selectedEntry) {
            this.deferred.resolve(this.selectedEntry);
        } else {
            this.deferred.reject();
        }
        this.deferred = null;
    },

    onCancel: function() {
        this.dialog.hide();
        this.deferred.reject();
        this.deferred = null;
    },

    _select: function() {
        this.deferred = $.Deferred();
        this.selectedEntry = null;

        if (!this.root) {
            this.load().done($.proxy(this._rootLoaded, this));
        } else {
            this._initDialog();
            this.dialog.show(this);
        }
        return this.deferred.promise();
    },

    _initDialog: function() {
        this.dialog.setData(this.root, this._applyFilter());
    },

    _rootLoaded: function(entry) {
        this.root = entry;
        this.currentEntry = entry;
        this._initDialog();
        this.dialog.show(this);
    },

    _pathLoaded: function(entry) {
        this.currentEntry = entry;
        this.cache[entry.path] = entry;
        this._initDialog();
    },

    _applyFilter: function() {
        if (!this.currentFilter && !this.selectionMode.filterRequired()) {
            return this.currentEntry;
        }

        var entry = $.extend({}, this.currentEntry);
        entry.entries = [];

        var i;
        var candidate;
        for (i = 0; i < this.currentEntry.entries.length; ++i) {
            candidate = this.currentEntry.entries[i];
            if (candidate.directory || (this.selectionMode.allows(candidate) && this.currentFilter === candidate.extension)) {
                entry.entries.push(candidate);
            }
        }

        return entry;
    },

    _pathNotExists: function(name) {
        for (var i = 0; i < this.currentEntry.entries.length; ++i) {
            var candidate = this.currentEntry.entries[i];
            if (candidate.name == name) {
                return false;
            }
        }
        return true;
    }
});
