// This is so we can expose our flash callback outside the closure.
var onVideoEnd, onVideoLoaded;
// The closure containing all our code. Any vars declared in here will actually
// shadow existing values rather than overwriting them.
(function($) {
  
  var nav = {
    current: 0,
    previous: 0,
    previousPosition: 0,
    position: 0,
    increment: 0,
    previousIncrement: 0,
    setup: function() {
      // Hide the navbox, so we can reveal it later.
      nav.container = $("#navBox");
      nav.container.css({opacity: 0});
      nav.node      = $("#cleverNav");
      nav.entries   = nav.node.find("a");
      // Get the top level links
      nav.sections  = nav.node.find("li > a");
      nav.sections[0].className = "current";
      // Attach events to the anchors in the navigation
      nav.node.click(nav.onNavClick);
      // Add some back and forward links.
      nav.container.prepend('<p id="back"><a href="#back" id="backLink">Back</a></p>');
      nav.container.append('<p id="forward"><a href="#forward" id="forwardLink">Forward</a></p>');
      $("#backLink, #forwardLink").click(nav.onControlClick);
      // Build the nub and make it draggable
      nav.nub = $("<div id=\"nub\"><span>Control</span></div>");
      nav.container.prepend(nav.nub);
      nav.nub.draggable({
        axis:         "x",
        containment:  nav.node,
        drag:         nav.nubOnDrag,
        start:        nav.nubOnDragStart,
        stop:         nav.nubOnDragStop
      });
    },
    // This function handles anything to be done post setup
    start: function() {
      nav.container.animate({opacity: 1});
    },
    // allows us to initialise the nav to the correct panel
    loadHistory: function(hash) {
      // Find the entry that has this matching hash
      if (hash != nav.currentHash) {
        for (var i=0; i < nav.entries.length; i++) {
          if (nav.entries[i].href.match(hash)) {
            nav.currentHash = hash;
            nav.updatePosition(i, false);
            return;
          }
        };
      }
    },
    // Stores the history in the hash
    setHistory: function(entry) {
      var path = entry.href.match("http://[A-Za-z0-9.:]+(/[A-Za-z0-9.:_/]+)")[1];
      
      window.setTimeout(function() {$.history.load(path)}, 2000);
      // Send the path to google analytics
      pageTracker._trackPageview(path);
    },
    // Handle clicks on the back and forward controls
    onControlClick: function() {
      if (this.id == "backLink" && nav.current > 0) {
        nav.updatePosition(nav.current - 1);
      }
      else if (this.id == "forwardLink" && nav.current < (nav.entries.length - 1)) {
        nav.updatePosition(nav.current + 1);
      }
      return false;
    },
    // Handles any clicks on the navigation
    onNavClick: function(event) {
      // Loop through the entries to find the one that matches the 
      // target. We then store it's index. This makes it easier to 
      // handle back/forward by incrementing or decrementing the index.
      if ($(event.target).is("a")) {
        for (var i=0; i < nav.entries.length; i++) {
          if (nav.entries[i] == event.target) {
            nav.updatePosition(i);
            break;
          }
        };
      }
      return false;
    },
    // Moves the nub and the slider
    updatePosition: function(newPosition, updateHistory) {
      nav.previousPosition = newPosition * 1000;
      nav.position = nav.previousPosition;
      
      var current = nav.entries[newPosition];
      nav.moveNub(current, 1500);
      panels.slide(current, 1500);
      nav.previous = nav.current;
      nav.current = newPosition;
      nav.highlightSection(current);
      
      // Update the history
      //if (updateHistory != false) nav.setHistory(current);
    },
    // Adds a 'current' highlight to the top level link in a particular section
    highlightSection: function(entry) {
      nav.sections.removeClass("current");
      if (entry.id) {
        $(entry).addClass("current");
      }
      else {
        // This is a filthy hack
        var section = $(entry).parent().parent().parent().find("a:first");
        section.addClass("current");
      }
    },
    // Moves the nub to the specified position, toggling the direction arrow
    moveNub: function(target, speed) {
      var position = $(target).position()["left"];
      nav.setNubPointer(position, nav.nub.position()["left"]);
      // Move it and reset the background when we're done
      speed = speed || 1500;
      nav.nub.animate({left: position + "px"}, speed, function() {
        nav.nub.css("background-position", "center top");
        nav.nubPosition = nav.nub.position().left;
      });
    },
    // Set the direction for the pointer based on the nub movement
    setNubPointer: function(position, current) {
      var alignment = position > current ? "right top" : "left top";
      nav.nub.css("background-position", alignment);
    },
    // Handles the drag event.
    nubOnDrag: function(event, ui) {
      nav.setNubPointer(ui.position.left, nav.nubPosition);
      nav.nubPosition = ui.position.left;
      // Percentage in the nav
      var left = $(this).position()["left"] - 55;
      var percentage = (left / nav.width) * 100;
      nav.position = panels.width * (percentage / 100);
    },
    nubOnDragStart: function() {
      // Kill any existing animations
      nav.nub.stop();
      panels.list.stop();
      // Start moving the panels with easing
      nav.dragging = true;
      nav.ease();
    },
    // Tidies up once the nub has finished dragging. We set the background on the nub.
    // Set the current panel and move the UI into the correct position.
    nubOnDragStop: function(event, ui) {
      nav.nub.css("background-position", "center top");
      nav.dragging = false;
      // Determine the current panel â€” this is based on the panel which takes up the
      // marjority of the panel.
      var current = Math.ceil(nav.position / 1000);
      var remainder = nav.position % 1000;
      if (remainder < 500 && remainder != 0) current -= 1;
      nav.previous = nav.current;
      nav.current = current;
      // Figure out how fast we were travelling when we stopped.
      var movement = Math.abs(nav.increment - nav.previousIncrement);
      var distance = Math.abs((current * 1000) - nav.previousPosition);
      var speed = Math.abs(distance / movement);
      // Make sure it's never too slow or too fast.
      if (speed > 1000) {
        speed = 1000;
      }
      else if (speed < 350) {
        speed = 350;
      }
      // Stash the previous position so the next time we start a drag it goes from the 
      // correct position
      nav.previousPosition = nav.current * 1000;
      nav.position = nav.previousPosition;
      // Highlight the current section
      nav.highlightSection(nav.entries[nav.current]);
      // Move the UI elements to the correct spot
      panels.slide(nav.entries[nav.current], speed);
      nav.moveNub(nav.entries[nav.current], speed);
      //nav.setHistory(nav.entries[nav.current]);
    },
    // This eases the animation of the panels - the position always lags a little behind 
    // the relative position of the nub.
    ease: function() {
      if (nav.dragging) {
        nav.previousIncrement = nav.increment;
        // Check to see which direction we need to go into
        if (nav.position < nav.previousPosition) {
          nav.increment = (nav.previousPosition - nav.position) * 0.05;
          var position = nav.previousPosition - nav.increment;
        }
        else {
          nav.increment = (nav.position - nav.previousPosition) * 0.05;
          var position = nav.increment + nav.previousPosition;
        }
        var position = Math.floor(position);
        // Move to the new position
        panels.list.css({left: "-" + position + "px"});
        nav.previousPosition = position;
        window.setTimeout(nav.ease, 10);
      }
    }
  };
  
  var panels = {
    data: {},
    pending: [],
    setup: function(entries) {
      panels.container = $("#panels");
      panels.list = $(document.createElement("ul"));
      panels.container.append(panels.list);
      panels.build(entries);
      panels.preload();
    },
    // Runs various tasks post setup
    start: function() {
      panels.list.animate({left: 0}, 1500);
    },
    // Builds the actual LIs that go inside the panel list. For each anchor, it 
    // generates the stub LI, positions it and puts it inside the list. It then 
    // creates a reference to it in the data hash, so that it can be used later
    // when handling loading and scrolling.
    build: function(links) {
      // Go on and build panels for all the other entries
      panels.count = links.length;
      panels.complete = 0;
      for (var i = 0; i < links.length; i++) {
        // Create, position and append panel
        var li = $(document.createElement("li"));
        var left = 1000 * i;
        li.css({left: left + "px"});
        panels.list.append(li);
        // Store a reference to the panel and the anchor to be used in the event
        // handler.
        panels.data[links[i]] = {panel: li, count: i, loaded: false, href: links[i].href};
        // Add each entry to the pending array as well
        panels.pending.push(panels.data[links[i]]);
      };
    },
    // Pulls the first entry out of the pending array and loads it. As long as there
    // are objects in the queue panels.load() will repeatedly call preload.
    preload: function() {
      // We could potentially have a simple locking scheme. We have a flag indicating 
      // that a ajax call is going back to the server, in which case we just skip 
      // loading anything. This works because the load() function always checks to see
      // if there is more to be preloaded and calls this function again.
      if (!panels.loading) {
        var entry = panels.pending.shift();
        if (!entry.loaded) panels.load(entry);
      }
    },
    // Returns a percentage representing the amount of panels that have successfully 
    // loaded.
    progress: function() {
      return (panels.complete / panels.count) * 100;
    },
    // Loads the contents of a panel, sourcing the URL from the anchor passed in.
    // Once loading is completed, it marks the panel as loaded in the data hash. 
    // Additionally a callback may be optionally passed in. This will be executed
    // once the panel has complated loading.
    load: function(entry, callback) {
      panels.loading = true;
      entry.panel.load(entry.href + "_partial.html", null, function() {
        panels.loading = false;
        entry.loaded = true;
        panels.complete += 1;
        if (panels.pending.length > 0) panels.preload();
        if (callback) callback();
      });
    },
    // Moves to the specified panel based on the anchor passed in. If the panel 
    // hasn't been loaded, it will go off to the server and grab it instead. Once
    // it is loaded, it will then complete the animation.
    slide: function(anchorOrEntry, time) {
      panels.list.stop();
      var entry = anchorOrEntry.panel ? anchorOrEntry : panels.data[anchorOrEntry];
      var position = entry.count * 1000;
      if (entry.loaded) {
        // Calculate how long it should take
        if (!time) {
          var offset = Math.abs(nav.current - nav.previous);
          var time = 500 * offset;
          if (time > 5000) 
            time = 5000;
          else if (time < 1500) {
            time = 1500;
          }
        }
        // Animate it
        panels.list.animate({left: "-" + position + "px"}, time);
      }
      else {
        // We pass in a callback to the loadPanel function. It triggers the panel
        // to slide after it's been loaded. It also checks to see if there are any
        // more panels that need to be preloaded. This is basically the mechanism
        // that lets a request be prioritised over those that are queued.
        panels.load(entry, function() { panels.slide(entry); });
      }
    }
  };
  
  var loading = {
    windowLoaded: false,
    show: function() {
      if (!loading.node) {
        loading.node = $("<div id=\"loading\"></div>");
        loading.display = $("<p>LOADING 0%</p>");
        loading.bar = $("<div>&nbsp;<div>");
        loading.node.append(loading.display);
        loading.node.append(loading.bar);
        $("#panels").append(loading.node);
      }
      else {
        loading.node.css({opacity: 1, display: "block"});
      }
    },
    hide: function() {
      loading.node.animate({opacity: 0}, 2000, function() {
        loading.node.css({display: "none"});
        loading.callback();
      });
    },
    update: function(progress, callback) {
      loading.progress = loading.progress || progress;
      loading.callback = loading.callback || callback;
      var status = loading.progress();
      if (status < 100 || !video.loaded || !loading.windowLoaded) {
        var percentage = Math.ceil(status);
        loading.display.html("LOADING " + percentage + "%");
        var offset = 1000 - (168 * (percentage / 100));
        // Figure out where we need to position the bar background
        loading.bar.css({"background-position": "-" + offset + "px -432px"});
        window.setTimeout(loading.update, 50);
      }
      else {
        video.reveal();
        loading.hide();
      }
    }
  };
  
  var video = {
    loaded: false,
    setup: function() {
      video.container = $('<div id="video"></div>');
      $("#panels").prepend(video.container);
      video.container.css({left: "-1000px"});
      // Insert the flash into the page
      var fo = new SWFObject("images/introduction.swf", "introduction", "1000", "430", "8", "#FFF");
      fo.write("video");
      video.node = $("#introduction")[0];
      if (!video.node) video.loaded = true;
    },
    reveal: function() {
      video.container.css({left: "0"});
      if (video.node) video.node.revealVideo();
    },
    play: function() {
      if (video.node) {
        video.node.startVideo();
      }
      else {
        window.setTimeout(video.end, 2000);
      }
    },
    end: function() {
      video.container.animate({left: "-1000px"}, 1500, function() {
        nav.start();
        contact.setup();
        video.container.remove();
        $.history.init(nav.loadHistory);
      });
      panels.start();
    },
    onLoaded: function() {
      video.loaded = true;
    }
  };
  // Expose the hide function to the flash object â€” outside this closure
  onVideoEnd = video.end;
  onVideoLoaded = video.onLoaded;
  
  var contact = {
    setup: function() {
      contact.australianList = $("#contactList #australianCities");
      contact.australianList.css({opacity: 0, display: "block"});
      
      $("#contactList").click(function(event) {
        var target = $(event.target);
        if (target.is("a")) {
          if (target.attr("href").match(/#/)) {
            $("#contactList #australianCities").animate({opacity: 1});
            return false;
          }
          else {
            $("#contactList #australianCities").animate({opacity: 0});
          } 
        }
      });
    }
  };
  
  // Begin preloading elements and display a loading screen
  $(document).ready(function() {
    // Override styles specifically for the JS version
    $("body").addClass("javascriptEnabled");
    // Insert loading panel
    loading.show();
    video.setup();
    nav.setup();
    panels.setup(nav.entries);
    // Figure out the relative values for the control and the slider panels
    var count = panels.list.find("li").length;
    panels.width = count * 1000;
    nav.width = count * 23;
    // Update the loading panel
    loading.update(panels.progress, video.play);
  });
  
  $(window).load(function() {
    loading.windowLoaded = true;
  });
})(jQuery);

// .history.init(callback);
//     $("a[@rel='history']").click(function(){
//         $.history.load(this.href.replace(/^.*#/, ''));
//         return false;
//     });