/*
   This file is part of the paintmyblog.com web client
   Copyright 2007 Karl Henrik Falck <f@lck.nu>
*/

var canvas = null;
var surface = null;
var ctx = null;
var mousedown = false;
var width = 0;
var height = 0;
var scale = 1.0;
var mypainter = null;
var painters = null;
var fastForward = false;
var vWidth = 1000;
var vHeight = 1000;
var xScale = 1.0;
var yScale = 1.0;
var lastUpdate = 0;
var lastMousing = 0;
var lastPainting = 0;
var updateTimeout = null;
var wakeOnMousing = false;
var mScale = 1.0;
var minimized = false;
var actions;
var running = false;
var stop = false;
var sending = false;
  
function initialize()
{
  canvas = $('canvas');
  surface = $('surface');
  ctx = canvas.getContext("2d");
  ctx.strokeStyle = "black";
  ctx.lineWidth = 4;
  Event.observe(window, "resize", onResize);
  actions = new Actions();
  painters = $H();
  mypainter = createPainter();
  onResize(true);
  process();
  scheduleInitialization();
  initializeGUI();
  setTimeout(registerEvents, 200);
  if (channelTitle)
    $("channeltitle").innerHTML = channelTitle + '<br>';
  else
    $("channeltitle").hide();
  if (channelCaption)
    $("channelcaption").innerHTML = channelCaption + '<br>';
  else
    $("channelcaption").hide();
  lastMousing = getTime();
  lastPainting = getTime();
  $("sidebox").observe("click", function() { window.location = "http://paintmyblog.com/home"; });
}

function registerEvents()
{
  var obj = surface ? surface : canvas;
  obj.observe("mousedown", onMouseDown);
  obj.observe("mouseup", onMouseUp);
  obj.observe("mousemove", onMouseMove);
}

var resizeTimeout = null;

function onResize(initial)
{
  stopProcessing();
  var w = window.innerWidth ? window.innerWidth : (document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth);
  var h = window.innerHeight ? window.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight);
  if (!canvas) return;
  canvas.setStyle({width: w, height: h});
  width = w;
  height = h;
  xScale = width / vWidth;
  yScale = height / vHeight;
  scale = (w + h) / 2048;
  var m = Math.min(1.5 * scale, 1.0);
  if (!minimized && (m < 0.5 || h <= 322 || w <= 320))
    minimize();
  else if (minimized && m > 0.5 && h > 322 && w > 320)
    maximize();
  debugStatus("w", w);
  debugStatus("h", h);
  mScale = m;
  var pal = $("palette");
  var d = minimized ? 2 : 1;
  pal.setStyle({top: h - 65 / d, left: w - 319 / d, visibility: "visible"});
  var size = $("sizebox");
  var sizesoffh = (surface ? 132 : 130);
  size.setStyle({left: w - 66 / d, top: h - sizesoffh / d, visibility: "visible"});
  var tool = $("toolbox");
  var toolsoffh = sizesoffh + (surface ? 190 : 189);
  tool.setStyle({left: w - 66 / d, top: h - parseInt(toolsoffh / d), visibility: "visible"});
  $("logo").setStyle({width: mScale * 150, height: mScale * 100});
  $("getyourown").setStyle({width: mScale * 45, height: mScale * 386});
  $("sidebox").setStyle({top: h / 2 - mScale * 386 / 4, left: 0, visibility: minimized ? "hidden" : "visible"});
  if ((surface || Prototype.Browser.Opera) && !initial) {
    var l = $("loading");
    var w2 = 50;
    var h2 = 20;
    l.setStyle({
      visibility: "visible",
	  left: parseInt(w / 2 - w2),
	  top: parseInt(h / 2 - h2)});
    ctx.clearRect(0, 0, w, h);
    surface.setStyle({width: w, height: h});
    clearTimeout(resizeTimeout);
    resizeTimeout = setTimeout(repaintAll, 500);
  }
}

function minimize()
{
  minimized = true;
  var elms = document.getElementsByClassName("maxi");
  elms.each(function(e) { e.addClassName("mini"); });
  $("swatchcorner").setStyle({visibility: "hidden"});
  $("swatchcornermini").setStyle({visibility: "visible"});
}

