$.extend(Function.prototype, {
    use: function() {
        var method = this, args = Array.prototype.slice.call(arguments), object = args.shift();
        return function() {
            return method.apply(object, args.concat(Array.prototype.slice.call(arguments)));
        }
    },
    useEL: function() {
        var method = this, args = Array.prototype.slice.call(arguments), object = args.shift();
        return function(event) {
            return method.apply(object, [event || window.event].concat(Array.prototype.slice.call(arguments)));
        }
    }
});

var Weather = function(el, conditions, wind, sunPosition, moonPhase) {
    this.$el = el;
    this.el = el[0]; // report the actual element, not jquery
    
    this.wind = wind;
    this.ctx = this.el.getContext('2d');
    
    this.setSize();
    
    this.conditions = {};
    
    if(this.wind.direction >= 0 && this.wind.direction <= 180) {
        this.wind.direction = -1;
    } else if(this.wind.direction > 180 && this.wind.direction <= 360) {
        this.wind.direction = 1;
    }
    
    if($.inArray('drizzle', conditions) != -1) {
        this.conditions.rain = new Rain(this.ctx, this.width, this.height, 20, this.wind);
        this.conditions.clouds = new Clouds(this.ctx, this.width, this.height, 3, this.wind);
    }

    if($.inArray('showers', conditions) != -1) {
        this.conditions.rain = new Rain(this.ctx, this.width, this.height, 100, this.wind);
        this.conditions.clouds = new Clouds(this.ctx, this.width, this.height, 3, this.wind);
    }

    if($.inArray('lightning', conditions) != -1) {
        this.conditions.lightning = new Lightning(this.ctx, this.width, this.height);
        this.conditions.clouds = new Clouds(this.ctx, this.width, this.height, 3, this.wind);
    }
    
    if($.inArray('flurries', conditions) != -1) {
        this.conditions.snow = new Snow(this.ctx, this.width, this.height, 20, this.wind);
        this.conditions.clouds = new Clouds(this.ctx, this.width, this.height, 3, this.wind);
    }

    if($.inArray('snow', conditions) != -1) {
        this.conditions.snow = new Snow(this.ctx, this.width, this.height, 100, this.wind);
        this.conditions.clouds = new Clouds(this.ctx, this.width, this.height, 3, this.wind);
    }

    if($.inArray('heavy_snow', conditions) != -1) {
        this.conditions.snow = new Snow(this.ctx, this.width, this.height, 200, this.wind);
        this.conditions.clouds = new Clouds(this.ctx, this.width, this.height, 3, this.wind);
    }
    
    if($.inArray('hail', conditions) != -1) {
        this.conditions.hail = new Hail(this.ctx, this.width, this.height, 40, this.wind);
        this.conditions.clouds = new Clouds(this.ctx, this.width, this.height, 3, this.wind);
    }
 
    if($.inArray('sleet', conditions) != -1) {
        this.conditions.sleet = new Sleet(this.ctx, this.width, this.height, 120, this.wind);
        this.conditions.clouds = new Clouds(this.ctx, this.width, this.height, 3, this.wind);
    }
 
    if($.inArray('mostly_cloudy', conditions) != -1 || $.inArray('mostly_cloudy_day', conditions) != -1) {
        this.conditions.clouds = new Clouds(this.ctx, this.width, this.height, 2, this.wind);
    }
    
    // set moon position opposite of sun position
    var moonPosition = 1 - (-1 * sunPosition);
    
    if($.inArray('cloudy', conditions) != -1) {
        if(sunPosition >= 0 && sunPosition <= 1) {
            this.conditions.sun = new Sun(this.ctx, this.width, this.height, sunPosition);
        } else {
            this.conditions.moon = new Moon(this.ctx, this.width, this.height, moonPosition, moonPhase);
        }
        this.conditions.clouds = new Clouds(this.ctx, this.width, this.height, 1, this.wind);
    } else if(!this.conditions.clouds) {
        if(sunPosition >= 0 && sunPosition <= 1) {
            this.conditions.sun = new Sun(this.ctx, this.width, this.height, sunPosition);
        } else {
            this.conditions.moon = new Moon(this.ctx, this.width, this.height, moonPosition, moonPhase);
        }
        this.conditions.clouds = new Clouds(this.ctx, this.width, this.height, 0, this.wind);
    }

    this.start();
    
    $(window).bind('resize', this.setSize.useEL(this));
};
$.extend(Weather, {
    fps: 29
});
$.extend(Weather.prototype, {
    setSize: function() {
        this.width = (!window.innerWidth) ? document.documentElement.offsetWidth : window.innerWidth;
        this.height = (!window.innerHeight) ? document.documentElement.offsetHeight : window.innerHeight;
        this.$el.attr('width', this.width);
        this.$el.attr('height', this.height);
        
        if(this.conditions) {
            for(var i in this.conditions) {
                this.conditions[i].setSize(this.width, this.height);
            }
        }
    },
    start: function() {
        this.timer = setInterval(function() {
            this.ctx.clearRect(0, 0, this.width, this.height);
            
            for(var i in this.conditions) {
                this.conditions[i].draw();
            }
        }.use(this), (1000 / Weather.fps));
    },
    stop: function() {
        clearInterval(this.timer);
    }
});

