(function($) {
    /**
     * Like a "string" field but hidden.
     */
    var _hidden = function (param, options) {

        var paramDiv = AJS.clone("#macro-param-hidden-template").hide();
        var input = $("input", paramDiv);

        return AJS.MacroBrowser.Field(paramDiv, input, options);
    };

     /**
     * The following types are for internal use only, currently used by the {viewfile} variants.
     *
     * TODO: itemList variants which handle multiple values (including enums) plus Space and User pickers exist on the
     * macro-broswer-fields branch. Don't rewrite them!
     */
    var _attachment = function (param, options) {
        var paramDiv = AJS.clone("#macro-param-select-template");
        var input = AJS.$("select", paramDiv);

        options = options || {};
        options.setValue = options.setValue || function (value) {
            AJS.log("_attachment:setValue called: " + value);

            // CONF-15415 : Spurious error thrown in IE6
            try {
                input.val(value);
            } catch(err) {
                AJS.log(err);
            }

            // Input may be set out of sequence (eg with outstanding JSON request) so store
            // temp value if select options not populated.
            if (input.val() != value) {
                input.tempValue = value;  // value not in the current options
            } else {
                delete input.tempValue;
            }
            input.change();
        };
        options.onchange = options.onchange || AJS.MacroBrowser.previewMacro;

        var field = AJS.MacroBrowser.Field(paramDiv, input, options);
        field.getData = function (req) {
            var currentValue = input.tempValue || input.val();
            AJS.log("_attachment:getData called, currentValue: " + currentValue);

            if (!req.title && !req.pageId) return;
            if (options.fileTypes) {
                req.fileTypes = options.fileTypes;
            }
            var url = AJS.params.contextPath + "/json/attachmentsearch.action";
            $.getJSON(url, req, function(data) {
                if (data.error) {
                    AJS.log("_attachment:getData AJAX returned error: " + data.error);
                    return;
                }

//                var MAX_SELECT_LENGTH = 50;     // TODO check with design - do nicer. dT
                $("option", input).remove();
                var attachments = data.attachments;
                AJS.$(attachments).each(function() {
                    var display = this.fileName;
//                    if (display.length > MAX_SELECT_LENGTH) {
//                        var chunkLength = (MAX_SELECT_LENGTH - 1) / 2;
//                        display = display.substr(0, chunkLength) + "..." + display.substr(display.length - chunkLength, chunkLength);
//                    }
                    input.append("<option value='" + this.fileName + "'>" + display + "</option>");
                });
                currentValue = currentValue || input.tempValue;
                AJS.log("_attachment:getData AJAX returned, currentValue: " + currentValue);
                if (currentValue) {
                    options.setValue(currentValue);
                } else if (attachments.length) {
                    options.setValue(attachments[0].fileName);
                }
            });
        };

        return field;
    };

    var _content = function(param, options) {

        options = options || {};

        options.queryParams = function() {
            return "/json/contentnamesearch.action?type=page&type=blogpost&query=";
        };

        var makeContentLink = function (contentProps) {

            var spaceKey = contentProps.spaceKey;
            var pageTitle = contentProps.name;
            var href = contentProps.href;
            // HACK - blogpost may have something like href "/confluence/display/ds/2009/04/23/Neewws"
            // This is the only reference to the date of the blogpost so it needs processing.
            // TODO - ContentNameSearch should include pageId in contentProps returned. dT
            if (contentProps.className == "content-type-blogpost") {
                var path = href.substr(AJS.params.contextPath.length + 1);
                var spaceIndex = path.indexOf("/" + spaceKey + "/");
                if (spaceIndex > -1) {
                    // has URL-friendly blogpost title, set to be like - ds:/2009/04/23/Neewws
                    var dateStart = spaceIndex + spaceKey.length + 1;
                    pageTitle = path.substring(dateStart);
                }
                else {
                    // TODO - if we have content id, use it! dT
                }
            }
            pageTitle = pageTitle.replace(/\+/g, " ");  // TODO - should do complete URL decode?
            return ((spaceKey && spaceKey != AJS.params.spaceKey) ? (spaceKey + ":") : "") + pageTitle;
        };

        var paramDiv = AJS.clone("#macro-param-template");
        var input = AJS.$("input[type='text']", paramDiv);

        options.setValue = options.setValue || function (value) {
            AJS.log("autoListField:setValue called: " + value);
            input.val(value);
            if (options.dependencies && options.dependencies.length) {
                $.each(options.dependencies, function() {
                    AJS.MacroBrowser.fields[this].dependencyUpdated(param.name, input.val());
                });
            }
            (typeof options.onchange == "function") && options.onchange.apply(input);
        };

        var onselect = options.onselect || function(selection) {
            // User makes selection from the search drop-down.
            var span = $("span", selection);
            var contentProps = $.data(span[0], "properties");
            var contentLink = makeContentLink(contentProps);
            options.setValue(contentLink, input);
        };

        input.contentsearch(options.queryParams(), function() {
            var dd = this;
            var $dd = dd.$;
            // Need to put a placeholder div before drop-down element for CSS styling.
            if (!$dd.parent(".macro-param-dropdown-wrapper").length) {
                var placer = AJS("div").addClass("macro-param-dropdown-wrapper");
                $dd.wrap(placer);
            }
            $("ol.last", $dd).remove();  // no need for "Search for" at bottom.
            $("a", $dd).click(function(e) {
                e.preventDefault();
                onselect(this);
            });
        });

        return AJS.MacroBrowser.Field(paramDiv, input, options);
    };

    /**
     * viewdoc, viewpdf, viewxls and viewppt should have the same behaviour for page/spacekey/date fields apart from
     * attachment filter types, so we can use a common constructor.
     */
    var ViewFileField = function(fileTypes) {
        this.fileTypes = fileTypes;
    };

    ViewFileField.prototype.beforeParamsSet = function(params, inserting) {
        if (inserting) {
            // Note - not feasible for blogposts.
            if (AJS.params.contentType == "page") {
                params.page = $("#content-title").val();
            }
        } else if (params.page) {
            if (params.date) {
                // will be in format mm/dd/yyyy, should be yyyy/mm/dd
                var parts = params.date.split("/");
                params.page = ["", parts[2], parts[0], parts[1], params.page].join("/");
            }
            if (params.space) {
                params.page = params.space + ":" + params.page;
            }
        }
        return params;
    };

    ViewFileField.prototype.beforeParamsRetrieved = function(params) {
        if (params.page) {
            var spacePageArr = params.page.split(":");
            if (spacePageArr.length > 1) {
                params.space = spacePageArr[0];
                params.page = spacePageArr[1];
            }
            var parts = params.page.split("/");
            if (parts.length > 1) {
                // will be in format yyyy/mm/dd/Page, should be mm/dd/yyyy
                params.date = [parts[2], parts[3], parts[1]].join("/");
                params.page = parts[4];
            }
        }
        return params;
    };

    ViewFileField.prototype.fields = {
        "attachment" : function(param) {

            /**
             *  Assumes value in form SPACEKEY:(/yyyy/mm/dd/)Title
             */
            var parsePageLink = function(pageLink) {
                var o = {};

                var bits = pageLink.split(":", 2);
                o.spaceKey = ((bits.length == 2) && bits[0]) || AJS.params.spaceKey;
                o.title = bits[bits.length - 1];

                if (o.title.indexOf("/") == 0) {
                    // blogpost
                    o.postingDay = o.title.substr(1, 10);
                    o.title = o.title.substr(12);
                }

                return o;
            };

            var field = _attachment(param, { fileTypes : this.fileTypes });
            field.dependencyUpdated = function(dependencyName, dependencyValue) {
                AJS.log("attachment:dependencyUpdated called: " + dependencyName + ", " + dependencyValue);

                var req = parsePageLink(dependencyValue);
                this.getData(req);
            };
            return field;
        },

        /**
         * Office Connector uses an unnecessary "space" parameter which could be merged with the "page" parameter as per
         * other macros. In the Macro Browser we can get around this by just hiding the space param's div
         */
        "spacekey" : function(param) {
            return _hidden(param, {});
//            return AJS.MacroBrowser.ParameterFields["_hidden"](param, {});
        },

        /**
         * Office Connector also uses an unnecessary "date" parameter which could be merged with the "page" parameter as per
         * other macros. In the Macro Browser we can get around this by just hiding the date param's div
         */
        "date" : function(param) {
            return _hidden(param, {});
//            return AJS.MacroBrowser.ParameterFields["_hidden"](param, {});
        },

        "confluence-content" : function(param) {
            var options = {
                // TODO - define the field with the dependency via the fallback. dT
                dependencies : ["name"]
            };
            return _content(param, options);
//            return AJS.MacroBrowser.ParameterFields["confluence-content"](param, options);
        }
    };

    AJS.MacroBrowser.Macros.viewdoc = new ViewFileField(["doc"]);
    AJS.MacroBrowser.Macros.viewpdf = new ViewFileField(["pdf"]);
    AJS.MacroBrowser.Macros.viewxls = new ViewFileField(["xls"]);
    AJS.MacroBrowser.Macros.viewppt = new ViewFileField(["ppt"]);

    // Allow existing viewfile to redirect to correct alias for its filetype.
    AJS.MacroBrowser.Macros.viewfile = {
        updateSelectedMacro : function (macro) {
            var fileName = macro.params[""] || macro.params["name"];
            if (fileName) {
                var lastDot = fileName.lastIndexOf(".");
                if (lastDot > 0) {
                    var ext = fileName.substring(lastDot + 1);
                    if (ext) {
                        (ext == "doc") && (macro.name = "viewdoc");
                        (ext == "pdf") && (macro.name = "viewpdf");
                        (ext == "ppt") && (macro.name = "viewppt");
                        (ext == "xls") && (macro.name = "viewxls");
                    }
                }
            }
        }
    }
})(AJS.$);