function maximize()
{
  minimized = false;
  var elms = document.getElementsByClassName("maxi");
  elms.each(function(e) { e.removeClassName("mini"); });
  $("swatchcornermini").setStyle({visibility: "hidden"});
  $("swatchcorner").setStyle({visibility: "visible"});
}

function repaintAll()
{
  //  painters.invoke("restart");
  process(true);
}

function getTime()
{
  if (Date.now)
    return Date.now();
  return new Date().getTime();
}

function timeSince(time)
{
  return getTime() - time;
}

function normalizeX(x)
{
  var x = parseInt(x * vWidth / width);
  if (x < 0) x = 0;
  else if (x >= vWidth) x = vWidth - 1;
  return x;
}

function normalizeY(y)
{
  var y = parseInt(y * vHeight / height);
  if (y < 0) y = 0;
  else if (y >= vHeight) y = vHeight - 1;
  return y;
}

function onMouseDown(e)
{
  mousedown = true;
  lastMousing = getTime();
  lastPainting = getTime();
  mypainter.startLine(normalizeX(e.clientX), normalizeY(e.clientY));
  process();
}

function onMouseUp(e)
{
  mousedown = false;
  lastMousing = getTime();
  lastPainting = getTime();
  mypainter.closeLine(normalizeX(e.clientX), normalizeY(e.clientY));
  mypainter.executeAll();
  process();
}

function dist(x1, y1, x2, y2)
{
  return Math.sqrt((x1 - x2) * (x1 - x2) +
		   (y1 - y2) * (y1 - y2));
}

function onMouseMove(e)
{
  if (wakeOnMousing) wake();
  lastMousing = getTime();
  if (!mousedown)
    return;
  lastPainting = getTime();
  mypainter.continueLine(normalizeX(e.clientX), normalizeY(e.clientY));
  process();
}

function wake()
{
  debugStatus("wake", "woken");
  wakeOnMousing = false;
  scheduleUpdate();
}

function run()
{
  running = true;
  var limit = fastForward ? 100 : 10;
  if (fastForward)
    for (var instrCount = 0; !stop && actions.hasMore() && instrCount < limit; ++instrCount)
      actions.execute();
  if (!fastForward && mypainter.hasMore())
    mypainter.executeAll();

  var hasMore = actions.hasMore();
  if (fastForward && (!hasMore || stop)) {
    fastForward = false;
    var l = $("loading");
    l.setStyle({visibility: "hidden"});
  }
  debugStatus("unsent", mypainter.numUnsent());
  if (hasMore && !stop)
    setTimeout(run, 10);
  else
    running = false;
  if (stop) stop = false;
  debugStatus("running", running);
  setDebugStatus();
}

function process(ff)
{
  fastForward = !!ff;
  if (!running) {
    running = true;
    setTimeout(run, 10);
  }
  if (mypainter.hasUnsent())
    scheduleUpdate();
}

function stopProcessing()
{
  if (running)
    stop = true;
}

var debugStatuses = $H();
function debugStatus(key, val)
{
  if (!val && val != 0) val = "null";
  debugStatuses[key] = "" + val;
  setDebugStatus();
}

function setDebugStatus()
{
  if (!debug)
    return;
  if (m = debugStatuses["message"])
    window.status = m;
  else
    window.status = debugStatuses.map(function(x) { return x.key + ": " + x.value; }).join(" ");
}

function debugMessage(str)
{
  debugStatuses["message"] = str;
  setDebugStatus();
}

function setMyPainterId(id)
{
  var p = painters["mypainter"];
  if (p != mypainter) debugMessage("mypainter is not my painter");
  p.id = id;
  painters.remove("mypainter");
  painters[id] = p;
}

function createPainter(id)
{
  var p = new Painter(id);
  painters[id ? id : "mypainter"] = p;
  if (id)
    statistics.painterCreated();
  return p;
}

function fetchPainter(id)
{
  var p = painters[id];
  if (!p) return createPainter(id);
  return p;
}