var Sun = function(ctx, width, height, position) {
    this.ctx = ctx;
    this.angle = position * 180;
    
    this.circleEndAngle = Math.PI * 2;
    
    this.setSize(width, height);
};
$.extend(Sun, {
    radius: 35,
    glowRadius: 70,
    edgePadding: 50
});
$.extend(Sun.prototype, {
    draw: function() {
        var ctx = this.ctx;
        
        // temporarily move the canvas origin to the bottom-center of the window
        ctx.save();
        ctx.translate(this.width / 2, this.height);
        
        // Outer glow
        var radgrad = ctx.createRadialGradient(this.x, this.y, Sun.radius, this.x, this.y, Sun.glowRadius);
        radgrad.addColorStop(0, 'rgba(255,278,0,0.3)');
        radgrad.addColorStop(1, 'rgba(255,278,0,0)');
        
        ctx.beginPath();
        ctx.fillStyle = radgrad;
        ctx.fillRect(this.x - Sun.glowRadius, this.y - Sun.glowRadius, Sun.glowRadius * 2, Sun.glowRadius * 2);
        
        // Center
        ctx.fillStyle = 'rgba(255,278,0,1)';
        ctx.arc(this.x, this.y, Sun.radius, 0, this.circleEndAngle, false);
        ctx.fill();
        
        // restore the canvas back to 0,0
        ctx.restore();
    },
    setSize: function(width, height) {
        this.width = width; 
        this.height = height; 
        
        var a = (this.width / 2);
        var b = this.height;
        var theta = this.angle * (Math.PI / 180);
        this.x = a * Math.cos(theta);
        this.x = (this.x > 0) ? this.x - Sun.edgePadding : this.x + Sun.edgePadding;
        this.y = -(b * Math.sin(theta)) + Sun.edgePadding;
    }
});

