/*
TODO:

*/

// Data

var all_paintings = [],
  all_years = [],
  all_paintings_and_years = [];
for (var year in paintings_by_year) {
  all_years.push(year);
  all_paintings_and_years.push(year);
  all_paintings_and_years = all_paintings_and_years.concat(paintings_by_year[year]);
  all_paintings = all_paintings.concat(paintings_by_year[year]);
};

// Constants

var gallery_w = 609,
  gallery_h = 261;

var columns = 7, rows = 3,
  num_views = rows * columns;
// in px
var view_w = 75, view_h = 75, gutter = 12;
var view_idxs = Array(num_views);  
for (var i = 1; i <= num_views; i++) view_idxs[i-1] = i;

var view_posns = [];
for (var i = 0; i < rows; i++) {
  for (var j = 0; j < columns; j++) {
    view_posns.push([i * (view_h + gutter), j * (view_w + gutter)]);
  }
}

var num_pages = Math.ceil((all_paintings_and_years.length) / num_views);

// Closure constructors

function ClosePaintingClosure(pinfo, mnu) {
  // must already be loaded
  var idnum = pinfo.viewnum;
    viewid = "#view" + idnum;
  return function () {
    SetSwatches(pinfo.page, mnu);
    $(viewid).unwrapInner('a')
    .animate({ 'top' : -pinfo.offset_y,
	       'left' : (gallery_w - pinfo.width)/2 - pinfo.offset_x,
	       'width' : view_w,
	       'height' : view_h}, 'fast',
	     function () {
	       $(viewid).animate({ 'top' : view_posns[idnum - 1][0],
				   'left' : view_posns[idnum - 1][1] });
	     })
    .css({'position' : 'absolute'})
    .children().eq(0)
    .animate({ 'top' : pinfo.offset_y,
	       'left' : pinfo.offset_x}).unmagnify();
    FadeInSeq(shuffle(view_idxs));
    $("#gallery").animate({'height' : gallery_h});
    ClosePainting = function () {};
  }
}

function SetBkgClosure(url) {
  return function () {
    SetBkg(url);
  };
};

// Helper functions

var load_queue = [];

for (var i in all_paintings_and_years) {
  i = Number(i);
  var p = all_paintings_and_years[i];
  p.page = Math.ceil((i+1) / num_views);
  p.viewnum = (i % num_views) + 1;
}

for (var i in all_paintings) {
  i = Number(i);
  all_paintings[i].index = i;
}

function ResetViews() {
  for (var i = 0; i < view_posns.length; i++)
    $("#view" + (i+1).toString()).css({ 'top' : view_posns[i][0],
					'left' : view_posns[i][1],
					'height' : view_h,
					'width' : view_w });
}

function formatNum(num, digits) {
  var ret = Number(num).toString();
  for (var i = 0; i < digits - ret.length; i++)
    ret = '0' + ret;
  return ret;
}

$.fn.unwrap = function(expr) {
  return this.each(function(){
     $(this).parents(expr).eq(0).after(this).remove();
  });
};

$.fn.unwrapInner = function(expr) {
  return this.each(function(){
    $(this).children().children().unwrap(expr);
  });
};

function SetBkg(url) {
  $("body").css("background-image", 'url("' + bkgs_loc + url + '")');
}

function LoadPainting(pinfo, next) {
  if (pinfo.loaded == false) {
    var sw = new Image;
    sw.src = swatches_loc + pinfo.swatch_file;
    pinfo.swatch = sw;
    var bkg = new Image;
    bkg.src = bkgs_loc + pinfo.bkg_file;
    pinfo.bkg = bkg;
    var img = new Image,
      url = paintings_loc + pinfo.painting_file;
    img.onload = function () {
      //console.log("loading " + pinfo.painting_file);
      pinfo.loaded = true;
      if (pinfo.view != null) {
	$("#" + pinfo.view + " img").attr('src', url).
	css({'top' : pinfo.offset_y,
	     'left' : pinfo.offset_x,
	     'width' : pinfo.width,
	     'height' : pinfo.height});
      }
      if (typeof(next) != 'undefined')
	next();
    }
    img.src = url;
    pinfo.painting = img;
  }
}

