
var Ctrl_Calendar_Renderer = Ctrl_Class.extend({

    init: function(targetID, eventModel, options) {
        this.range = [];

        this.setEventModel(eventModel);
        this.options = jQuery.extend({
            dayEventDecorator: Ctrl_Calendar_Event_Decorator_Node_Week,
            weekEventDecorator: Ctrl_Calendar_Event_Decorator_Node_Week,
            monthEventDecorator: Ctrl_Calendar_Event_Decorator_Node_Month,
            
            drawMonthEventsCallback: this._drawMonthEvents,
            
            clickableMonthDays: true,
            
            dayHeaderFormat: 'm/d',
            weekHeaderFormat: 'm/d',
            
            date: new Date(),
            hourHeight: 40,
            serverTime: null,
            view: 'week', // day, week or month
            
            onEventsDrawn: null,
            onChange: null
        }, options);
        
        if(typeof this.options.onEventsDrawn == 'function') {
            this.bind('eventsDrawn', this.options.onEventsDrawn);
        }
        
        if(typeof this.options.onChange == 'function') {
            this.bind('change', this.options.onChange);
        }
        
        this.now = new Date();
        this.displaying = this.options.date;

        this.ID = targetID;
        this.target = $('#' + targetID);
        
        this.grid = {}

        this._drawContainers();
        
        this.view(this.displaying, this.options.view);
    },   

    setEventModel: function(eventModel) {
        this._eventModel = eventModel;
    },
    
    _drawContainers: function() {
        this.target.append($('<div class="colTitlesWrapper"><div class="colTitles"></div></div><div id="viewportContainer"></div>'));
        $('#viewportContainer', this.target).append(
                '<div id="'+ this.ID +'-viewport" class="viewport">' +
                    '<div id="calendarContainer" style="height: '+ this.options.hourHeight * 24 +'px;">' +
                      '<div id="decoratorContainer"></div>' +
                      '<div id="gridContainer"></div>' +
                      '<div id="eventContainer"></div>' +
                    '</div>' +
                 '</div>');
       this.viewport = $('#' + this.ID +'-viewport', this.target);             
       this.gridContainer = $('#gridContainer', this.viewport);   
       this.decoratorContainer = $('#decoratorContainer', this.viewport);
       this.eventContainer = $('#eventContainer', this.viewport); 
       this.calendarContainer = $('#calendarContainer', this.viewport);
       

       var rowTitles = ['<div class="rowTitles unselectable"><div style="height: '+ this.options.hourHeight +'px">12am</div>'];
       for (var i=1; i<24; i++) {
           rowTitles.push('<div style="height: '+ this.options.hourHeight +'px">' + ((i < 12)? (i)+'am' : ((i != 12)? (i-12)+'pm' : i+'pm' )) + '</div>');
       }
       
       this.viewport.prepend(rowTitles.join(''));
    },

    getHourHeight: function () { return this.options.hourHeight; },
    
    getRange: function() { return this.range; },

    /**
     * Returns the name of the view currently enabled.
     */
    getView: function() { return this.currentView; },

    /**
     * Returns the height of the drawable area of a grid cell in pixels.
     */
    getCellHeight: function() {
        var height = this.grid.rowHeight;
        
        switch(this.grid.rowHeightUnit) {
            case '%':
                height = (this.viewport.height() /100) * height;
                break;
        }
        
        return height;
    },

    /**
     * Returns the width of the drawable area of a grid cell in pixels.
     */
    getCellWidth: function() {
    },
    
    /**
     * getTimeFromY(y)
     *
     * Transforms a y coord into a Date object. 
     *
     * @param int
     * @return Date
     */
    getTimeFromY: function(y) {
        var hours = Math.round(y/10)*10 / this.options.hourHeight;
        var minutes = (hours - Math.floor(hours)) * 60;
        var date = new Date(0, 0, 0, hours, minutes);

        return date;
    },
    
    render: function(date, view) { 
        this.displaying = date;
        this.currentView = view;

        this.target.attr('class', view);

        var str = '';

        switch(view) {
        case 'day':
            this._drawGrid({rows: 48, cols: 1, rowHeight: (this.options.hourHeight /2) + 'px'});
            
            this.range = [date, date];
            str = date.getFullDay() + ', ' + date.getMonthAbbr() + ' ' + date.getDate() + ', ' + date.getFullYear();
            break;
        case 'week':
            this._drawGrid({rows: 48, cols: 7, rowHeight: (this.options.hourHeight /2) + 'px'});
            
            var first = date.getFirstDayOfWeek();
            var last = date.getLastDayOfWeek();

            this.range = [first, last];

            if(first.getMonth() == last.getMonth()) {
                str = first.getMonthAbbr() + ' ' + first.getDate() + ' - ' + last.getDate() + ' ' + last.getFullYear();
            } else {
                str = first.getMonthAbbr() + ' ' + first.getDate() + ' - ' + last.getMonthAbbr() + ' ' + last.getDate() + ' ' + last.getFullYear();
            }
            break;
        case 'month':
            var firstDay = new Date(this.displaying.getFullYear(), this.displaying.getMonth(), 1);
            var lastDay = new Date(this.displaying.getFullYear(), this.displaying.getMonth() + 1, 0)
            this.range[0] =  firstDay.getFirstDayOfWeek();
            this.range[1] = lastDay.getLastDayOfWeek();

            if(
                (firstDay.getDay() > 4 && firstDay.getDate() == 31)
                || (firstDay.getDay() > 5)
            ) {
                this._drawGrid({rows: 6, cols: 7, rowHeight: '16.666%'});
            } else {
                this._drawGrid({rows: 5, cols: 7, rowHeight: '20%'});
            }
        
            
            str = date.getFullMonth() + ' ' + date.getFullYear();
            break;

        }
        
        $('#displayingDate').text(str);
        

        this._drawHeaders(view);
        this._drawDecorators(view);
        
        if(!this._eventModel.hasMonth(this.displaying)) {
            var that = this;
            this._eventModel.fetchMonth(this.displaying, function(){ that._drawEvents(view); } );
        } else {
            this._drawEvents(view);
        }
    },

    _drawEvents: function(view) { 
        //$('#calendar-viewport').scrollTop(300);
        
        var view = this.getView();
        switch(view) {
        case 'day':
            var events = this._eventModel.getDate(this.displaying);
            for(var i in events) {
                var node = this.options.dayEventDecorator.apply(this, [events[i]]);

                //if(events[i].all_day) {
                //    $('#calendar .colTitles .dayHeader .events').appendEvent(node);
                //} else {
                    $('#cal-day-'+ events[i].start.format('Ymd')).appendEvent(node);
                //}
            };

            break;
        case 'week':
            var events = this._eventModel.getRange(this.range[0], this.range[1]);
            for(var i in events) {
                var node = this.options.weekEventDecorator.apply(this, [events[i]]);
                
                //if(events[i].all_day) {
                //    $('#calendar .colTitles .dayHeader:eq('+d+') .events').appendEvent(node);
                //} else {
                    $('#cal-day-' + events[i].start.format('Ymd')).appendEvent(node);
                //}
            }
            
            break;
        case 'month':
            this.options.drawMonthEventsCallback.apply(this);
            
            break;
        }
        
        this.trigger('eventsDrawn', [this]);
    },
    
    _drawMonthEvents: function() {
        var events = this._eventModel.getRange(this.range[0], this.range[1]);
        for(var i in events) {
            var node = this.options.monthEventDecorator.apply(this, [events[i]]);    
            node.appendTo($('#cal-day-' + events[i].start.format('Ymd')));
        }
    },
    
    _drawDecorators: function(view) {
        this.eventContainer.empty();
        this.decoratorContainer.empty();
        
        switch(view) {
        
        case 'day':
            var t = this.displaying.format('Ymd');
            var wD = $(document.createElement('div')).attr({
                id: 'cal-day-' + t,
                'class': 'day' + ((t == this.now.format('Ymd'))? ' today' : '')
            }).data('date', this.displaying.clone());
            
            wD.appendTo(this.eventContainer);
            
            if(this.displaying.toLocaleString() == this.now.toLocaleString()) {
                var top = ((this.now.getHours() * 60) + this.now.getMinutes()) * (this.options.hourHeight/60);
                var curTime = $(document.createElement('div')).attr({
                    'id': 'rightNow'
                }).css({
                    'position': 'absolute', 'top': top + 'px', 'left': (c * 14.2) + '%', 'height': '2px', 'width': '100%'
                });
                curTime.appendTo(this.decoratorContainer);
            }
            break;
        
        case 'week':
            var d = this.displaying.clone();
            d = d.getFirstDayOfWeek();
            
            var n = this.now.format('Ymd');
            
            for(var i=0; i<(this.grid.cols); i++) {
                var t = d.format('Ymd');
                var wD = $(document.createElement('div')).attr({
                    id: 'cal-day-' + t,
                    'class': 'dayOfWeek' + ((t == n)? ' today' : '')
                }).css({height: this.calendarContainer.height()
                }).data('date', d.clone());
                
                wD.appendTo(this.eventContainer);
                d.setDate(d.getDate() + 1);  
            }
            
            if(this.now <= this.range[1] && this.now >= this.range[0]) {
                var c = this.now.getDay();               
                var top = ((this.now.getHours() * 60) + this.now.getMinutes()) * (this.options.hourHeight/60);
                var curTime = $(document.createElement('div')).attr({
                    'id': 'rightNow'
                }).css({
                    position: 'absolute', top: top + 'px', left: (c * 14.2) + '%', height: '2px', width: '14.2%' 
                });
                curTime.appendTo(this.decoratorContainer);
            }
            
            break;

        case 'month':
            var d = this.displaying.clone();

            d.setDate(1);
            d = d.getFirstDayOfWeek();
            
            var that = this;
            
            for(var i=0; i<(this.grid.rows * 7); i++) {
                var h = $('<div class="dayHeader">' + d.getDate() + '</div>');
                if(this.options.clickableMonthDays)
                    h.click(function(){ that.view($(this).parent().data('date'), 'day'); return false;})
                
                var mD = $(document.createElement('div')).attr({
                    id: 'cal-day-' + d.format('Ymd'),
                    'class': 'dayOfMonth'
                }).css({ 'height': (this.grid.rowHeight) + this.grid.rowHeightUnit
                }).data('date', d.clone())
                .append(h);

                if(d.getMonth() != this.displaying.getMonth()) {
                    mD.addClass('dayNotInMonth');
                }

                if(d.getMonth() == this.now.getMonth() && d.getDate() == this.now.getDate()) {
                    mD.addClass('today');
                }

                mD.appendTo(this.eventContainer);
                d.setDate(d.getDate() + 1);                
            }

            d = null;
            break;
        }

    },

    _drawHeaders: function(view) {
        var rowTitles = $('.rowTitles', this.target);
        var colTitles = $('.colTitles', this.target);

        colTitles.empty();

        switch(view) {
        case 'week':
            rowTitles.show();
            colTitles.show();

            var d = this.displaying.getFirstDayOfWeek();

            for(var i=0; i<7; i++){
                var cH = $(document.createElement('div')).attr({
                    'class': 'dayHeader' + ((d.toLocaleDateString() == this.now.toLocaleDateString())? ' today' : '')
                })
                .html('<div class="title">' + d.format(this.options.weekHeaderFormat) + '</div><div class="events"></div>')
                .data('date', d.clone());
                
                cH.appendTo(colTitles);
                d.setDate(d.getDate() + 1);
            }

            break;
        case 'day':
            var cH = $(document.createElement('div')).attr({
                'class': 'dayHeader' + ((this.displaying.toLocaleDateString() == this.now.toLocaleDateString())? ' today' : '')
            })
            .html('<div class="title">' + this.displaying.format(this.options.dayHeaderFormat) + '</div><div class="events"></div>')
            .data('date', this.displaying.clone());
            
            cH.appendTo(colTitles);


            rowTitles.show();
            colTitles.show();
            break;
        case 'month':
            for(var i=0; i<7; i++){
                var cH = $(document.createElement('div')).attr({
                    'class': 'dayHeader'
                }).html('<div class="title">' + Date.DAYNAMES[i] + '</div>');
                cH.appendTo(colTitles);
            }
            rowTitles.hide();
            colTitles.show();
            break;
        }        
    },

    _drawGrid: function(o) {
        /*
         * o = {rows: 5, cols: 7, rowHeight: 20%}
         */
        o.rowHeightUnit = o.rowHeight.replace(/[0-9\.]+/, '');
        o.rowHeight = o.rowHeight.replace(/[^0-9\.]+/, '');
        this.grid = o;

        this.gridContainer.empty();

        for (var i=0; i<o.rows; i++) {
            var gridY = $(document.createElement('div')).attr({
                'id': 'y-' + i,
                'class': 'gridy' + ((i%2)? ' alt' : '')
            }).css({ top: (i * o.rowHeight) + o.rowHeightUnit });
            
            gridY.appendTo(this.gridContainer);
        }     

        
        var gridXHeight = (this.options.hourHeight * 24) + 'px';
        for (var i=0; i<o.cols; i++) {
            var gridX = $(document.createElement('div')).attr({
                'id': 'x-' + i,
                'class': 'gridx' + ((i%2)? ' alt' : '')
            }).css({ left: (i * 14.2) + '%', height: gridXHeight});
            
            gridX.appendTo(this.gridContainer);
        }
        

    },

    /*
     * Eventually we will support arbitrary ranges, rather than set week or month.
     * 
     * So, like gCal, you'll be able to say, look at 3 days, or 8 days, or 23 days etc....
     */
    viewRange: function(startDay, endDay) {},

    view: function(date, view) {
        view = view || this.getView();

        if(date == null)
            date = this.displaying;

        this.render(date, view);

        this.trigger('change', [this]);
    },

    next: function() {
        var d = this.displaying.clone();

        switch(this.getView()) {
        case 'day':
            d.setDate(d.getDate() + 1);
            break;
        case 'week':
            d.setDate(d.getDate() + 7);
            break;
        case 'month':
            d.setMonth(d.getMonth() + 1);
            break;
        }

        this.view(d);
    },

    prev: function() {
        var d = this.displaying.clone();

        switch(this.getView()) {
        case 'day':
            d.setDate(d.getDate() - 1);
            break;
        case 'week':
            d.setDate(d.getDate() - 7);
            break;
        case 'month':
            d.setMonth(d.getMonth() - 1);
            break;
        }

        this.view(d);
    },

    today: function(){
        this.view(new Date());
    }


});