var Moon = function(ctx, width, height, position, phase) {
    this.ctx = ctx;
    this.angle = position * 180;
    this.phase = phase;
    
    this.circleEndAngle = Math.PI * 2;
    
    this.setSize(width, height);
    
    switch(this.phase) {
        case 1:
            this.draw = function() { this._waxingCrescent(); }
        break;
        case 2:
            this.draw = function() { this._firstQuarter(); }
        break;
        case 3:
            this.draw = function() { this._waxingGibbous(); }
        break;
        case 4:
            this.draw = function() { this._fullMoon(); }
        break;
        case 5:
            this.draw = function() { this._waningGibbous(); }
        break;
        case 6:
            this.draw = function() { this._lastQuarter(); }
        break;
        case 7:
            this.draw = function() { this._waningCrescent(); }
        break;
        case 0:
            this.draw = function() { this._newMoon(); }
        break;
    }
};
$.extend(Moon, {
    radius: 50,
    glow: 40,
    edgePadding: 150
});
$.extend(Moon.prototype, {
    _newMoon: function() {
        var ctx = this.ctx;
        ctx.save();
        ctx.translate(this.width / 2, this.height - 100);
        
        var glowRadius = Moon.radius + Moon.glow;
        
        // Outer glow
        var radgrad = ctx.createRadialGradient(this.x, this.y, Moon.radius, this.x, this.y, glowRadius);
        radgrad.addColorStop(0, 'rgba(255,255,255,0.05)');
        radgrad.addColorStop(1, 'rgba(255,255,255,0)');

        ctx.beginPath();
        ctx.fillStyle = radgrad;
        ctx.fillRect(this.x - glowRadius, this.y - glowRadius, glowRadius * 2, glowRadius * 2);
        
        ctx.globalCompositeOperation = 'destination-out';
    
        // Center
        ctx.fillStyle = 'rgba(255,255,255,1)';
        ctx.arc(this.x, this.y, Moon.radius, 0, this.circleEndAngle, false);
        ctx.fill();
 
        ctx.globalCompositeOperation = 'source-over';       
   
        this._endDraw();
    },
    _waxingCrescent: function() {
        var ctx = this.ctx;
        
        this._startDraw();
        
        ctx.beginPath();
        ctx.fillStyle = 'rgba(255,255,255,1)';
        ctx.moveTo(this.x, this.y); // center
        ctx.arc(this.x, this.y, Moon.radius, Math.PI * 1.5, Math.PI * 0.5, false);
        
        ctx.moveTo(this.x, this.y + Moon.radius); // bottom center
        ctx.bezierCurveTo(this.x + (Moon.radius / 1.2), this.y + Moon.radius, this.x + (Moon.radius / 1.2), this.y - Moon.radius, this.x, this.y - Moon.radius);
        ctx.fill();
        
        this._endDraw();
    },
    _firstQuarter: function() {
        var ctx = this.ctx;
        
        this._startDraw();
        
        ctx.beginPath();
        ctx.fillStyle = 'rgba(255,255,255,1)';
        ctx.moveTo(this.x, this.y); // top center
        ctx.arc(this.x, this.y, Moon.radius, Math.PI * 1.5, Math.PI * 0.5, false);
        ctx.fill();
        
        ctx.moveTo(this.x, this.y + Moon.radius); // bottom center
        ctx.bezierCurveTo(this.x - (Moon.radius / 4), this.y + Moon.radius, this.x - (Moon.radius / 4), this.y - Moon.radius, this.x, this.y - Moon.radius);
        ctx.fill();
 
        this._endDraw();
    },
    _waxingGibbous: function() {
        var ctx = this.ctx;
        
        this._startDraw();
        
        ctx.fillStyle = 'rgba(255,255,255,1)';
        ctx.moveTo(this.x, this.y); // center
        ctx.arc(this.x, this.y, Moon.radius, Math.PI * 1.5, Math.PI * 0.5, false);
        ctx.fill();
        
        ctx.moveTo(this.x, this.y + Moon.radius); // bottom center
        ctx.bezierCurveTo(this.x - (Moon.radius / 1.2), this.y + Moon.radius, this.x - (Moon.radius / 1.2), this.y - Moon.radius, this.x, this.y - Moon.radius);
        ctx.fill();
        
        this._endDraw();
    },
    _fullMoon: function() {
        var ctx = this.ctx;
        
        this._startDraw();
                
        // Center
        ctx.beginPath();
        ctx.fillStyle = 'rgba(255,255,255,1)';
        ctx.arc(this.x, this.y, Moon.radius, 0, this.circleEndAngle, false);
        ctx.fill();
 
        this._endDraw();
    },
    _waningGibbous: function() {
        var ctx = this.ctx;
        
        this._startDraw();
                
        ctx.beginPath();
        ctx.fillStyle = 'rgba(255,255,255,1)';
        ctx.moveTo(this.x, this.y); // center
        ctx.arc(this.x, this.y, Moon.radius, Math.PI * 1.5, Math.PI * 0.5, true);
        
        ctx.moveTo(this.x, this.y + Moon.radius); // bottom center
        ctx.bezierCurveTo(this.x + (Moon.radius / 1.2), this.y + Moon.radius, this.x + (Moon.radius / 1.2), this.y - Moon.radius, this.x, this.y - Moon.radius);
        ctx.fill();

        this._endDraw();
    },
    _lastQuarter: function() {
        var ctx = this.ctx;
        
        this._startDraw();
                
        ctx.beginPath();
        ctx.fillStyle = 'rgba(255,255,255,1)';
        ctx.moveTo(this.x, this.y); // top center
        ctx.arc(this.x, this.y, Moon.radius, Math.PI * 1.5, Math.PI * 0.5, true);
        ctx.fill();
        
        ctx.moveTo(this.x, this.y + Moon.radius); // bottom center
        ctx.bezierCurveTo(this.x + (Moon.radius / 4), this.y + Moon.radius, this.x + (Moon.radius / 4), this.y - Moon.radius, this.x, this.y - Moon.radius);
        ctx.fill();

        this._endDraw();
    },
    _waningCrescent: function() {
        var ctx = this.ctx;
        
        this._startDraw();
        
        ctx.beginPath();
        ctx.fillStyle = 'rgba(255,255,255,1)';
        ctx.moveTo(this.x, this.y); // center
        ctx.arc(this.x, this.y, Moon.radius, Math.PI * 1.5, Math.PI * 0.5, true);
        
        ctx.moveTo(this.x, this.y + Moon.radius); // bottom center
        ctx.bezierCurveTo(this.x - (Moon.radius / 1.2), this.y + Moon.radius, this.x - (Moon.radius / 1.2), this.y - Moon.radius, this.x, this.y - Moon.radius);
        ctx.fill();
        
        this._endDraw();
    },
    _startDraw: function() {
        var ctx = this.ctx;
 
        ctx.save();
        ctx.translate(this.width / 2, this.height);
 
        ctx.shadowOffsetX = 0;
        ctx.shadowOffsetY = 0;
        ctx.shadowBlur = Moon.glow;
        ctx.shadowColor = "rgba(255,255,255,0.7)";
    },
    _endDraw: function() {
        var ctx = this.ctx;

        ctx.shadowBlur = 0;

        ctx.restore();
    },
    setSize: function(width, height) {
        this.width = width; 
        this.height = height; 
        
        var a = (this.width / 2);
        var b = this.height;
        var theta = this.angle * (Math.PI / 180);
        this.x = a * Math.cos(theta);
        this.x = (this.x > 0) ? this.x - Moon.edgePadding : this.x + Moon.edgePadding;
        this.y = -(b * Math.sin(theta)) + Moon.edgePadding;
    }
});

