/**
 * Toolkit class for JS ans AJAX.
 * @author Petr Kellnhofer, ID-SIGN 2010
 * @version 1.0.0.1
 *
 */
var IdSignToolkit = function IdSignToolkit() {

    /**
     * Submits JQuery form object.
     *
     * @param form JQueryObject | string - object or JQuery arg.
     * @param callback function(form, data) [optional] - always sync if missing or NULL
     * @param url string [optional] - default value is action attr of form
     * @param sync bool (default false) [optional]
     * @return string | bool | json
     */
    this.submitForm = function(form, callback, url, sync) {
        if (typeof form == "string") {
            form = $(form);
        }

        if (typeof form != "object" || form == null) {
            throw "Invalid form argument!";
        }

        var response;

        if (callback == null || typeof callback == "undefined") {
            sync = true;
            callback = this.getDefaultFormValidateCallback();
        }

        if (typeof callback == "string") {
            callback = $(callback);
        }

        if (typeof callback == "object") {
            var targetObject = callback;
            callback = function(form, data) {
                targetObject.html(data);
            }
        }

        if (typeof callback != "function") {
            throw "Invalid callback argument!";
        }
        
        if (url == null || typeof url == "undefined") {
            url = form.attr("action");
        }

        if (typeof url != "string" || url.length == 0) {
            throw "Invalid url argument!";
        }

        var asyncArg = true;
        
        if (sync) {
            asyncArg = false;
        }

        var urlArg = url;
        
        var values = {};
        $.each(form.serializeArray(), function(i, field) {
            values[field.name] = field.value;
        });

        $.ajax({
            url: urlArg,
            async: asyncArg,
            data: values,
            type: "POST",
            dataType: "text",
            success: function(data) {
                response = data;
                callback(form, data);
            }
        });

        if (sync) {
            return response;
        } else {
            return true;
        }
    }

    /**
     * Registers form submit action to be handeled by toolkit.
     * Submit is always async.
     *
     * @param form JQueryObject | string - object or JQuery arg.
     * @param callback function(form, data) | object function or JQuery object to place result
     * @param url string [optional] - default value is action attr of form
     * @return bool
     */
    this.registerFormSubmit = function(form, callback, url) {
        if (typeof form == "string") {
            form = $(form);
        }
        
        if (typeof form != "object") {
            throw "Invalid form argument!";
        }

        if (typeof callback != "function" && (typeof callback != "object" || callback == null) && typeof callback != "string") {
            throw "Invalid callback argument!";
        }

        if (url != null && typeof url != "undefined" && typeof url != "string") {
            throw "Invalid url argument!";
        }

        var context = this;

        form.submit(function(event) {
            event.preventDefault();
            context.submitForm(form, callback, url);
        });

        return true;
    }

    /**
     * Gets default form validator compatibile with Zend_Form and JSON.
     *
     * @param successCallback function(form, data) called after successfull validation
     * @param formErrorCallback function(form, data) called after error detected
     * @param errorInitFunction(form) function that prepares divs with IDs = elem. ID + _error
     * @return function(form, data)
     */
    this.getDefaultFormValidateCallback = function(successCallback, formErrorCallback, errorInitFunction) {
        if (typeof successCallback == "string") {
            successCallback = $(successCallback);
        }
        
        if (successCallback == "object" && successCallback != null) {
            var target = successCallback;
            successCallback = function(form, data) {
                target.html(data);
            };
        }

        if (typeof errorInitFunction != "function") {
            errorInitFunction = clearFormError;
        }

        var callback = function(form, data) {
            if(typeof data == "error")
            {
                console.warn("error!");
                return;
            }

            var json = $.parseJSON(data);

            if (data == "" || data == "true" || json["success"] == true || json["success"] == "true")
            {
                successCallback(form, data);
                return;
            }            

            // show our response
            errorInitFunction(form);
            for (var elem in json)
            {
                var texts = {};

                var msg = "<ul class='errors'>";
                for (var err in json[elem])
                {
                    if (err == "removeDuplicates" || err == "empty") {
                        continue; // bug fix - i don't quite understand why these values appears in the json
                    }

                    var text = json[elem][err];

                    // prevent duplicates
                    if (texts[text] != null) {
                        continue;
                    }

                    texts[text] = text;

                    msg += "<li>" + text + "</li>";
                }
                msg += "</ul>";

                if ($("#" + elem + "_error").size() > 0) {
                    $("#" + elem + "_error").html(msg);
                } else {
                    $("#" + elem + "[input]_error").html(msg); // captcha fix
                }
            }
            
            if (typeof formErrorCallback == "function") {
                formErrorCallback(form, data);
            }
        }

        return callback;
    }

    /**
     * Clears error divs in form.
     *
     * @param form object
     */
    var clearFormError = function(form) {
        var elements = form.find(":input");

        elements.each(function()
        {
            var name = $(this).attr("name");
            name = name.replace(/\[.*\]/mgi, "");

            var errorElem = $("#" + name + "_error");

            if (errorElem.size() > 0)
            {
                errorElem.html("");
            }
            else
            {
                var div = document.createElement("div");
                div.setAttribute("id", name + "_error");
                $(this).parent().append(div);
            }
        });
    }

    /**
     * Downloads target of anchor.
     *
     * @param anchor JQueryObject | string - object or JQuery arg.
     * @param callback function(data) [optional] - always sync if missing or NULL
     * @param url string [optional] - default value is action attr of form
     * @param sync bool (default false) [optional]
     * @return string | bool | json
     */
    this.downloadAnchor = function(anchor, callback, url, sync) {
        if (typeof anchor == "string") {
            anchor = $(anchor);
        }

        if (typeof anchor != "object") {
            throw "Invalid anchor argument!";
        }

        if (anchor == null && typeof url != "string") {
            throw "Missing both anchor and URL arguments.";
        }

        var response;

        if (callback == null || typeof callback == "undefined") {
            sync = true;
            callback = function(data) {
                response = data;
            }
        }

        if (typeof callback == "string") {
            callback = $(callback);
        }

        if (typeof callback == "object") {
            var targetObject = callback;
            callback = function(data) {
                targetObject.html(data);
            }
        }

        if (typeof callback != "function") {
            throw "Invalid callback argument!";
        }

        if (url == null || typeof url == "undefined") {
            url = anchor.attr("href");
        }

        if (typeof url != "string" || url.length == 0) {
            throw "Invalid url argument!";
        }

        var asyncArg = true;

        if (sync) {
            asyncArg = false;
        }

        var urlArg = url;

        $.ajax({
            url: urlArg,
            async: asyncArg,
            type: "GET",
            success: callback
        });

        if (sync) {
            return response;
        } else {
            return true;
        }
    }

    /**
     * Registers anchor click action to be handeled by toolkit.
     * Submit is always async.
     *
     * @param anchor JQueryObject | string - object or JQuery arg.
     * @param callback function(data) | object function or JQuery object to place result
     * @param url string [optional] - default value is action attr of form
     * @return bool
     */
    this.registerAnchorClick = function(anchor, callback, url) {
        if (typeof anchor == "string") {
            anchor = $(anchor);
        }

        if (typeof anchor != "object") {
            throw "Invalid form argument!";
        }

        if (typeof callback != "function" && (typeof callback != "object" || callback == null) && typeof callback != "string") {
            throw "Invalid callback argument!";
        }

        if (url != null && typeof url != "undefined" && typeof url != "string") {
            throw "Invalid url argument!";
        }

        var context = this;
        var handler = function(event) {
            event.preventDefault();
            context.downloadAnchor(anchor, callback, url);
        }

        anchor.click(handler);
        //anchor.keypress(handler);

        return true;
    }    
}

