function GeneratorSet() {
    var generators = [];
    this.addGenerator = function (generator) {
        generators.push(generator);
    }
    var selectedGenerator, hoveredGenerator;
    this.update = function (context) {
        for (var i = 0; i < generators.length; i++) {
            generators[i].update();
		}
		if (selectedGenerator) {
			context.beginPath();
			context.strokeStyle = "yellow";
			drawCircle(context, selectedGenerator.x(), selectedGenerator.y(), 4);
			context.fill();
		}
		if (hoveredGenerator) {
			context.beginPath();
			context.strokeStyle = "green";
			drawCircle(context, hoveredGenerator.x(), hoveredGenerator.y(), 4);
			context.fill();
		}
    }
    this.mousedown = function (x,y) {
        var abs = Math.abs;
        var oldSelected = selectedGenerator;
        for (var i = 0; i < generators.length; i++) {
            var generator = generators[i];
            if (abs(generator.x() - x) <= 10 && abs(generator.y() - y) <= 10) {
                selectedGenerator = generator;
                return true;
            }
        }
        if (selectedGenerator === oldSelected)
            selectedGenerator = null;
        return false; 
    }
    this.mousemove = function (x,y) {
        var abs = Math.abs;
        hoveredGenerator = null;
        for (var i = 0; i < generators.length; i++) {
            var generator = generators[i];
            if (abs(generator.x() - x) <= 10 && abs(generator.y() - y) <= 10) {
                hoveredGenerator = generator;
                return true;
            }
        }
        return false; 
    }
}

function drawCircle(context, cx, cy, r) {
    context.moveTo(cx + r, cy);
    context.arc(cx,cy,r,0,2*Math.PI,false);
}

function WellSet(context) {
    var wells = [];
    var selectedWell = null;
    this.getWells = function() { return wells; }
    var hoveredWell = null;
    var mousedown = false;
    this.mousedown = function(x, y) {
        var abs = Math.abs;
        var newWells = [];
        var oldSelected = selectedWell;
        selectedWell = null;
        for (var i = 0; i < wells.length; i++) {
            var well = wells[i];
            if (abs(well.x - x) <= 7 && abs(well.y - y) <= 7) {
                selectedWell = well;
            }
            newWells.push(well);
        }
        if (!selectedWell)
            newWells.push(selectedWell = {x:x, y:y, mass:1000});
        else if (selectedWell === oldSelected)
            selectedWell = null;
        wells = newWells;
        hoveredWell = selectedWell;
        mousedown = true;
    }
    this.mouseup = function() {
        mousedown = false;
    }
    this.mousemove = function(x,y) {
        var abs = Math.abs;
        if (!mousedown) {
            for (var i = 0; i < wells.length; i++) {
                var well = wells[i];
                if (abs(well.x - x) <= 7 && abs(well.y - y) <= 7) {
                    hoveredWell = well;
                    return;
                }
            }
            hoveredWell = null;
        }
    }
    this.selectedWell = function (){ return selectedWell; }
    this.update = function (){
        for (var i = 0; i < wells.length; i++) {
            context.fillStyle = wells[i].mass > 0 ? "red" : "blue";
            context.beginPath();
            drawCircle(context, wells[i].x, wells[i].y, 5);
            context.fill();
            if (wells[i] === selectedWell) {
                context.beginPath();
                context.strokeStyle = "yellow";
                drawCircle(context, wells[i].x+0.1, wells[i].y+0.1, 10);
                context.stroke();
            } else if (wells[i] === hoveredWell) {
                context.beginPath();
                context.strokeStyle = "green";
                drawCircle(context, wells[i].x+0.1, wells[i].y+0.1, 10);
                context.stroke();
            }
        }
    }
    this.removeSelected = function (){
        if (!selectedWell)
            return;
        var newWells = [];
        for (var i = 0; i < wells.length; i++) {
            var well = wells[i];
            if (well === selectedWell)
                continue;
            newWells.push(well);
        }
        wells = newWells;
        selectedWell = null;
    }
    return this;
}

function ParticleEngine(wellSet, context, x, y, nParticles, colour, initialOffset, vybase, xbase) {
    var self = this;
    var particles = new Array(nParticles);
    var rand = Math.random;
    for (var i = 0; i < nParticles; i++) {
        particles[i] = {life:-20*rand() - 0.5};
    }
    var lastTime;
    var frameCount = 0;
    var lastFrameTime = new Date;
    this.wx = 0;
    this.wy = 0;
	this.x = function() { return x; };
	this.y = function() { return y; };
    this.update = function () {
        if (!lastTime) {
            lastTime = new Date;
        }
        var wells = wellSet.getWells();
        var wellCount = wells.length;
        var ps = particles;
        var count = ps.length;
        var time = new Date;
        var delta = time - lastTime;
        lastTime = time;
        delta /= 1000;
        var ctx = context;
        ctx.fillStyle = colour;
        var xb = xbase;
        var xb2 = xbase/2;
        var log = Math.log;
        var wx = self.wx;
        var wy = self.wy;
        var sqrt = Math.sqrt;
        var max = count;
        for (var i = 0; i < max; i++) {
            var particle = particles[i];
            var l = particle.life;
            if (l < 0) {
                if ((particle.life = l + delta) >= 0) {
                    particle.originalLife = particle.life = 16 - -5 * rand();
                    particle.x = x - -15 * rand() - 7.5;
                    particle.y = y + rand() * 3;
                    particle.vx = xb * rand() - xb2;
                    particle.vy = vybase - 5 * rand();
                    
                }
            } else {
                try{
                    var  l = particle.life;
                    particle.life -= delta;
                    var py = particle.y;
                    if (py < 0) continue;
                    var rad = log(l) * (0.6 - -0.1 * (l/particle.originalLife));
                    if (!(rad > 0.1)) continue;
                    var pvy = particle.vy;
                    var px = particle.x;
                    var pvx = particle.vx;
                    px = (particle.x = px + pvx * delta);
                    py = (particle.y = py + pvy * delta);
                    var ay = wy - 9;
                    var ax = wx;
                    for (var j = 0; j < wellCount; j++) {
                        var well = wells[j];
                        var dx = (well.x - px);
                        var dy = (well.y - py);
                        var r = (dx*dx + dy*dy);
                        r = sqrt(r) + 0.1;
                        var wellm = well.mass/r;
                        dx = dx / r;
                        dy = dy / r;
                        ax = ax + wellm*dx;
                        ay = ay + wellm*dy;
                    }
                    particle.vy = pvy + ay * delta;
                    particle.vx = pvx + ax * delta;
                    var rad2 = rad/2;
                    ctx.fillRect(px - rad2, py - rad2, rad, rad);
                }catch(e){}
            }
        }
    }
}