var Clouds = function(ctx, width, height, amount, wind) {
    this.ctx = ctx;
    this.width = width; 
    this.height = height; 
    this.amount = amount;
    this.wind = wind;
    
    this.circleEndAngle = Math.PI * 2;
    this.velocity = ((this.wind.speed * 4) / Weather.fps) * this.wind.direction;
    
    this.clouds = [];
    switch(this.amount) {
        case 1: 
            this.total = 5;
        break;
        case 2:
            this.total = 8;
        break;
        case 3:
            this.total = 20;
        break;
        default:
            this.total = 3;
        break;
    }
    
    var i = this.total;
    while(i--) {
        this.clouds.push({
            x: (i - 1) * ((this.width + (this.width / this.total)) / this.total),
            y: Math.round(Math.random() * Clouds.yMax) - (Clouds.yMax / 2),
            type: Math.floor(Math.random() * 5)
        });
    }
};
$.extend(Clouds, {
    yMax: 40,
    color: 'rgba(255,255,255,0.95)'
});
$.extend(Clouds.prototype, {
    draw: function() {
        var i = this.clouds.length;

        while(i--) {
            if(this.clouds[i].width) {
                if(this.clouds[i].x > -this.clouds[i].width && this.clouds[i].x < this.width) {
                    this.clouds[i].x += this.velocity;
                } else if(this.wind.direction >= 0) {
                    this.clouds[i].x = -this.clouds[i].width + 1;
                } else {
                    this.clouds[i].x = this.width - 1;
                }
            }
            
            switch(this.clouds[i].type) {
                case 0:
                    this._draw0(this.clouds[i]);
                break;
                case 1:
                    this._draw1(this.clouds[i]);
                break;
                case 2:
                    this._draw2(this.clouds[i]);
                break;
                case 3:
                    this._draw3(this.clouds[i]);
                break;
                case 4:
                    this._draw4(this.clouds[i]);
                break;
            }
        }
    },
    _draw0: function(vars) {
        var ctx = this.ctx;
        
        ctx.fillStyle = Clouds.color;
        
        ctx.beginPath();
        ctx.arc(vars.x, vars.y + 25, 28, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 33, vars.y, 32, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 38, vars.y + 30, 28, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 80, vars.y + 25, 32, 0, this.circleEndAngle, false);
        ctx.fill();
 
        ctx.beginPath();
        ctx.arc(vars.x + 120, vars.y + 35, 20, 0, this.circleEndAngle, false);
        ctx.fill();
        
        vars.width = 140;
    },
    _draw1: function(vars) {
        var ctx = this.ctx;
        
        ctx.fillStyle = Clouds.color;
     
        ctx.beginPath();
        ctx.arc(vars.x, vars.y, 40, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 50, vars.y - 18, 35, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 43, vars.y + 30, 28, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 90, vars.y + 10, 30, 0, this.circleEndAngle, false);
        ctx.fill();
 
        ctx.beginPath();
        ctx.arc(vars.x + 120, vars.y + 10, 15, 0, this.circleEndAngle, false);
        ctx.fill();

        ctx.beginPath();
        ctx.arc(vars.x + 130, vars.y + 27, 15, 0, this.circleEndAngle, false);
        ctx.fill();
        
        vars.width = 145;
    },
    _draw2: function(vars) {
        var ctx = this.ctx;
        
        ctx.fillStyle = Clouds.color;
   
        ctx.beginPath();
        ctx.arc(vars.x + 60, vars.y + 16, 32, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 10, vars.y + 45, 20, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 50, vars.y + 60, 32, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 100, vars.y + 48, 25, 0, this.circleEndAngle, false);
        ctx.fill();
 
        ctx.beginPath();
        ctx.arc(vars.x + 100, vars.y + 20, 20, 0, this.circleEndAngle, false);
        ctx.fill();

        ctx.beginPath();
        ctx.arc(vars.x + 120, vars.y + 30, 15, 0, this.circleEndAngle, false);
        ctx.fill();

        ctx.beginPath();
        ctx.arc(vars.x + 150, vars.y + 55, 38, 0, this.circleEndAngle, false);
        ctx.fill();

        ctx.beginPath();
        ctx.arc(vars.x + 185, vars.y + 45, 23, 0, this.circleEndAngle, false);
        ctx.fill();
 
        ctx.beginPath();
        ctx.arc(vars.x + 195, vars.y + 65, 23, 0, this.circleEndAngle, false);
        ctx.fill();
        
        vars.width = 218;
    },
    _draw3: function(vars) {
        var ctx = this.ctx;
        
        ctx.fillStyle = Clouds.color;
    
        ctx.beginPath();
        ctx.arc(vars.x + 40, vars.y + 76, 46, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 150, vars.y + 21, 42, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 198, vars.y + 90, 48, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 100, vars.y + 90, 27, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 138, vars.y + 80, 29, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 75, vars.y + 46, 36, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 95, vars.y + 46, 36, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 95, vars.y + 10, 15, 0, this.circleEndAngle, false);
        ctx.fill();
        
        vars.width = 246;
    },
    _draw4: function(vars) {
        var ctx = this.ctx;
        
        ctx.fillStyle = Clouds.color;

        ctx.beginPath();
        ctx.arc(vars.x + 58, vars.y + 30, 30, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 20, vars.y + 52, 20, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 47, vars.y + 82, 34, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 95, vars.y + 63, 33, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 97, vars.y + 32, 18, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 146, vars.y + 75, 36, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 120, vars.y + 40, 16, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 160, vars.y + 40, 18, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 190, vars.y + 48, 23, 0, this.circleEndAngle, false);
        ctx.fill();
        
        ctx.beginPath();
        ctx.arc(vars.x + 195, vars.y + 85, 25, 0, this.circleEndAngle, false);
        ctx.fill();
        
        vars.width = 220;
        
    },
    setSize: function(width, height) {
        this.width = width; 
        this.height = height; 
        
        var i = this.clouds.length;
        this.ctx.fillStyle = Clouds.color;
        while(i--) {
            this.clouds[i].x = i * ((this.width + 100) / this.total) - 50 + this.velocity;
        }
        
    }
});

