var app = {
	/* Variables */
	panel: $('#multipanel'),
	results: {},
	blocks: [],
	iterations: 2,
  group_by: 1,
	position: [],
	types: ['Random, Squares Only', 'Random, Squares and Circles, Separated', 'Random, Squares and Circles, Mixed', 'Grouped By 2, Squares Only', 'Grouped By 2, Squares and Circles, Separated', 'Grouped By 2, Squares and Circles, Mixed','Grouped By 3, Squares Only', 'Grouped By 3, Squares and Circles, Separated', 'Grouped By 3, Squares and Circles, Mixed'],
	i: 0,
  animation: null,
  canvas: null,
  ctx: null,
  boxColor: null,
  time: 0,
  shape_mode: 0,
  shape: 1,
  noticed: 'No',
	current_experiment: 0,
	experiment_descriptions: ["<h2>Welcome!</h2><p>The following experiment will measure how grouping objects can affect your ability to count large amounts of these objects.</p><p>If you have any questions, ask Mike.</p><input type=\"submit\" id=\"begin\" value=\"Begin &raquo;\"/>",
                            "<h3>Identification</h3><p>You will be shown a series of streams of objects for a few seconds.  For each stream, count the total number of objects emitted from the box in the top left of the viewing area.</p><p>There may be more than one types of objects in flowing the stream at a time.  You want to keep count of the total number of objects, regardless of type, in the stream.</p><input type=\"submit\" id=\"start\" value=\"Start &raquo;\"/>",
                            ],
	/* Functions */
	// Clear the multipanel of children.
	empty: function() {
		return this.panel.empty();
	},
	
	// Bind the click action for the Begin button in the welcome view.
	bindBegin: function(context) {
		$('#begin').click(function(event) {
			event.preventDefault();
			$(this).unbind();
			setTimeout(function() {app.initExperiment();}, 25);
		});
	},
	
	// Bind the click action for the Start button in the description view.
	bindStart: function(context) {
		$('#start').click(function(event) {
			event.preventDefault();
			$(this).unbind();
			setTimeout(function() {app.runExperiment();}, 25);
		});
	},
	
	// Bind the click action for the Restart button in the finish view.
	bindRestart: function(context) {
		$('#restart').click(function(event) {
			event.preventDefault();
			$(this).unbind();
			setTimeout(function() {app.start();}, 25);
		});
	},
	
	// Bind the click action for the Continue button in the question view.
	bindContinue: function(context) {
		$('#continue').click(function(event) {
    
      // Check the input.
			event.preventDefault();
			if(/^(\d+)$/.test($('#numblocks').val()) == false) {
				alert('Please enter a number.');
				return false;
			}

      // Unbind the submit action.
			$(this).unbind();
			
      // Store the entered number in the results hash.
      context.results[context.types[context.current_experiment-1]][context.i*2] = context.blocks[context.i];
      context.results[context.types[context.current_experiment-1]][(context.i*2)+1] = $('#numblocks').val();
			
      // Continue to the next set of experiments or show another stimulus?
			if(++context.i >= context.iterations) {
				setTimeout(function() {app.initExperiment();}, 25);
			} else {
				setTimeout(function() {app.beginCountdown();}, 25);
			}
		});
	},
  
  // Bind the click action for the yes/no buttons in the last question.
	bindYesOrNo: function(context) {
		$('.noticeChange').click(function(event) {
			event.preventDefault();
			$('.noticeChange').unbind();
			context.noticed = $(this).val();
      setTimeout(function() {app.finish();}, 25);
		});
	},
	
	// Reset the results objects.
	resetResults: function() {
		this.results = {};
    for (i=0;i<this.types.length;++i) {
      this.results[this.types[i]] = [-1, -1, -1, -1];
    }
	},
	
	// Begin the study.  Reset variables and display the welcome view.
	start: function() {
		this.resetResults();
		this.current_experiment = 0;
		this.welcome();
	},
	
	// Show the welcome view.
	welcome: function() {
		this.empty();
		this.panel.append(this.experiment_descriptions[0]);
		this.bindBegin(this);
	},
	
	// Initialize an experiment.
	initExperiment: function() {
		++this.current_experiment;

		// Check experiment number.
		if(this.current_experiment < 1) {
      // This shouldn't happen.
			alert("Invalid experiment.");
			return false;
		} else if (this.current_experiment > this.types.length) {
      // Just completed the last set, submit the results and ask one last question.
			setTimeout(function() {app.askOneLastQuestion();}, 25);
			return true;
		} else if (this.current_experiment > 1) {
      // For this type of experiment, we don't want't to show information screens after the first set.
      
      // Adjust settings appropriately.
      // Set the grouping.
      if (this.current_experiment > 6) {
        this.group_by = 3;
      } else if (this.current_experiment > 3) {
        this.group_by = 2;
      } else {
        this.group_by = 1;
      }
      
      // Set the shape types.
      if ((this.current_experiment)%3 == 0) {
        this.shape_mode = 3;
      } else if((this.current_experiment)%3 == 2) {
        this.shape_mode = 2;
      } else {
        this.shape_mode = 1;
      }
      
      // Launch the next experiment asynchronously so we don't cause a huge function tree.
      setTimeout(function() {app.runExperiment();}, 25);
      return true;
    }
		
		this.empty();
		this.panel.append(this.experiment_descriptions[this.current_experiment]);
		
		this.bindStart(this);
	},
	
	// Run a round of tests.
	runExperiment: function() {
		this.blocks = [30+Math.ceil(Math.random()*5)*this.group_by, 30+Math.ceil(Math.random()*5)*this.group_by];
		
		this.i = 0;
		setTimeout(function() {app.beginCountdown();}, 25);
	},
	
	// Show the view.
	beginCountdown: function() {
		this.empty();
		this.panel.append("<div id='canvasWrap'><div>3</div></div>");
		
		$('#canvasWrap div:last-child').fadeOut(1000, function() {app.countdown2();});
	},
	
	// 2nd part of the countdown.
	countdown2: function() {
		$('#canvasWrap').append('<div>2</div>');
		$('#canvasWrap div:last-child').fadeOut(1000, function() {app.countdown1();});
	},
	
	// Last part of the countdown.
	countdown1: function() {
		$('#canvasWrap').append('<div>1</div>');
		$('#canvasWrap div:last-child').fadeOut(1000, function() {app.showCanvas();});
	},
	
	// Show the canvas.
	showCanvas: function() {
		$('#canvasWrap').empty();
		$('#canvasWrap').append('<canvas width="1400" height="800" id="canvas"></canvas>');
		
    // Get the canvas and the context.
		this.canvas = document.getElementById('canvas');
    this.ctx = this.canvas.getContext('2d');
    
    // Set the 'emitter' color.
    this.boxColor = 225;
    
    // Set the fill and stroke style for the squares.
    this.ctx.fillStyle = '#F02311';
    this.ctx.strokeStyle = '#000';
    this.ctx.lineWidth = 1;
		
		this.positions = [];
    
    for(j=0;j<this.blocks[this.i];j += this.group_by) {
      offset = this.getPosition();
      
      // Select the correct shape to render.
      if (this.shape_mode == 1) {
        // Only squares.
        this.shape = 1;
      } else if (this.shape_mode == 2) {
        // Separated squares and circles.
        this.shape = Math.round(Math.random())+1;
      }
      
      // Add each square to the positions array.
      for (k=offset[0],l=0;l<this.group_by;++l,k+=60) {
        if (this.shape_mode == 3) {
          // Mixed squares and circles.
          this.shape = Math.round(Math.random())+1;
        }
        this.positions.push([k, offset[1], this.shape]);
      }
    }
		
    this.time = 0;
    this.animation = setInterval(function(){app.animate();}, 25);
    
    return true;
	},
  
  animate: function() {
    if (this.time == 6650) {
      // We're done with animation.
      clearInterval(this.animation);
      // Ask the user how many blocks there were.
      setTimeout(function(){app.askQuestion();}, 50);
      
      return true;
    }
    
    // Clear the canvas.
    this.ctx.clearRect(0, 0, 1400, 800);
    
    // Draw each block.
    for (k=0;k<this.positions.length;++k) {
      pos_x = this.positions[k][0]-2000+this.time*.53;
      pos_y = this.positions[k][1]+75;
    
      // Draw the square, if it's in view.
      if (pos_x > 50 && pos_x < 1400) {
        pos_y += (pos_x-100)*.005 + (pos_x-100)*(pos_x-100)*.0003;
        
        // Determine the rendering mode.
        if (this.positions[k][2] == 1) {
          // Draw a red square.
          this.ctx.fillRect(pos_x, pos_y, 50, 50);
          this.ctx.strokeRect(pos_x, pos_y, 50, 50);
        } else {
          // Draw a blue circle.
          this.ctx.save();
          this.ctx.fillStyle = '#3482B2';
          this.ctx.beginPath();
          this.ctx.arc(pos_x+25, pos_y+25, 25, 0, Math.PI*2);
          this.ctx.fill();
          this.ctx.stroke();
          this.ctx.restore();
        }
      }
    }
    
    // Save the current styles.
    this.ctx.save();
    
    this.ctx.strokeStyle = '#1f1f1f';
    this.ctx.lineWidth = 3;
    
    // Animate the gradient after 0.5 seconds and until the box is black.
    if (this.time >= 500 && this.boxColor > 0) {
      // Create the next step in the gradient.
      this.boxColor -= 1.5;
      colorInt = Math.round(this.boxColor);
      hexVal = "0123456789ABCDEF".charAt((colorInt-colorInt%16)/16) + "0123456789ABCDEF".charAt(colorInt%16);
      
      this.ctx.fillStyle = '#'+hexVal+hexVal+hexVal;
      this.ctx.fillRect(-10, 50, 110, 225);
      this.ctx.strokeRect(-10, 50, 110, 225);
      
      // Reset the fill color.
      this.ctx.fillStyle = '#F02311';
    } else if (this.boxColor < .5) {
      // Fill with black (after animation completes).
      this.ctx.fillStyle = '#000000'
      this.ctx.fillRect(-10, 50, 110, 225);
      this.ctx.strokeRect(-10, 50, 110, 225);
      
    } else {    
      // Fill with grab (before animation starts).
      this.ctx.fillStyle = '#E1E1E1'
      this.ctx.fillRect(-10, 50, 110, 225);
      this.ctx.strokeRect(-10, 50, 110, 225);
    }
    
    // Reset the styles.
    this.ctx.restore();
    
    // Increment the time counter.
    this.time += 25;
  },
  
  	// Find a non-overlapping position for the next block.
	getPosition: function() {
		good = false;
		
		offset_x = 0;
		offset_y = 0;
    
    num_loops = 0;
		
		while(!good) {
			good = true;
      
      if (this.group_by == 1) {
        // Group randomly.
        offset_x = Math.round(Math.random()*1800);
        offset_y = Math.round(Math.random()*140);
      } else {
        // Group in sets.
        offset_x = Math.round(Math.random()*2200);
        offset_y = Math.floor(Math.random()*1.99)*90;
      }
      
      width_x = this.group_by*60;
      
      if (num_loops == 80) {
        good = true;
        break;
      }
			
			for (k=0;k<this.positions.length;++k) {
				// Check to see if the new square would intersect with the existing ones.
				if (((offset_x >= this.positions[k][0] && offset_x <= (this.positions[k][0]+width_x)) || ((offset_x+width_x) >= this.positions[k][0] && offset_x < this.positions[k][0])) && ((offset_y >= this.positions[k][1] && offset_y <= this.positions[k][1]+50) || (offset_y+50 >= this.positions[k][1] && offset_y < this.positions[k][1]))) {
					// If it does, then break and choose another random starting point.
					good = false;
          ++num_loops;
					break;
				}
			}
		}
    // The current offset will not intersect with any other squares.
		this.positions.push([offset_x, offset_y]);
		return [offset_x, offset_y];
	},
	
	// Ask question.
	askQuestion: function() {
		this.empty();

		this.panel.append('<h3>How Many Objects?</h3><p><input type="text" id="numblocks" maxlength="3" size="3" value="" /> <input type="submit" id="continue" value="Continue" /></p>');
		$('#numblocks').focus();
		this.bindContinue(this);
	},
  
  // Ask one last question.
	askOneLastQuestion: function() {
		this.empty();
  
    this.panel.append('<h3>Did you notice the object emitter change color?</h3><input type="submit" class="noticeChange" value="Yes" /> <input type="submit" class="noticeChange" value="No" />');
    this.bindYesOrNo(this);
  },
	
	// All done with the experiments.
	// (Old, for class) Upload data and show a thank you view.
  // (New, for web demo) Show the user how they did.
	finish: function() {
		this.empty();

		// Prep results for display.
		str = '<thead><tr><th>Type</th><th>% Correct</th></tr></thead>';
		for(type in this.results) {
			str += '<tr><th>' + type + '</th>';
      
      percent = 0.0;
			for(j=0;j<this.results[type].length;j+=2) {
        percent += this.results[type][j] == this.results[type][j+1] ? 1 : 0;
			}
      
			str += '<td>' + ((percent / (this.results[type].length / 2)) * 100) + '</td></tr>';
		}
		str += '</tbody>';
    
		this.panel.append("<h2>Thanks!</h2><p>Hey, you finished all the tests.  Thanks for helping out.</p><p><strong>Here are your results:</strong></p><table>" + str + "</table><input type=\"submit\" id=\"restart\" value=\"Restart\"/>");
		this.bindRestart(this);
	}
};

app.start();
window.onbeforeunload = function () {
	return "You are about to leave the experiment.  Are you sure you want to continue?";	
}
