/*
 * from sjamaan, irc.freenode.net channel: #prototype
 * Checks if the current Hash is a complete subset of the given Hash.
 * It is a subset IFF every key of the current hash is in the given hash and
 * every value of that key equals the value of the same key in the other hash.
 */
Hash.prototype.subset = function(h2)
{
        return this.all(function(entry) {
                var myval = entry[1];
                var theirval = h2[entry[0]];
                if (theirval && (myval instanceof Array) || (myval instanceof Hash))
                        return myval.subset(theirval);
                else
                        return myval == theirval;
        });
}

/* Subset for Enumerable */
Object.extend(Enumerable, {subset : function(a2)
{
        return this.all(function(x) { return a2.include(x); });
}});
Array.prototype.subset = Enumerable.subset;
//equal to Object.extend(Array.prototype, Enumerable) // it seems


//--- herzstueck - der controller fuer ausgehende ajax requests.. ---
var Controller = Class.create(); //prototype syntax
Controller.prototype = {
    initialize: function(path_to_index, testing){ //constructor
        this.reset_to_globals();
        this.reset_last_response();
        if (typeof path_to_index == "undefined") {
          path_to_index = "";
        }
        this.path_to_index=path_to_index;
        this.testing=testing;
        this.on_ajax_loaded_functions = $A();
    },
    
    reset_to_globals: function(){
        this.viewconfig = $H(state.containers); //state has inital values from index.tpl
        this.kunde = $H(state.kunde);
        this.templates_to_fetch=$A(); //collects what has to be updated.., cleared after update;
    },
    
    reset_last_response: function(){
        this.last_transport=null;
        this.last_responseText=null;
        this.last_json=null;
        this.last_xjson=null;
    },

    /*
     * dispatches Ajax requests
     * 'options' contains arguments in associative array/hash
     */
    dispatch: function(options)
    {
        // --- Setting Request Options ---
        if( typeof(options['script']) == 'undefined' )
        {
            options['script'] = this.path_to_index + 'index.php';
        }

        // --- Setting Request Method ---
        if( typeof(options['method']) == 'undefined' )
        {
            options['method'] = 'post';
        }

        options['queryhash'] = $H({
            'aktion':options['aktion'],
            'action':options['action']
        }).merge(this.hashoptions(options['options']));

        //testing?
        //then we only want test requests to happen.. index switches to test database..
        if( typeof(this.testing) != "undefined" )
        {
            options['queryhash'] = options['queryhash'].merge($H({'testing':'1'}));

        }

        options['onCreate_handlers']=this.turn_it_real(options['onCreate_handlers']);
        options['json_handlers']=this.turn_it_real(options['json_handlers'], this.fetch_xjson);
        options['onSuccess_handlers']=this.turn_it_real(options['onSuccess_handlers'], this.fetch_transport);
        options['onComplete_handlers']=this.turn_it_real(options['onComplete_handlers']);
        
        // this.add_message.curry('Request was successful')
        // defaults should rather be set when creating the controller... so it does not get annoying...
        
        //--- starting Ajax Request ---
        var myRequest = new Ajax.Request(
            options['script'],
            {
                method: options['method'],
                parameters: options['queryhash'],

                onCreate: function(){
                    this.execute_functions(options['onCreate_handlers']);
                }.bind(this),
                
                onSuccess: function(transport, json) {
                    
                    this.reset_last_response(); //set our last request response back
                    this.eval_xjson(transport, json);
                    
                    this.execute_functions(options['json_handlers'], transport, json);
                    this.execute_functions(options['onSuccess_handlers'], transport, json);
                    
                    this.trigger_ajax_loaded();
                    
                    //options['json_handlers'](transport, json);  
                    //options['onSuccess_handlers'](transport, json);
                }.bind(this),
                
                onComplete: function(transport, json){
                    this.execute_functions(options['onComplete_handlers'], transport, json);
                }.bind(this)
            });
    }, 
    
    //--- Handler functions --- (functions having arguments before transport must be curried!)
    execute_functions: function(functopt, transport, json){
        if(typeof(functopt)=='function'){
            functopt(transport, json);
        }else{
            if (functopt instanceof Array){
                $A(functopt).each(function(eachfunct){eachfunct(transport, json);}.bind(this));
            }
        }
    },
    
    turn_it_real: function(functopt, mydefault){
        //multiple binds do no harm - only the first counts
        var tir = function(functopt){
            if(typeof(functopt)=='function'){
                return functopt.bind(this);
            }else{
                if(typeof(this[functopt])=='function'){
                    return this[functopt].bind(this);
                }else{
                    if(typeof(mydefault)=='function'){
                        return mydefault.bind(this);
                    }
                } 
            }
            return false;
        }.bind(this); 
        
        if(functopt instanceof Array){
            return $A(functopt).collect(tir).findAll(function(s){return s;}); //no false values in array
        }else{
            return tir(functopt);
        }
        return false;
    },
    
    hashoptions: function(options){
        //allowed are arrays, hashes and strings/numeric values
        var myhash = $H();
        if (typeof(options)=='object' && options.length){     //is an array
            for (var i=0, j=options.length; i<j;i++){
                myhash['option['+i+']']=options[i];  //funktioniert tadellos!
            }
        }
      
        if (typeof(options)=='object' && !options.length){  
            myhash.merge(options);
        }
        
        if (typeof(options)=='string' || options==Number(options)){  
            myhash['option']=options;
        }
        
        return myhash;
    },
     
    // --- for data ---
        
    trigger_ajax_loaded: function(){
    		$A(this.on_ajax_loaded_functions).each(function(eachfunct){eachfunct();}.bind(this));
    },
    
    fetch_transport: function(transport){
        this.last_responseText=transport.responseText;
        this.last_transport=transport;
    },
    
    eval_xjson: function(transport, json){               
        if(typeof(json)=='undefined' && transport.getResponseHeader('X-JSON')){
            this.last_xjson=transport.getResponseHeader('X-JSON').evalJSON();
        }else{
            if (typeof(json)!='undefined'){
                this.last_xjson=json;
            }
        }
    },
    
    eval_json_response: function(transport)
    {
        try
        {
            this.last_json = transport.responseText.evalJSON();
        }
        catch(err)
        {
            this.last_json=$H({'Error':err});
        }
    },
       
    act_on_statechange: function()
    {
        if ( this.last_xjson )
        {
            switch( this.last_xjson['statechange'] )
            {
                case 'make_tmpKunde':
                    this.viewconfig['navblock2']='cart_small.tpl';
                    //that's not really what i want do i?
                    this.add_templates_to_fetch(
                        ['cart_small.tpl',
                        'logged_status.tpl','usermenu.tpl']
                    );
                break;

                case 'plain_register':
                    this.kunde=this.last_xjson['kunde'];
                    this.viewconfig['navblock1']='empty.tpl';
                    this.viewconfig['navblock2']='cart_small.tpl';
                    this.add_templates_to_fetch(
                        ['cart_small.tpl','logged_status.tpl',
                        'usermenu.tpl', 'empty.tpl']
                    );

                break;
            }
        }
    },
    
    //--- view to update? ---
    add_templates_to_fetch: function(templates){
        this.templates_to_fetch=this.templates_to_fetch.concat($A(templates)).uniq();
    },
    
    clear_templates_to_fetch: function(){
        this.templates_to_fetch=$A();
    },
    
    templates_in_viewconfig: function(templates){
        return templates.invoke('toLowerCase').findAll(function(template){
            return this.viewconfig.values().grep(RegExp("^"+template+"$|/+"+template+"$", "i")).length;
        }.bind(this)).uniq();
    },
        
    //--- methods for display
        
    show_in_container: function(div_id, transport){
        $(div_id).innerHTML=transport.responseText; 
    },
   
    display_views: function(){ 
        if (typeof(this.last_json['templates'])!='undefined'){
            $H(this.last_json['templates']).each(            
            function show(view){
                this.viewconfig.each(function(pair){
                    if (pair.value.search(view.key)!=-1){
                        $(pair.key).innerHTML=view.value;
                    }
                }.bind(this));
            }.bind(this));
        }
    },
    
    //--- messages
    add_message:   function(message){
        $('messages').innerHTML+='<br>'+message;
    },
    
    clear_messages:   function(){
        $('messages').innerHTML='';
    },
    
    replace_messages:   function(message){
        $('messages').innerHTML=message;
    },
        
    refresh_view: function()
    {
        var ttf=this.templates_in_viewconfig(this.templates_to_fetch);
        if ( ttf.length>0 )
        {
            this.dispatch(
                $H({
                    'aktion':'json_chat',
                    'options':$H({'templates': ttf}).toJSON(),
                    'onSuccess_handlers': [this.eval_json_response, this.display_views, this.clear_templates_to_fetch]
                })
            );
        }
    },


    //--- Real Public Controller Methods that change the view or do something - 1-CLICK FUNCTIONS -

    // MODEL: this.dispatch($H({'aktion':'add_to_cart', 'options':[artnum, menge], 'json_handlers': [this.act_on_statechange], 'onSuccess_handlers':[this.show_in_container.curry('messages'), this.add_templates_to_fetch.curry(['cart.tpl','cart_small.tpl']), this.refresh_view]}));
    // -> ajax request, index.php hat einzig "ANZ" oder "aktion", ersteres rendert, letzteres ändert das model, aktion wird ausgewählt und alle parameter mittels option übergeben (!=RESTful)

    // this.dispatch(options) where options is a hash with following possibilities
    // script, default is index.php
    // aktion, options -> werden automatisch im query an das php script geschickt
    // onCreate_handlers -> list of functions to be executed before request
    // now we receive a response object, we can add the following callbacks: json_handlers, onSuccess_handlers, onComplete_handlers

    // functions to use, convenience:
    // eval_json_response for normal json, act_on_statechange for xjson in header..
    // SHOULDNT BE USED (?) show_in_container(div_id, transport) shows transport.responseText in div
    // add_templates_to_fetch.curry defines what views have to be refetched after modelchange
    // refresh_view: second ajax request to get changed views and display them..

    //how does things like logged_in menue / change of small_cart work? .. json_statechange?

    registerOnAjaxLoaded: function(triggerFunction) {
    	this.on_ajax_loaded_functions[this.on_ajax_loaded_functions.length] = triggerFunction;
    },

    add_to_cart: function(artnum, menge){
        if (menge>0){
            this.dispatch($H({'aktion':'add_to_cart', 'options':[artnum, menge], 'json_handlers': [this.act_on_statechange], 'onSuccess_handlers':[this.show_in_container.curry('messages'), this.add_templates_to_fetch.curry(['cart.tpl','cart_small.tpl']), this.refresh_view]}));
        }
    },

    changecount: function(artnum, relativecount){
        this.dispatch($H({'aktion':'changecount', 'options':[artnum, relativecount], 'json_handlers': [this.act_on_statechange], 'onSuccess_handlers':[this.show_in_container.curry('messages'), this.add_templates_to_fetch.curry(['cart.tpl','cart_small.tpl']), this.refresh_view]}));
    },

    copy_order_to_cart: function(orderNr){
        this.dispatch($H({'aktion':'copy_order_to_cart', 'options':[orderNr], 'json_handlers': [this.act_on_statechange], 'onSuccess_handlers':[this.show_in_container.curry('messages'), this.add_templates_to_fetch.curry(['cart.tpl','cart_small.tpl']), this.refresh_view]}));
    },

    delete_from_cart: function(id){
        this.dispatch($H({'aktion':'delete_from_cart', 'options':id, 'onSuccess_handlers': [this.show_in_container.curry('messages'), this.add_templates_to_fetch.curry(['cart.tpl','cart_small.tpl']), this.refresh_view]}));
    },

    show_staffelinfo: function(artnum){
        this.dispatch($H({'aktion':'staffelinfo', 'options':artnum, 'onSuccess_handlers':this.show_in_container.curry('navblock3')}));
    },

    register: function(form){
        this.dispatch(
            $H({
                'aktion':'register',
                'options':form.serialize(true),
                'json_handlers': [this.act_on_statechange],
                'onSuccess_handlers':[this.show_in_container.curry('mainview'),  this.refresh_view],
                'onComplete_handlers':[delete_alert]
            })
        );
    },

    send_password: function(form){
      this.dispatch($H({'aktion':'passwort_zusenden', 'options':form.serialize(true), 'onSuccess_handlers': [this.show_in_container.curry('messages')]}));
    },

    bestellen: function(form){
      this.dispatch($H({'aktion':'bestellen', 'options':form.serialize(true), 'onSuccess_handlers': [this.show_in_container.curry('mainview'), this.refresh_view]}));
    },

    show_order_details: function(bestellungnr)
    {
      this.dispatch($H({
        'aktion':'show_order_details',
        'options':bestellungnr,
        'onSuccess_handlers': [this.show_in_container.curry('messages'), this.refresh_view],
        'method': 'get'}));
    },

    admin_update:function(form){
      this.dispatch($H({'action':'UPDATE', 'options':form.serialize(true),
      'onCreate_handlers':[function(){j$('#update_shop_button').toggle(); j$('#await_update').addClass('loading');}],
      'onSuccess_handlers': [this.show_in_container.curry('messages'), this.refresh_view],
      'onComplete_handlers':[function(){j$('#await_update').removeClass('loading'); j$('#update_shop_button').toggle();}]}));
    },

    send_feedback: function(form){
      this.dispatch($H({'aktion':'feedback', 'options':form.serialize(true),
      'onSuccess_handlers': [this.show_in_container.curry('mainview'), this.refresh_view] }));
    }
};


