/**
 * ajax/ajaj/ajah
 */
function Ajax()
{
	// odkaz na tento objekt
	var that = this;

    // maximalni pocet soubezne probihajicich requestu
    var MAX = 9;

    // pocitadlo pozadavku
    this.reqCounter = 0;

	// HTTP Pozadavek
	this.requests = new Array(MAX);

    // Odesilatele pozadavku
	this.senders = new Array(MAX);
    
	// Callback
	this.callbacks = new Array(MAX);

    // kontejner s obsahem zobrazenym vedle mysi pri probihajicim ajaxovem pozadavku
    this.ajaxLoaderContainerId = null;

    // za kolik milisekund se povoli znovuodeslani ajaxoveho formulare
    this.ajaxFormResendAllowed = 200;

    // posun o kolik pixelu horizontalne od mysi
    this.ajaxLoaderDeltaX = 15;

    // posun o kolik pixelu vertikalne od mysi
    this.ajaxLoaderDeltaY = 15;

    // Callback pro ajaxove formulare
    this.formCallbacks = new Array();

    // Callback pro ajaxove formulare po aktualizaci
    this.formUpdatedCallbacks = new Array();

    // Callback pro ajaxove odkazy
    this.linkCallbacks = new Array();

    // Callback pro ajaxove odkazy po aktualizaci
    this.linkUpdatedCallbacks = new Array();

    // parametry predane pres kotvu (tj. #param=value@par=val )
    this.anchorParams = { };

    // hashmapa odeslanych formularu
    this.sentForms = { };

    // skyje / zobrazi loading container
    this.ShowHideLoading = function(show)
    {
        if(show) {
            $('#'+that.ajaxLoaderContainerId).show();
        } else {
            $('#'+that.ajaxLoaderContainerId).hide();
        }
    }


    // nastavi callback funkci pri obdrzeni odpovedi u ajaxoveho volani formulare
    this.AddFormCallback = function(callback)
    {
        this.formCallbacks.push(callback);
    }

    // nastavi callback funkci pri obdrzeni odpovedi u ajaxoveho volani formulare - po aktualizaci APH
    this.AddFormUpdatedCallback = function(callback)
    {
        this.formUpdatedCallbacks.push(callback);
    }

    // nastavi callback funkci pri obdrzeni odpovedi u ajaxoveho volani formulare
    this.AddLinkCallback = function(callback)
    {
        this.linkCallbacks.push(callback);
    }

    // nastavi callback funkci pri obdrzeni odpovedi u ajaxoveho volani formulare - po aktualizaci APH
    this.AddLinkUpdatedCallback = function(callback)
    {
        this.linkUpdatedCallbacks.push(callback);
    }

    // pomocna metoda, ktera zavola vsechny registrovane callback funkce a vrati jejich navratove hodnoty v poli
    this.CallCallbacks = function(arr, sender, respText)
    {
        var rslts = new Array();
        for(var i=0; i<arr.length; i++)
        {
            var cbResult = arr[i](sender, respText);
            rslts.push(cbResult);
        }
        return rslts;
    }

    // metoda projde vsechny boolean navratove hodnoty v poli results a vrati jejich logicky soucin
    this.checkBoolResults = function(results)
    {
        for(var i=0; i<results.length; i++)
        {
            if(!results[i]) return false;
        }
        return true;
    }


	// Metoda vytvori novy objekt HttpRequest
	this.CreateRequest = function()
	{
        var index = this.reqCounter;
		if (window.ActiveXObject)
		{
			this.requests[index] = new ActiveXObject("Microsoft.XMLHTTP");
		}
		else
		{
			this.requests[index] = new XMLHttpRequest();
		}
        this.reqCounter++;
        if(this.reqCounter > MAX)
        {
            this.reqCounter = 0;
        }
        return index;
	};
	
    // vlastni GET pozadavek
    this.CustomGetRequest = function(obj, url, callback, sync)
    {
        return this.GetRequest(obj, url, callback, sync, 'custom');
    }

	// Metoda udela GET request na `url` a po obdrzeni odpovedi zavola `cb` callback funkci,
    // pokud je sync = true, vola se synchronne a metoda vrati text odpovedi
    // rtype pokud je custom jde o vlastne ajaxovy pozadavek, pokud se neuvede jde o regulerni GET pozadavek
	this.GetRequest = function(obj, url, callback, sync, rtype)
	{
        var reqtype = rtype != undefined ? rtype : 'get';
        var async = sync != undefined ? !sync : true;
		var index = this.CreateRequest();
        this.senders[index] = obj;
        if(callback != undefined)
        {
            this.callbacks[index] = callback;
        }
		this.requests[index].open("GET", url, async); // true = async
        this.requests[index].setRequestHeader("X-Request-Type", reqtype);
        this.requests[index].setRequestHeader("X-Request-Seq", index.toString());
        if(async) eval("this.requests[index].onreadystatechange = function() { var i = " + index + "; that.ProcessRequest(i); };");
        that.ShowHideLoading(true);
		this.requests[index].send(null); // query string je do GET requestu null
        if(!async) {
            var res = this.HandleSyncRequest(index);
            return res;
        }
        return undefined;
	};
	
	// Metoda udela POST request s parametry na `url` a po obdrzeni odpovedi zavola `cb` callback funkci
	// params je retezec querystring
    // pokud je sync = true, vola se synchronne a metoda vrati text odpovedi
	this.PostRequest = function(obj, url, params, callback, sync)
	{
        var async = sync != undefined ? sync : true;
        var index = this.CreateRequest();
        this.senders[index] = obj;
        if(callback != undefined)
        {
            this.callbacks[index] = callback;
        }
		this.requests[index].open("POST", url, async);
		if(sync) eval("this.requests[index].onreadystatechange = function() { var i = " + index + "; that.ProcessRequest(i); };");
		this.requests[index].setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		this.requests[index].setRequestHeader("Content-length", params.length);
		this.requests[index].setRequestHeader("Connection", "close");
        this.requests[index].setRequestHeader("X-Request-Type", "post");
        this.requests[index].setRequestHeader("X-Request-Seq", index.toString());
        that.ShowHideLoading(true);
		this.requests[index].send(params);
        if(!async) {
            var res = this.HandleSyncRequest(index);
            return res;
        }
        return undefined;
	};

    // dokonce request a vrati odpoved
    this.HandleSyncRequest = function(index)
    {
        var resp = this.requests[index].responseText;
        that.FinalizeRequest();
        return resp;
    }

    // obnovi / zasedne formularove prvky
    this.enableDisableFormControls = function(frm, enable)
    {
        //if(frm.id != undefined && frm.id != '')
        //{
            //var form = $('#'+frm.id);
            var elems = $('input[type=submit],input[type=image],button[type=submit]', frm);
            if(enable)
            {
                elems.attr('disabled', true);
            }
            else
            {
                elems.removeAttr('disabled');
            }
        //}
    }


    // zrusit odeslani formulare
    this.CancelSendForm = function(frm)
    {
        that.UnsetFormSent(frm.id);
        ajax.ShowHideLoading(false);
        that.enableDisableFormControls(frm, false);
    }

    // Metoda se rozhodne, jakym zpusobem poslat formular na server (POST/GET)
    // zaroven naplni parametry z formularovych poli
    this.FormSubmit = function(frm, event)
    {
        if(that.IsFormSent(frm.id)) {return false;}
        that.SetFormSent(frm.id);
        that.enableDisableFormControls(frm, true);
        that.ShowHideLoading(true);
        return true;
    }

	// Metoda vytahne vysledek ze zadaneho dotazu a zavola callback funkci
	// callback funkce se vola se dvema parametry, prvni je text odpovedi a druhy reference 
	// na AjaxObject ktery odpoved vyvolal
    this.ProcessRequest = function(index)
    {
        if(that.requests[index].readyState == 4) // dokument nacten
        {
            var indexHeader = that.requests[index].getResponseHeader("X-Request-Seq");
            var reqType = that.requests[index].getResponseHeader("X-Request-Type");
            if(index == indexHeader && reqType != undefined)
            {
                if(that.requests[index].status == 200) // zdarilo se
                {
                    var respText = that.requests[index].responseText;
                    try
                    {
                        respText = JSON.parse(respText);
                    }
                    catch(exception)
                    {
                        // TODO: logovani z ajaxu !!!
                        alert(exception + ' :: ' + respText);
                    }
                    var sndr = that.senders[index];
                    if(that.callbacks[index] != undefined)
                    {
                        that.callbacks[index](sndr, respText, that); // volani callback funkce
                    }
                    else if(reqType == 'get' || reqType == 'post')
                    {
                        var results = that.CallCallbacks(that.linkCallbacks, sndr, respText);
                        if(that.checkBoolResults(results)) that.UpdatePlaceHolders(respText);

                        that.CallCallbacks(that.linkUpdatedCallbacks, sndr, respText);
                        that.AdjustForms();
                    }
                }
                else
                {
                    // i kdyz se nezdari, zavolame callback funkci
                    // FIXME: callback funkce pro chybne requesty - opetovny ajax request na logovani ????
                    that.callbacks[index]("ERROR"); //("Error: " + that.request.status + " : " + that.request.statusText);
                }
            }
            that.FinalizeRequest();
        }
    };

    // vymaze pouzity request a pokusi se skryt cekatko
    this.FinalizeRequest = function(index)
    {
        // vyprazdnime index requestu
        that.requests[index] = null;
        // pokusime se skryt ajax loader
        var hideAjaxLoader = true;
        for(var i=0; i<MAX; i++)
        {
            if(that.requests[index] != null) hideAjaxLoader = false;
        }
        if(hideAjaxLoader)
        {
            that.ShowHideLoading(false);
        }
    };


    // kontrola, zda se formular neodeslal 2x po sobe
    this.IsFormSent = function(frmId)
    {
        if(this.sentForms[frmId] == undefined) return false;
        return this.sentForms[frmId];
    };

    // nastavi, ze byl formular odeslan
    this.SetFormSent = function(frmId)
    {
        this.sentForms[frmId] = true;
    };

    // nastavi, ze formular nebyl odeslan
    this.UnsetFormSent = function(frmId)
    {
        this.sentForms[frmId] = false;
    };



    // nastavi automaticke zamenovani URL v odkazech a formularich
    this.AdjustURLs = function()
    {
        // Zmenime odkazy
        $("a.ajaxlink").live("click", function(event) {
            if(!event.isDefaultPrevented())
            {
                ajax.GetRequest(this, this.href);
            }
            return false;
        });

        // Zmenime formulare
        this.AdjustForms();
    };

    // upravi formulare aby byly odesilane ajaxem
    this.AdjustForms = function()
    {
        $("form.ajaxform").each(function(i, frm) {
            if(frm.id) {
                // prepriradit udalost submit
                $('#'+frm.id).unbind('submit', that.AjaxFormSubmit);
                $('#'+frm.id).bind('submit', that.AjaxFormSubmit);
                // identifikace ajax formu
                var fname = frm.method.toString().toLowerCase() == 'post' ? '__html_post_form_sent_by_ajax__' : '__html_get_form_sent_by_ajax__';
                // pokud tam jeste neni, vlozime skryte pole
                if(!document.getElementById(frm.id + fname))
                {
                    var hidden = document.createElement('input');
                    hidden.type = 'hidden';
                    hidden.name = fname;
                    hidden.id = frm.id + fname;
                    hidden.value = 'yes';
                    frm.appendChild(hidden);
                }
                var ifr = document.getElementById(frm.id + '__iframe');
                // pokud iframe jeste neexistuje, tak ho vlozime
                if(!ifr)
                {
                    ifr = document.createElement('iframe');
                    ifr.style.display = 'none';
                    ifr.name = frm.id + '__iframe';
                    ifr.id = frm.id + '__iframe';
                    document.body.appendChild(ifr);// insertBefore(ifr, document.body.firstChild);
                }
                frm.target = frm.id + "__iframe";
                // prepriradit udalost load
                $(ifr).unbind();
                $(ifr).bind("load", function(event) {
                    that.ShowHideLoading(false);
                    var respText = ifr.contentDocument.body.innerHTML;
                    if(respText != '')
                    {
                        that.UnsetFormSent(frm.id);
                        that.enableDisableFormControls(frm, false);
                        respText = decodeURIComponent(respText);
                        try
                        {
                            respText = JSON.parse(respText);
                            var results = that.CallCallbacks(that.formCallbacks, frm, respText);
                            if(that.checkBoolResults(results)) that.UpdatePlaceHolders(respText);
                            that.CallCallbacks(that.formUpdatedCallbacks, frm, respText);
                            that.AdjustForms();
                        }
                        catch(exception)
                        {
                            // TODO: logovani z ajaxu !!!
                            alert(exception + ' :: ' + respText);
                        }
                    }
                });
            }
        });
    };

    // Handler ktery se zavola pri odeslani formulare ajaxem
    this.AjaxFormSubmit = function(event)
    {
        var rval = ajax.FormSubmit(this, event);
        return rval;
    };


    // naplni slovnik s parametry predanymi pres kotvu
    // @todo otestovat zda nektera kotva neni urcena ke skoku na urcite misto na strance
    this.ParseAnchorParams = function()
    {
        var href = window.location.href.toString();
        var anchorPos = href.indexOf("#");
        // pokud je kotva a je neprazdna, tak ji zpracujeme
        if(anchorPos > -1 && href.length > anchorPos+1)
        {
            var aPars = href.substr(anchorPos+1);
            var aParsArray = aPars.toString().split('@');
            for(var i=0; i<aParsArray.length; i++)
            {
                var pair = aParsArray[i].split('=');
                this.anchorParams[pair[0]] = (pair.length > 1 ? pair[1] : '');
            }
        }
    }

    // window.location bez kotvy
    this.GetAnchorlessLocation = function()
    {
        var href = window.location.href.toString();
        var anchorPos = href.indexOf("#");
        if(anchorPos > -1) href = href.substr(0, anchorPos);
        return href;
    }

    // zaktualizuje URL s aktualne nastavenymi anchor paramtry
    this.AnchorLocationUpdate = function()
    {
        var href = this.GetAnchorlessLocation();
        var first = true;
        for(var param in this.anchorParams)
        {
            if(this.anchorParams[param] != undefined)
            {
                if(first) {href += '#';first = false;}
                else {href += '@';}
                href += param;
                if(this.anchorParams[param] != '')
                {
                    href += '=' + this.anchorParams[param];
                }
            }
        }
        window.location.href = href;        
    }


    // aktualizuje ajax placeholders
    this.UpdatePlaceHolders = function(jsonResponse)
    {
        for(var id in jsonResponse)
        {
            var elem = document.getElementById('APHElem_' + id);
            if(elem != null)
            {
                elem.innerHTML = jsonResponse[id];
            }
            else
            {
                //alert("APH " + id + " chybi!" + jsonResponse);
            }
        }
    }
    
    $(document).mousemove(function(e){
        //document.title = e.pageX + ',' + e.pageY;
        $('#'+that.ajaxLoaderContainerId).css({
            'position': 'absolute',
            'z-index': 9999,
            'top': (e.pageY + that.ajaxLoaderDeltaY).toString() + 'px',
            'left': (e.pageX + that.ajaxLoaderDeltaX).toString() + 'px'
        });
    });


}

var ajax = new Ajax();

// po nacteni zajaxujeme odkazy a form action
$(document).ready(function() {
    ajax.AdjustURLs();
    ajax.ParseAnchorParams();
});