/**
 * Manager for IdSignEvent objects. Singleton.
 */
var IdSignEventManager = new function IdSignEventManager() {
    var _events = { }

    /**
     * Adds new event.
     *
     * @param event IdSignEvent
     */
    this.addEvent = function(event) {
        if (event == null || !event instanceof IdSignEvent) {
            throw "Invalid event object!";
        }

        _events[event.getName()] = event;
    }

    /**
     * Removes event by event or name.
     *
     * @param name
     */
    this.removeEvent = function(name) {
        delete _events[name];
    }

    /**
     * Gets event or NULL.
     *
     * @param name
     * @return IdSignEvent
     */
    this.getEvent = function(name) {
        return _events[name];
    }

    /**
     * Test existence of event.
     *
     * @param name
     * @return bool
     *
     */
    this.hasEvent = function(name) {
        var event = this.getEvent(name);
        if (event == null) {
            return false;
        }
        return true;
    }

    /**
     * Clears event cache.
     */
    this.clearEvents = function() {
        _events = { };
    };
}

/**
 * Event object.
 */
var IdSignEvent = function IdSignEvent(name) {

    /**
     * Name of object - unique ID.
     */
    var _name = null;

    /**
     * Gets events name.
     *
     * @return string
     */
    this.getName = function() {
        return _name;
    }

    /**
     * Handler functions.
     */
    var _handlers = { }

    this.init = function(name) {
        if (typeof name != "string") {
            throw "Name argument is missing or invalid!";
        }

        _name = name;
    }
    this.init(name);

    /**
     * Adds event handler.
     *
     * @param handler function or array[context, function]
     * @param name string [optional]
     */
    this.addHandler = function(handler, name) {
        if (typeof handler == "object") {
            if (!handler.length >= 2 || typeof handler[1] != "function") {
                throw "Invalid handler.";
            }
        } else if (typeof handler != "function") {
            throw "Invalid handler.";
        }

        if (typeof name != "string" || name.length == 0) {
            name = handler;
        }

       _handlers[name] = handler;
    }

    /**
     * Removes handler.
     *
     * @param name string | function
     */
    this.removeHandler = function(name) {
        delete _handlers[name];
    }

    /**
     * Removes all handlers.
     */
    this.clearHandlers = function() {
        _handlers = { };
    }

    /**
     * Triggers event with arguments.
     * Any arguments supported by handlers are acceptable.
     *
     * @param args mixed
     */
    this.trigger = function(args) {
        for (var key in _handlers) {
            var handler = _handlers[key];

            if (typeof handler == "function") {
                handler(args);
            } else if (typeof handler == "object") {
                handler[1].call(handler[0], args);
            }
        }
    }
}