var mc = new Controller; //aktiv und wartend
function fade_msgs(){
  setTimeout(function(){j$('.message.info').fadeOut(2000);}, 1500);
}
function delete_alert(){
  j$('div#messages .message.alert').hide();
}

//for global methods to be used - strange, doesnt work without bind
var add_to_cart = mc.add_to_cart.bind(mc);
var delete_from_cart = mc.delete_from_cart.bind(mc);
var changecount = mc.changecount.bind(mc);
var show_staffelinfo = mc.show_staffelinfo.bind(mc);
var register = mc.register.bind(mc);
var send_password = mc.send_password.bind(mc);
var bestellen = mc.bestellen.bind(mc);
var show_order_details = mc.show_order_details.bind(mc);
var admin_update = mc.admin_update.bind(mc);
var send_feedback = mc.send_feedback.bind(mc);


var copy_order_to_cart = mc.copy_order_to_cart.bind(mc);


var registerOnAjaxLoaded = mc.registerOnAjaxLoaded.bind(mc);

function is_valid_email(email){
    var filterEmail = /^([a-zA-Z0-9_\.\-])+\@(([0-9a-zA-Z\-_])+\.)+([a-zA-Z]{2,4})+$/;
    if(filterEmail.test(email)){
        return true;
    }
    return false;
}
String.prototype.trim = function () {
    return this.replace(/^\s*/, '').replace(/\s*$/, '');
}

