/**
 * Core Class
 * This class implements methods that are not implemented by mootools and must be part of the core functionality
 *
 * @abstract
 * @static
 */
Core = {
    /**
     * Create a namepsace
     *
     * @param namespace string, the namespace to create
     */
    createNamespace: function(namespace) {
        var namespace_arr = namespace.split('.');
        var root = window;

        for (var i = 0; i < namespace_arr.length; i++) {
            if (! $type(root[namespace_arr[i]])) {
                root[namespace_arr[i]] = {};
            }

            root = root[namespace_arr[i]] ;
        }
    }
};

/**
 * Loading Class
 *
 * @static
 */
Core.createNamespace('nl.code.util');
nl.code.util.Loading = {
    container: null,

    /**
     * Show the loading box
     *
     * @return void
     */
    show: function() {
        var container = nl.code.util.Loading.getContainer();
        var overlay = container.getElement('div.overlay');

        overlay.setStyles({
            width: Window.getScrollWidth().toInt(),
            height: Window.getScrollHeight().toInt(),
            opacity: 0.4
        });

        container.setStyle('display', 'block');
    },

    /**
     * Hide the loading box
     *
     * @return void
     */
    hide: function() {
        var container = nl.code.util.Loading.getContainer();

        container.setStyle('display', 'none');
    },

    /**
     * Get the loading box
     *
     * @return Element
     */
    getContainer: function() {
        if (! nl.code.util.Loading.container) {
            nl.code.util.Loading.container = nl.code.util.Loading.renderContainer();
        }

        return nl.code.util.Loading.container;
    },

    /**
     * render the loading box
     *
     * @return Element
     */
    renderContainer: function() {
        var container = new Element('div', {
            'id': 'loading'
        });

        container.inject($(document.body));

        var overlay = new Element('div', {
            'class': 'overlay'
        });

        overlay.inject(container);

        var image_container = new Element('div', {
            'class': 'image-container'
        });

        image_container.inject(container);

        return container;
    }
};

/**
 * Observable Class
 *
 * @abstract
 */
Core.createNamespace('nl.code.util');
nl.code.util.Observable = new Class({
    /**
     * Constructor
     *
     * @param an array of supported event names
     */
    initialize: function(event_arr) {
        this.event_hash = new Hash({});

        for (var i = 0; i < event_arr.length; i++) {
            this.event_hash.set(event_arr[i], new nl.code.util.Event(event_arr[i]));
        }
    },

    /**
     * @param string
     * @param JSON
     */
    fireEvent: function(event_name, option_json) {
        if (this.event_hash.has(event_name)) {
            var event = this.event_hash.get(event_name);
            event.fire(this, option_json);

            return true;
        }

        return false;
    },

    /**
     * Add an listener to an event
     *
     * @param string, the name of the event to listen to
     * @param object, the observer object
     * @return boolean
     */
    addListener: function(event_name, object) {
        if (this.event_hash.has(event_name)) {
            var event = this.event_hash.get(event_name);

            return event.addListener(object);
        }

        return false;
    },

    /**
     * Remove an listener to an event
     *
     * @param string, the name of the event to listen to
     * @param object, the observer object
     * @return boolean
     */
    removeListener: function(event_name, object) {
        if (this.event_hash.has(event_name)) {
            var event = this.event_hash.get(event_name);

            return event.removeListener(object);
        }

        return false;
    }
});

/**
 * Event Class
 * This class is only used with the observable class, it is a custom event that an observable can fire
 * to trigger listener (observer) objects
 */
Core.createNamespace('nl.code.util');
nl.code.util.Event = new Class({
    /**
     * Constructor
     *
     * @param string, the name of the event
     */
    initialize: function(event_name) {
        this.event_name = event_name;
        this.listener_arr = [];
    },

    /**
     * Fire the event
     *
     * @param Observable
     * @param JSON
     * @return void
     */
    fire: function(observable, option_json) {
        for (var i = 0; i < this.listener_arr.length; i++) {
            if ($type(this.listener_arr[i].o[this.event_name]) == 'function') {
                this.listener_arr[i].o[this.event_name](observable, option_json);
            }
        }
    },

    /**
     * Add an listener to an event
     *
     * @param object, the listener object
     * @param function, the function to call when the event is fired
     * @return boolean
     */
    addListener: function(object, callback) {
        var listener = {o: object, c: callback};

        if (! this.listener_arr.contains(listener)) {
            this.listener_arr.push(listener);

            return true;
        }

        return false;
    },

    /**
     * Remove an listener to an event
     *
     * @param object, the listener object
     * @param function, the function to call when the event is fired
     * @return boolean
     */
    removeListener: function(object, callback) {
        var listener = {o: object, c: callback};

        if (this.listener_arr.contains(listener)) {
            this.listener_arr.erase(listener);

            return true;
        }

        return false;
    }
});