var Lightning = function(ctx, width, height) {
    this.ctx = ctx;
    this.width = width; 
    this.height = height; 
};
$.extend(Lightning, {
    interval: 4000,
    displayTime: 500,
    bends: 6,
    color: 'rgba(254,245,108,1)'
});
$.extend(Lightning.prototype, {
    draw: function() {
        var timer = new Date();
        var now = timer.valueOf();
        if(!this.start) {
            this.start = now;
        }
                        
        if(now >= this.start + Lightning.interval) {
            this.start = now;
            this.xStart = Math.random() * this.width;
        }
        
        if(now <= this.start + Lightning.displayTime) {
            var yDiff = this.height / Lightning.bends;
            this.ctx.strokeStyle = Lightning.color;
            this.ctx.lineWidth = 8;
            this.ctx.beginPath();
            this.ctx.moveTo(this.xStart + 50, 0);
            for(var i = 1; i <= Lightning.bends; i++) {
                this.ctx.lineTo(this.xStart, (yDiff * i));
                this.ctx.lineTo(this.xStart + 50, (yDiff * i) - 25);
            }
            this.ctx.stroke();
        }
    },
    setSize: function(width, height) {
        this.width = width; 
        this.height = height; 
    }
});

var Rain = function(ctx, width, height, drops, wind) {
    this.ctx = ctx;
    this.width = width; 
    this.height = height; 
    this.wind = wind;
    
    this.xVector = this.wind.speed * 4;
        
    this.drops = [];
    i = drops;
    while(i--) {
        this.drops.push({
            x: Math.round(Math.random() * width),
            y: Math.round(Math.random() * (height + Rain.size)) - Rain.size,
            gravity: (Math.random() * 10) + 50
        });
    }
};
$.extend(Rain, {
    size: 200,
    color: 'rgba(255,255,255,0.3)'
});
$.extend(Rain.prototype, {
    draw: function() {
        var ctx = this.ctx;

        var xStart = this.xVector * this.wind.direction;
        var yStart = Math.sqrt(Math.pow(Rain.size, 2) + Math.pow(xStart, 2));
                
        i = this.drops.length - 1;
        while(i--) {
                        
            if(this.drops[i].x > 0 && this.drops[i].x < this.width) {
                this.drops[i].x += this.wind.speed * this.wind.direction;
            } else if(this.wind.direction >= 0) {
                this.drops[i].x = 1;
            } else {
                this.drops[i].x = this.width - 1;
            }
            
            if(this.drops[i].y <= this.height) {
                this.drops[i].y += this.drops[i].gravity;
            } else {
                this.drops[i].y = -Rain.size;
            }
            
            ctx.fillStyle = Rain.color;
            ctx.beginPath();
            ctx.moveTo(this.drops[i].x, this.drops[i].y);
            ctx.lineTo(this.drops[i].x + xStart, this.drops[i].y + yStart);
            //ctx.lineTo(this.drops[i].x + xPoints.start + 3, this.drops[i].y + yPoints.start + 3);
            ctx.quadraticCurveTo(this.drops[i].x + xStart, this.drops[i].y + yStart + 3, this.drops[i].x + xStart + 3, this.drops[i].y + yStart + 3);
            ctx.fill();
        }
    },
    setSize: function(width, height) {
        this.width = width; 
        this.height = height; 
    }
});


