Countdown = Class.create();
Object.extend(Countdown.prototype, {

    options: null,

    endtime: null,

    counterFrame: null,
    daysCounter: null,
    hoursCounter: null,
    minutesCounter: null,
    secondsCounter: null,
    
    finished: false,

	initialize: function(endtime, options) {
        
        this.endtime = endtime;
        this.options = options || {};
        
        var defaultRenderingStrategy = TextCounterStrategy;
        
        if (!this.options.daysCounter) {
            this.daysCounter = new Counter(0, 364, 'Days', new defaultRenderingStrategy());
        } else {
            this.daysCounter = this.options.daysCounter;
        }
        
        if (!this.options.hoursCounter) {
            this.hoursCounter = new Counter(0, 23, 'Hours', new defaultRenderingStrategy());
        } else {
            this.hoursCounter = this.options.hoursCounter;
        }
        
        if (!this.options.minutesCounter) {
            this.minutesCounter = new Counter(0, 59, 'Minutes', new defaultRenderingStrategy());
        } else {
            this.minutesCounter = this.options.minutesCounter;
        }
        
        if (!this.options.secondsCounter) {
            this.secondsCounter = new Counter(0, 59, 'Seconds', new defaultRenderingStrategy())
        } else {
            this.secondsCounter = this.options.secondsCounter;
        }
        
        var status = this.getStatus();
        
        if (!status.finished) {
            this.counterFrame = $(document.createElement('div'));
            this.counterFrame.addClassName('countdown');
            
            this.daysCounter.setCurrent(status.days);
            this.counterFrame.appendChild(this.daysCounter.getPanel());
            
            this.hoursCounter.setCurrent(status.hours);
            this.counterFrame.appendChild(this.hoursCounter.getPanel());
            
            this.minutesCounter.setCurrent(status.minutes);
            this.counterFrame.appendChild(this.minutesCounter.getPanel());
            
            this.secondsCounter.setCurrent(status.seconds);
            this.counterFrame.appendChild(this.secondsCounter.getPanel());
            
            this.updater = setInterval(this.update.bind(this), 1000);
        } else {
            this.finish();
        }
    },
    
    
    isFinished: function() {
        return this.finished;
    },
    
    finish: function() {
        clearInterval(this.updater);
        this.finished = true;
        
        this.secondsCounter.setCurrent(0);
        this.minutesCounter.setCurrent(0);
        this.hoursCounter.setCurrent(0);
        this.daysCounter.setCurrent(0);
        
        if (typeof(this.options.afterFinish) == 'function') {
            this.options.afterFinish();
        }
    },
    
    update: function() {
        var status = this.getStatus();

        if (status.finished) {
            this.finish();
            return;
        }

        this.secondsCounter.setCurrent(status.seconds);
        this.minutesCounter.setCurrent(status.minutes);
        this.hoursCounter.setCurrent(status.hours);
        this.daysCounter.setCurrent(status.days);
    },
    
    getStatus: function() {
        
        var now = new Date();
        var difference = new Date();
        difference.setTime(Math.abs(this.endtime.getTime() - now.getTime()));
        
        var timeDifference = difference.getTime();
        
        var days = Math.floor(timeDifference / (1000 * 60 * 60 * 24));
        timeDifference -= days * (1000 * 60 * 60 * 24);
        var hours = Math.floor(timeDifference / (1000 * 60 * 60)); 
        timeDifference -= hours * (1000 * 60 * 60);
        var minutes = Math.floor(timeDifference / (1000 * 60)); 
        timeDifference -= minutes * (1000 * 60);
        var seconds = Math.floor(timeDifference / 1000); 
        timeDifference -= seconds * 1000;
        
        var finished = now >= this.endtime;
        
        return {finished: finished, days: days, hours: hours, minutes: minutes, seconds: seconds};
    },
    
    toString: function() {
        return this.daysCounter.current + "D " + 
            this.hoursCounter.current + "H " + 
            this.minutesCounter.current + "M " +
            this.secondsCounter.current + "S" + 
            " until " + this.endtime;
    }
    
});

Counter = Class.create();
Object.extend(Counter.prototype, {
    
    min: 0,
    max: 0,
    current: 0,
    panel: 0,
    
    renderStrategy: null,
    
    initialize: function(min, max, unit, renderStrategy) {
        this.min = min;
        this.max = max;
        this.unit = unit;
        this.renderStrategy = renderStrategy;
    },
    
    setCurrent: function(current) {
        
        if (current < this.min) {
            this.current = this.min;
        } else if (current > this.max) {
            this.current = this.max;
        } else {
            this.current = current;
        }
        
        this.render();
    },
    
    getPanel: function() {
        return this.renderStrategy.getPanel();
    },
    
    render: function() {
        this.renderStrategy.render(this.current, this.unit);
    }
    
})