/**
 * Date Class
 *
 * @namespace nl.code.util
 */
Core.createNamespace('nl.code.util');
nl.code.util.Date = new Class({
    /**
     * Constructor
     *
     * @param Date/string
     * @param object, {seperator: '-', year_index: 0, month_index: 1, day_index: 2}
     */
    initialize: function(date, format) {
        // set the default date format
        if (! $type(format)) {
            format = {
                seperator: '-',
                year_index: 0,
                month_index: 1,
                day_index: 2
            };
        }
        this.format = format;

        this.day = null ;
        this.month = null ;
        this.year = null ;

        if ($type(date) == 'string') {
            date = this.stringToDate(date, format);

            if (date !== null) {
                this.day = date.getDate();
                this.month = date.getMonth();
                this.year = date.getFullYear();
            }
        } else if ($type(date) == 'date') {
            this.day = date.getDate();
            this.month = date.getMonth();
            this.year = date.getFullYear();
        }
    },

    /**
     * Convert this object to a string
     *
     * @param object
     * @return string
     */
    toString: function(format) {
        if (! $type(format)) {
            format = {
                leading_zeros: true,
                full_day: false,
                full_month: true,
                seperator: ' ',
                year_index: 2,
                month_index: 1,
                day_index: 0
            };
        }

        var date_arr = [];

        // year part
        date_arr[format.year_index] = this.year;

        // month part
        if (format.full_month) {
            date_arr[format.month_index] = nl.code.util.Date.month_arr[this.month];
        } else if (format.leading_zeros && this.month < 9) {
            date_arr[format.month_index] = '0'+ (this.month + 1);
        } else {
            date_arr[format.month_index] = this.month + 1;
        }

        // day part
        if (format.full_day) {
            var date = new Date(this.year, this.month, this.day);
            date_arr[format.day_index] = nl.code.util.Date.day_arr[date.getDay()] +' '+ this.day;
        } else if (format.leading_zeros && this.day < 10) {
            date_arr[format.day_index] = '0'+ this.day;
        } else {
            date_arr[format.day_index] = this.day;
        }

        // wrap it up
        var date_str = '';
        for (var i = 0; i < date_arr.length; i++) {
            if (date_str) {
                date_str += format.seperator;
            }

            date_str += date_arr[i];
        }

        return date_str;
    },

    /**
     * Convert a string to a Date
     *
     * @param string
     * @param object, the used format
     * @return Date, the internal javascript Date object
     */
    stringToDate: function(date_str, format) {
        var item_arr = date_str.split(format.seperator);

        if (item_arr.length != 3) {
            return null;
        }

        return new Date(item_arr[format.year_index], (item_arr[format.month_index]-1), item_arr[format.day_index]) ;
    }
});
/**
 * @static variables
 */
nl.code.util.Date.month_arr = ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'];
nl.code.util.Date.day_arr = ['zondag', 'maandag', 'dinsdag', 'woendag', 'donderdag', 'vrijdag', 'zaterdag'];

/**
 * Debug Class
 *
 * @static
 */
Core.createNamespace('nl.code.util');
nl.code.util.Debug = {
    container: null,

    /**
     * @param string
     * @param boolean
     * @return void
     */
    trace: function(text, make_clean) {
        if (! $type(make_clean)) {
            make_clean = false;
        }

        var container = nl.code.util.Debug.getContainer();

        if (! make_clean) {
            text = text +'<br />'+ container.get('html');
        }

        container.set('html', text);
    },

    /**
     * @return Element
     */
    getContainer: function() {
        if (nl.code.util.Debug.container === null) {
            nl.code.util.Debug.container = new Element('div', {
                'id': 'debug'
            });
            nl.code.util.Debug.container.inject($(document.body), 'top');
        }

        return nl.code.util.Debug.container;
    }
};