var Hail = function(ctx, width, height, balls, wind) {
    this.ctx = ctx;
    this.width = width; 
    this.height = height; 
    this.wind = wind;

    this.xVector = this.wind.speed * 1;
    this.circleEndAngle = Math.PI * 2;
        
    this.balls = [];
    i = balls;
    while(i--) {
        this.balls.push({
            x: Math.round(Math.random() * width),
            y: Math.round(Math.random() * height),
            radius: Math.round(Math.random() * (Hail.size.max - Hail.size.min) + Hail.size.min),
            gravity: (Math.random() * 10) + 50,
        });
    }
};
$.extend(Hail, {
    size: {
        min: 3,
        max: 5
    },
    color: 'rgba(255,255,255,1)',
    tailColor: 'rgba(255,255,255,0.2)',
    tailLength: 100
});
$.extend(Hail.prototype, {
    draw: function() {
        var ctx = this.ctx;

        var xEnd = this.xVector * this.wind.direction;
        //var yEnd = Math.sqrt(Math.pow(Hail.tailLength, 2) + Math.pow(xEnd, 2));
 
        i = this.balls.length - 1;
        while(i--) {
   
            if(this.balls[i].x > 0 && this.balls[i].x < this.width) {
                this.balls[i].x += this.wind.speed * this.wind.direction;
            } else if(this.wind.direction >= 0) {
                this.balls[i].x = 1;
            } else {
                this.balls[i].x = this.width - 1;
            }
            
            var yStart = this.balls[i].y;
            if(this.balls[i].y < this.height) {
                this.balls[i].y += this.balls[i].gravity;
            } else {
                this.balls[i].y = -Hail.size.max * 2;
                this.balls[i].bounced = false;
            }
            var yEnd = (this.balls[i].y > yStart) ? this.balls[i].y - yStart : Hail.tailLength;
                        
            ctx.fillStyle = Hail.color;
            ctx.beginPath();
            ctx.arc(this.balls[i].x, this.balls[i].y, this.balls[i].radius, 0, this.circleEndAngle, false);
            ctx.fill();
            
            ctx.strokeStyle = Hail.tailColor;
            ctx.beginPath();
            ctx.moveTo(this.balls[i].x, this.balls[i].y);
            ctx.lineTo(this.balls[i].x - xEnd, this.balls[i].y - yEnd);
            ctx.stroke();
        }
    },
    setSize: function(width, height) {
        this.width = width; 
        this.height = height; 
    }
});