function scheduleUpdate(secs)
{
  if (!mypainter.id) return;
  clearTimeout(updateTimeout);
  debugStatus("update", "scheduled (" + (secs ? secs : "now") + ")");
  var time = secs ? secs * 1000 : 1000;
  updateTimeout = setTimeout(synchronize, time);
}

function scheduleInitialization()
{
  if (!standAlone)
    setTimeout(sendInitialize, 100);
}

function sendInitialize()
{
  new Ajax.Request(baseUrl + "/initialize",
		   {
		   onSuccess: deserializeData,
		       onFailure: function(req) {
		       debugMessage("initialization failed");
		     },
		       onException: onException
		   });
}

function synchronize()
{
  if (sending ||
      (mousedown && mypainter.numUnsent() < 200) ||
      (timeSince(lastPainting) < 2000 && mypainter.numUnsent() < 100) ||
      timeSince(lastUpdate) < 5000) {
    scheduleUpdate();
    return;
  }
  if (timeSince(lastMousing) > 30 * 60 * 1000) {
    debugStatus("wake", "sleeping");
    wakeOnMousing = true;
    return;
  }
  sending = true;
  if (mypainter.hasUnsent()) {
    debugStatus("update", "sending");
    statistics.painted();
    mypainter.executeAll();
    var str = actions.serialize(mypainter);
    new Ajax.Request(baseUrl + "/send",
		     {
		     method: "post",
			 parameters: {c: str},
			 onSuccess: deserializeData,
			 onFailure: updateFailed,
			 onException: onException
			 });
  }
  else {
    debugStatus("update", "updating");
    new Ajax.Request(baseUrl + "/update",
		     {
		     method: "post",
			 onSuccess: deserializeData,
			 onFailure: updateFailed,
			 onException: onException
			 });
  }
}

var responseText = null;

function deserializeData(req)
{
  if (debug)
    responseText = req.responseText;
  lastUpdate = getTime();
  var res = req.responseText.evalJSON();
  if (res.id) {
    debugStatus("id", res.id);
    setMyPainterId(res.id);
    fastForward = true;
  }
  var cmds = res.c;
  var interval = res.i;
  if (cmds) {
    var len = cmds.length;
    for (var i = 0; i < len; ++i) {
      var id = cmds[i][0];
      var cmd = cmds[i][1];
      if (!cmd) continue;
      var p = fetchPainter(id);
      actions.deserialize(p, cmd, !!res.id);
    }
  }
  if (!isNaN(res.v) && !isNaN(res.p)) statistics.aquire(res.v, res.p, res.y);
  else statistics.track();
  process(!!res.id);
  sending = false;
  scheduleUpdate(interval);
}

function updateFailed(req)
{
  if (debug)
    alert(req.status + ": " + req.responseText);
  sending = false;
  debugMessage("update failed!");
  scheduleUpdate(30);
}

function onException(req)
{
  if (debug)
    alert(responseText);
  sending = false;
  debugMessage("exception");
  scheduleUpdate(60);
}

var Statistics = Class.create();
Statistics.prototype = {
  viewers: 0,
  painters: 0,
  me: null,
  updated: false,

  initialize: function() { },

  aquire: function(v, p, y) {
    this.viewers = v;
    this.painters = p;
    this.me = y;
    if (!y)
      this.viewers++;
    this.updated = true;
    this.update();
  },

  track: function() {
    this.updated = false;
  },

  painted: function() {
    if (!this.me || this.me == "v") {
      this.viewers--;
      this.painters++;
      this.me = "p";
      this.update();
    }
  },
  
  painterCreated: function() {
    if (!this.updated) {
      this.painters++;
      this.update();
    }
  },

  update: function() {
    $("viewers").innerHTML = "<b>" + this.viewers + "</b> watching";
    $("painters").innerHTML = "<b>" + this.painters + "</b> painting";
    if (mypainter.id)
      $("contributors").innerHTML = "<b>" + painters.size() + "</b> have contributed";
  }
};

var statistics = new Statistics();

var exCanvasElm = null;

function setExCanvasElement(elm)
{
  exCanvasElm = elm;
}