function SetSwatches(page, mnu) {
  Page = page;

  var start = (page - 1) * num_views,
    end = page * num_views;
  for (var i in all_paintings)
    all_paintings[i].view = null;
  var sl = all_paintings_and_years.slice(start, end),
    views = $("div.view").unwrapInner('a');

  SetBkg("bkgorg.jpg");

  for (var i = 0; i < sl.length; i++) {
    var pinfo = sl[i];
    var view = views.eq(i),
      img = view.children().eq(0);
    if (typeof(pinfo) == "string") {
      img.attr("src", boxes_loc + "box" + pinfo + ".gif")
      .css({'width' : view_w, 'height' : view_h, 'top' : 0, 'left' : 0});
    } else {
      pinfo.view = view.attr('id');
      if (pinfo.loaded) {
	img.attr("src", paintings_loc + pinfo.painting_file)
	.css({'width' : pinfo.width, 'height' : pinfo.height, 
	      'top' : pinfo.offset_y, 'left' : pinfo.offset_x});
      } else {
	img.attr("src", swatches_loc + pinfo.swatch_file)
	.css({'width' : view_w, 'height' : view_h, 
	      'top' : 0, 'left' : 0});
      }
      view.hover(SetBkgClosure(pinfo.bkg_file),
		 SetBkgClosure("bkgorg.jpg"));
      view.wrapInner('<a href="#page=p' + pinfo.index + '"></a>');
    }
  }
  for (var i = sl.length; i < num_views; i++) {
    views.eq(i).unwrapInner('a').children().eq(0).attr("src", boxes_loc + "box.gif")
    .css({'top' : 0, 'left' : 0, 'width' : view_w, 'height' : view_h});
  }

  ViewMenu(mnu);
  bindMenus(page);
  $("#arrowspan").empty();
  if (page < num_pages) 
    $("#arrowspan").prepend('<a href="#page=s' + formatNum(page+1, 2) + mnu + '">next<img class="arrow" src="img/arrowright.gif"></a>');
  else
    $("#arrowspan").prepend('<span>next<img class="arrow" src="img/arrowrightlight.gif"></span>');

  if (page > 1)
    $("#arrowspan").prepend('<a href="#page=s' + formatNum(page-1, 2) + mnu + '"><img class="arrow" src="img/arrowleft.gif">prev</a>');
  else
    $("#arrowspan").prepend('<span><img class="arrow" src="img/arrowleftlight.gif">prev</span>');

}

function bindMenus(page) {
  jQuery.each(menuID, function (m, i) {
    $("#" + menuID[m])
    .unbind()
    .unwrapInner('a')
    .wrapInner('<a class="clear" href="#page=' + 's' + formatNum(page, 2) + m + '"></a>"');
  });
}

// Mode changers

function HideSwatches() {
  $("div.view").hide();
}

function ShowSwatches(page, mnu) {
  SetSwatches(page, mnu);
  FadeInSeq(shuffle(view_idxs));
  $("#arrowspan").show();
};

function ShowPainting(pinfo) {
  var id = "#view" + pinfo.viewnum,
    img = new Image;
  img.onload = function () {
    ResetViews();
    HideSwatches();
    $(id).unwrapInner('a');
    paintCommon(pinfo, id);
    $('#gallery').css({'height' : pinfo.height + 12});
    $(id).css({ 'top' : 0,
	   'left' : (gallery_w - pinfo.width)/2,
	   'width' : pinfo.width,
	   'height' : pinfo.height })
    .show()
    .children().eq(0)
    .css({ 'top' : 0,
	   'left' : 0,
	   'width' : pinfo.width,
	   'height' : pinfo.height })
    .magnify();};
  img.src = paintings_loc + pinfo.painting_file;
}

/*
Order of actions:
Load Painting
Set Picture (at correct offset) / Set description / Set background
Fade Out other views
Resize Gallery / hide arrow / Move to spot
Expand to painting size / move painting to 0,0
magnify
set ClosePainting
set Menus
*/
function AnimateToPainting(pinfo) {
  var id = "#view" + pinfo.viewnum,
    img = new Image;
  img.onload = function () {
    $(id).unwrapInner('a');
    paintCommon(pinfo, id);
    $(id).children().eq(0)
    .css({ 'top' : pinfo.offset_y, 'left' : pinfo.offset_x,
	   'width' : pinfo.width, 'height' : pinfo.height });
    FadeOutSeq(view_idxs, pinfo.viewnum,
      function () {
	$('#gallery').animate({'height' : pinfo.height + 12});
	$(id).animate({ 'top' : -pinfo.offset_y,
			'left' : (gallery_w - pinfo.width)/2 - pinfo.offset_x }, 'fast',
                      function () {
	                $(id).animate({ 'top' : 0,
			                'left' : (gallery_w - pinfo.width)/2,
			                'width' : pinfo.width,
			                'height' : pinfo.height })
	                .children().eq(0)
                        .animate({ 'top' : 0,
				   'left' : 0,
				   'width' : pinfo.width,
				   'height' : pinfo.height })
	                .magnify();
	              })})};
  img.src = paintings_loc + pinfo.painting_file;
}