var Sleet = function(ctx, width, height, drops, wind) {
    this.ctx = ctx;
    this.width = width; 
    this.height = height; 
    this.wind = wind;
    
    this.xVector = this.wind.speed * 4;
    this.circleEndAngle = Math.PI * 2;
        
    this.drops = [];
    i = drops;
    while(i--) {
        this.drops.push({
            x: Math.round(Math.random() * width),
            y: Math.round(Math.random() * (height + Sleet.size)) - Sleet.size,
            gravity: (Math.random() * 10) + 50
        });
    }
};
$.extend(Sleet, {
    size: 200,
    color: 'rgba(255,255,255,0.1)',
    iceColor: 'rgba(255,255,255,0.9)'
});
$.extend(Sleet.prototype, {
    draw: function() {
        var ctx = this.ctx;

        var xStart = this.xVector * this.wind.direction;
        var yStart = Math.sqrt(Math.pow(Sleet.size, 2) + Math.pow(xStart, 2));
                
        i = this.drops.length - 1;
        while(i--) {
                        
            if(this.drops[i].x > 0 && this.drops[i].x < this.width) {
                this.drops[i].x += this.wind.speed * this.wind.direction;
            } else if(this.wind.direction >= 0) {
                this.drops[i].x = 1;
            } else {
                this.drops[i].x = this.width - 1;
            }
            
            if(this.drops[i].y <= this.height) {
                this.drops[i].y += this.drops[i].gravity;
            } else {
                this.drops[i].y = -Sleet.size;
            }
            
            ctx.fillStyle = Sleet.color;
            ctx.beginPath();
            ctx.moveTo(this.drops[i].x, this.drops[i].y);
            ctx.lineTo(this.drops[i].x + xStart, this.drops[i].y + yStart);
            ctx.quadraticCurveTo(this.drops[i].x + xStart, this.drops[i].y + yStart + 3, this.drops[i].x + xStart + 3, this.drops[i].y + yStart + 3);
            ctx.fill();
            
            ctx.fillStyle = Sleet.iceColor;
            ctx.beginPath();
            ctx.arc(this.drops[i].x + xStart, this.drops[i].y + yStart, 1, 0, this.circleEndAngle, false);
            ctx.fill();
        }
    },
    setSize: function(width, height) {
        this.width = width; 
        this.height = height; 
    }
});