ImageCounterStrategy = Class.create();
Object.extend(ImageCounterStrategy.prototype, {
    
    options: null,
    panel: null,

    initialize: function(options) {
        this.options = options || {};
        this.panel = document.createElement('img');
        this.preload();
    },
    
    getPanel: function() {
        return this.panel;
    },
    
    preload: function() {
        
        var image = new Image();
        
        var singleDigits = [];
        singleDigits.push(this.options.singleDigitPath);
        
        singleDigits.each(function(path) {
          for (var i = 0; i <= 9; i++) {
            image.src = path.replace(/#{VALUE}/, i);
          }
        })
        
        
        var doubleDigits = [];
        doubleDigits.push(this.options.doubleDigitsPath);
        
        doubleDigits.each(function(path) {
          for (var i = 0; i <= 59; i++) {
            image.src = path.replace(/#{VALUE}/, i);
          }
        })
        
        image = null;
    },
    
    render: function(value, unit) {
        var imagePath = this.options.doubleDigitsPath;
        if (value < 10) {
            imagePath = this.options.singleDigitPath;
        }
        this.panel.src = imagePath.replace(/#{VALUE}/, value);
        this.panel.setAttribute('alt', value + " " + unit);
    }

});

DigitImageCounterStrategy = Class.create();
Object.extend(DigitImageCounterStrategy.prototype, {
    
    options: null,
    panel: null,

    initialize: function(options) {
        this.options = options || {};
        this.panel = document.createElement('div');
        
        this.tens = $(document.createElement('img'));
        this.panel.appendChild(this.tens);
        
        this.ones = $(document.createElement('img'));
        this.panel.appendChild(this.ones);
        
        this.preload();
    },
    
    getPanel: function() {
        return this.panel;
    },
    
    preload: function() {
        
        var image = new Image();
        
        var singleDigits = [];
        singleDigits.push(this.options.singleDigitPath);
        
        singleDigits.each(function(path) {
          for (var i = 0; i <= 9; i++) {
            image.src = path.replace(/#{VALUE}/, i);
          }
        })
        
        image = null;
    },
    
    render: function(value, unit) {
        
        var imagePath = this.options.singleDigitPath;
        
        var noLeadingZero = (typeof(this.options.leadingZero) == 'undefined' || !this.options.leadingZero);
        
        if (value < 10 && noLeadingZero) {
            this.tens.hide();
        } else {
            this.tens.show();
        }
        
        this.tens.src = imagePath.replace(/#{VALUE}/, Math.floor(value/10));
        
        this.ones.src = imagePath.replace(/#{VALUE}/, value%10);
        this.ones.setAttribute('alt', value + " " + unit);
    }

});

ImageFlipCounterStrategy = Class.create();
Object.extend(ImageFlipCounterStrategy.prototype, ImageCounterStrategy.prototype);
Object.extend(ImageFlipCounterStrategy.prototype, {
    
    options: null,
    panel: null,
    
    currentFlap: null,
    nextFlap: null,
    
    currentSrc: null,
    
    initialize: function(options) {
        
        throw "No fully implemented."
        
        this.options = options || {};
        this.panel = document.createElement('span');
        
        this.currentFlap = $(document.createElement('img'));
        this.panel.appendChild(this.currentFlap);
        
        this.nextFlap = $(document.createElement('img'));
        this.panel.appendChild(this.nextFlap);
        
        this.preload();
    },
    
    render: function(value, unit) {
        
        var imagePath = this.options.doubleDigitsPath;
        if (value < 10) {
            imagePath = this.options.singleDigitPath;
        }
        
        this.currentSrc = imagePath.replace(/#{VALUE}/, value);
        this.currentFlap.src = this.currentSrc;
        this.currentFlap.setStyle({zIndex: 10, width: '100%'});
        this.currentFlap.setAttribute('alt', value + " " + unit);
        
        this.flip();
    },
    
    flip: function() {
        new Effect.BlindDown(this.currentFlap, {
            duration: 0.15,
            afterFinish: function() {
                this.nextFlap.setStyle({zIndex: 5})
                this.nextFlap.src = this.currentSrc;
            }.bind(this)
        })
    }

});

TextCounterStrategy = Class.create();
Object.extend(TextCounterStrategy.prototype, {
    
    options: null,
    panel: null,

    initialize: function(options) {
        this.options = options || {};
        this.panel = document.createElement('div');
    },
    
    getPanel: function() {
        return this.panel;
    },
    
    render: function(value, unit) {
        
        var output = value;
        
        if (this.options.leadingZero && value < 10) {
            output = "0" + value;
        }
        
        this.panel.innerHTML = output;
    }

});

AnimatedCounterStrategy = Class.create();
Object.extend(AnimatedCounterStrategy.prototype, {
    
    options: null,
    panel: null,
    
    previousOutput: null,

    initialize: function(options) {
        this.options = options | {};
        this.panel = document.createElement('div');
        
        this.topFlap = $(document.createElement('div'));
        this.topFlap.addClassName('top-flap');
        this.panel.appendChild(this.topFlap);
        
        this.bottomFlap = $(document.createElement('div'));
        this.bottomFlap.addClassName('bottom-flap');
        this.panel.appendChild(this.bottomFlap);

        this.flap = $(document.createElement('div'));
        this.flap.addClassName('flap');
        this.flap.hide();
        this.panel.appendChild(this.flap);
    },
    
    getPanel: function() {
        return this.panel;
    },
    
    render: function(value, unit) {
        
        var output = value;
        
        if (this.options.leadingZero && value < 10) {
            output = "0" + value;
        }
        
        this.topFlap.innerHTML = output;
        this.bottomFlap.innerHTML = this.previousOutput || output;
        
        this.previousOutput = output;
        
        this.flip();
    },
    
    flip: function() {
        new Effect.BlindDown(this.flap, {
            duration: 0.1,
            afterFinish: function() {
                this.flap.hide();
                this.bottomFlap.innerHTML = this.previousOutput;
            }.bind(this)
        })
    }

});