function paintCommon(pinfo, id) {
  pinfo.loaded = true;
  pinfo.img = $(id).children().get(0);
  $(id).unbind().children().eq(0)
  .attr("src", paintings_loc + pinfo.painting_file);
  $("span.menu").removeClass("underline");
  bindMenus(pinfo.page);
  $("div#description").html(pinfo.description);
  SetBkg("bkgorg.jpg");
  ClosePainting = ClosePaintingClosure(pinfo);

  $('#arrowspan').empty();
  if (pinfo.index < all_paintings.length - 1) 
    $("#arrowspan").prepend('<a href="#page=p' + formatNum(pinfo.index+1, 3) + '">next<img class="arrow" src="img/arrowright.gif"></a>');
  else
    $("#arrowspan").prepend('<span>next<img class="arrow" src="img/arrowrightlight.gif"></span>');

  if (pinfo.index > 0)
    $("#arrowspan").prepend('<a href="#page=p' + formatNum(pinfo.index-1, 3) + '"><img class="arrow" src="img/arrowleft.gif">prev</a>');
  else
    $("#arrowspan").prepend('<span><img class="arrow" src="img/arrowleftlight.gif">prev</span>');

  $("#arrowspan").prepend('<a href="#page=s' + formatNum(pinfo.page, 2) + 'hme">close<img class="arrow" src="img/closex.gif"></a>');
}

function ViewMenu(mnu) {
  $("span.menu").removeClass("underline");
  $("#" + menuID[mnu]).addClass("underline");
  $("#description").load(menuURL[mnu]);
}

// Global state -- hopefully not much of this

var Page, Mode, Painting, Menu;
var ClosePainting = function () {};

// Dispatcher

function dispatcher(hash) {
  // the state stores our state
  // mode (s - swatches, p - painting)
  // page : 2 digits
  // menu : 3 char abv (hme, abt, nws, fds)
  // painting : 3 digits
  // swatches: s page menu
  // painting: p painting
  // default mode is #s01hme

  // if we've come to the page fresh, then we can just draw the state.
  // but if we're coming to the page from a click within the page, we
  // need to make an animated transition.  there are too many states
  // for a manual finite state machine in the table-driven sense.

  // transitions: 
  // page / menu <-> page / menu
  // page / menu <-> painting
  // painting <-> painting

  var state;
  if (hash.length == 0) state = 's01hme';
  else state = hash;

  var newMode, newPage, newPainting, newMenu;
  newMode = state.charAt(0);
  if (newMode == 's') {
    newPage = Number(state.slice(1,3));
    newMenu = state.slice(3,6);
  } else if (newMode == 'p') {
    newPainting = Number(state.slice(1,4));
    pinfo = all_paintings[newPainting];
  }

  if (newMode == 's') {
    ClosePainting();
    ResetViews();
    ShowSwatches(newPage, newMenu);
  } else if (newMode == 'p') {
    if (Mode && Mode == 'p') {
      ShowPainting(all_paintings[newPainting])
    } else {
      if (Page && Page != pinfo.page) {
	if (Menu) 
	  ShowSwatches(pinfo.page, Menu);
	else
	  ShowSwatches(pinfo.page, 'hme');
      }
	
      AnimateToPainting(pinfo)
    }
  }

  Mode = newMode;
  if (newPage) Page = newPage;
  if (newMenu) Menu = newMenu;
  if (newPainting) Painting = newPainting;

  Chain(all_paintings, LoadPainting);
}

// Initialization

$(document).ready(function () { 
  HideSwatches();

  var bookmarkedstate = YAHOO.util.History.getBookmarkedState("page");
  var state = bookmarkedstate || 's01hme';
  Mode = state.charAt(0);
  if (Mode == 's') {
    Page = Number(state.slice(1,3));
    Menu = state.slice(3,6);
  } else if (Mode == 'p') {
    Painting = Number(state.slice(1,4));
    pinfo = all_paintings[Painting];
  }

  if (Mode == 's') {
    ResetViews();
    ShowSwatches(Page, Menu);
  } else if (Mode == 'p') {
    ShowPainting(all_paintings[Painting]);
  }

  //Chain(all_paintings.slice(1,5), LoadPainting);

  YAHOO.util.History.register("page", state, dispatcher); 
  YAHOO.util.History.initialize("yui-history-field", "yui-history-iframe"); 
});

// General purpose utilities

// would add this to Array.prototype but it messes with enumeration
function shuffle(arr) {
  for (var i = 0; i < arr.length; i++) {
    var idx = Math.round(Math.random() * (arr.length - 1)),
      swap = arr[idx];
    arr[idx] = arr[i];
    arr[i] = swap;
  }
  return arr;
}

// For chaining functions with callbacks
function Chain(seq, fn, next) {
  var clos = function() {};
  if (seq.length > 1)
    clos = function() { Chain(seq.slice(1), fn, next) };
  else if (seq.length == 1 && typeof(next) != 'undefined')
    clos = next;
  fn(seq[0], clos);
}

function FadeInSeq(idxs, next) {
  Chain(shuffle(idxs), 
	function(id, clos) {
	  $("#view" + id).fadeIn(20, clos);
	}, next);
}

function FadeOutSeq(idxs, keep, next) {
  Chain(shuffle(jQuery.grep(idxs, function (e, i) { return e != keep })), 
	function(id, clos) {
	  $("#view" + id).fadeOut(20, clos);
	}, next);
}