var Snow = function(ctx, width, height, amount, wind) {
    this.ctx = ctx;
    this.width = width; 
    this.height = height; 
    this.amount = amount; 
    this.wind = wind;
    
    this.wind.speed = (this.wind.speed === 0) ? 1 : this.wind.speed;
    this.xVector = this.wind.speed * 2;
    
    this.circleEndAngle = Math.PI * 2;

    this.flakes = [];
    var i = this.amount;
    while(i--) {
        this.flakes.push({
            x: Math.random() * width,
            y: Math.random() * height,
            radius: Math.round(Math.random() * (Snow.size.max - Snow.size.min) + Snow.size.min),
            swayMax: (Math.random() * 60) / this.xVector,
            vector: Math.round(Math.random() * 3) - 1,
            gravity: Math.random() * 10,
            sway: 0
        });
    }
};
$.extend(Snow, {
    size: {
        min: 3,
        max: 12
    },
});
$.extend(Snow.prototype, {
    draw: function() {
        var ctx = this.ctx;
        
        var i = this.flakes.length;
        while(i--) {        
            if(Math.abs(this.flakes[i].sway) >= this.flakes[i].swayMax) {
                this.flakes[i].vector *= -1;
            }
            
            this.flakes[i].sway = this.flakes[i].sway + (1 * this.flakes[i].vector);
   
            if(this.flakes[i].x > 0 && this.flakes[i].x < this.width) {
                this.flakes[i].x += (this.wind.speed * this.wind.direction) + this.flakes[i].sway;
            } else if(this.wind.direction >= 0) {
                this.flakes[i].x = 1;
            } else {
                this.flakes[i].x = this.width - 1;
            }
            
            if(this.flakes[i].y <= this.height) {
                this.flakes[i].y += this.flakes[i].gravity;
            } else {
                this.flakes[i].y = -Snow.size.max;
            }

            var radgrad = ctx.createRadialGradient(
                this.flakes[i].x, 
                this.flakes[i].y, 
                this.flakes[i].radius / 3, 
                this.flakes[i].x, 
                this.flakes[i].y, 
                this.flakes[i].radius
            );
            radgrad.addColorStop(0, 'rgba(255,255,255,0.6)');
            radgrad.addColorStop(1, 'rgba(255,255,255,0)');
            
            this.ctx.strokeStyle = Lightning.color;
            this.ctx.lineWidth = 1;
            ctx.fillStyle = radgrad;
            ctx.fillRect(
                this.flakes[i].x - this.flakes[i].radius, 
                this.flakes[i].y - this.flakes[i].radius, 
                this.flakes[i].radius * 2, 
                this.flakes[i].radius * 2
            );
        }
    },
    setSize: function(width, height) {
        this.width = width; 
        this.height = height; 
    }
});
