$(document).ready(function() {

window.ToggleView = Spine.Class.create({
  init: function () {

    this.element = $(arguments[0])

    var eventPrefix = arguments[1]

    $.subscribe( eventPrefix + ":show",$.proxy(function() {
      this.show()
    },this));

    $.subscribe( eventPrefix + ":hide",$.proxy(function() {
      this.hide()
    },this));

    $.subscribe( eventPrefix + ":toggle",$.proxy(function() {
      this.toggle()
    },this));

  },
  show: function() {
    this.element.show()
  },
  hide: function() {
    this.element.hide()
  },
  toggle: function() {
    this.element.toggle()
  }
});

window.Footer = ToggleView.sub({
  init: function() {
    this.constructor.__super__.init.apply(this, arguments);

    this.progressView = $("#slideInfo")
    $.subscribe("presentation:slide:didChange",$.proxy(this.updateProgress,this));
  },
  progressPercentage: function (slideIndex,slideCount) {
    return Math.ceil(((slideIndex + 1) / slideCount) * 100)
  },
  updateProgress: function(event,slideIndex,slideCount) {
    percent = this.progressPercentage(slideIndex,slideCount);
    this.progressView.text((slideIndex + 1) + '/' + slideCount + '  - ' + percent + '%')
  }
});

window.HelpMenu = ToggleView.sub();

window.PauseScreen = ToggleView.sub();

window.DebugView = ToggleView.sub({
  log: function(text) {
    this.element.text(text);
  }
});

window.Slide = Spine.Class.create({
  index: 0,
  maxSlides: 0,
  init: function() {
    this.htmlContent = arguments[0];
    this.index = arguments[1];
    this.maxSlides = arguments[2];

    this.transition = this.htmlContent.attr('data-transition');
    this.sequence = parseInt(this.htmlContent.attr('data-sequence'));
    this.title = this.htmlContent.attr('data-title');

    if (this.title === undefined) {
      this.title = this.htmlContent.text().trim().split("\n")[0];

      if (this.title.length > 20) {
        this.title = this.title.substr(0,15) + "...";
      }

    }

    this.sections = [];

    if (this.htmlContent.attr('data-sections') != undefined) {
      this.sections = this.htmlContent.attr('data-sections').split(',');
    }

    this.increments = this.htmlContent.find(".incremental > ul > li");
    this.currentIncrement = 0;

    if (this.increments.size() == 0) {
      this.increments = this.htmlContent.find(".incremental pre pre")
      this.codeIncremental = true;
    }

    this.increments.each(function(index, increment) {
      $(increment).css('visibility', 'hidden');
    });

  },
  currentIncrement: 0,
  increment: function(incrementIndex) {
    var increment = this.increments.eq(incrementIndex);

    if (this.codeIncremental && increment.hasClass('command')) {

      var typeInputOverDuration = increment.find('.input').text().length / 24;

      increment.find('.prompt').css('visibility', 'visible');
      increment.find('.input').css('visibility', 'visible').jTypeWriter({duration:typeInputOverDuration});
    } else {
      increment.css('visibility', 'visible');
    }

  },
  nextIncrement: function() {
    this.increment(this.currentIncrement);
    this.trigger("parade:incr");
    this.currentIncrement ++;
  },
  hasIncrement: function() {
    return (this.currentIncrement < this.increments.size());
  },
  showAllIncrements: function() {
    this.increments.each(function(index, increment) {
      $(increment).css('visibility', 'visible');
    });
    this.currentIncrement = this.increments.size();
  },
  notes: function() {
    return this.htmlContent.find("p.notes").text();
  },
  trigger: function(eventName) {
    var event = jQuery.Event(eventName);
    this.htmlContent.find(".content").trigger(event,this);
    return event;
  }
});

window.Slides = Spine.Class.create({
  init: function() {
    this.element = $("#slides");
    this.load(arguments[0],arguments[1]);
    this
  },
  show: function() {
    this.element.show()
  },
  hide: function() {
    this.element.hide()
  },
  load: function(options,afterLoad) {

    var slidesURL = options.urlPrefix + "/slides?height=" + options.height + "&width=" + options.width;

    $("#slides").load(slidesURL,$.proxy(function() {
      this.slides = $("#slides > .slide");
      afterLoad();
    },this));

  },
  size: function() {
    return this.slides.size();
  },
  at: function(index) {
    return new Slide($(this.slides.eq(index)),index,this.size());
  },
  find: function(query) {
    var regexQuery = new RegExp(query,"i");
    var matchedSlideNumbers = [];
    for (var index = 0; index < this.size(); index++) {
      var slide = this.at(index);
      // console.log(slide.title + " " + regexQuery);
      if (regexQuery.exec(slide.title)) { matchedSlideNumbers.push(slide.sequence); }
    }
    console.log("Found: " + matchedSlideNumbers);
    return matchedSlideNumbers;
  },
  findClosestToQuery: function(position,query) {
    console.log('Finding the query `' + query + '` next to position ' + position);
    var matchingSlides = this.find(query);

    var closestPosition = matchingSlides[0];
    console.log('Defaulting to ' + closestPosition);

    for (var mI = 0; mI < matchingSlides.length; mI++) {
      if (matchingSlides[mI] > position) {
        console.log('Setting movement to ' + matchingSlides[mI]);
        closestPosition = matchingSlides[mI];
        break;
      }
    }

    return closestPosition;
  }
});

window.NavigationMenu = ToggleView.sub({
  init: function() {
    this.constructor.__super__.init.apply(this,arguments);
    this.menuView = $("#navigation");
  },
  populate: function(slides) {

    var menu = new ListMenu();

    for (var i = 0; i < slides.size(); i++) {
      var slide = slides.at(i);
      var sectionsMinusRoot = slide.sections.slice(1,slide.sections.length);
      sectionsMinusRoot.push(slide.sequence);
      menu.addItem(sectionsMinusRoot, slide.title, slide.sequence);
    }

    this.menuView.html(menu.getList());

    this.element.menu({
      content: this.menuView.html(),
      flyOut: true,
      width: 200
    });

  },
  toggle: function() {
    this.constructor.__super__.toggle.apply(this,arguments);
    this.open();
  },
  show: function() {
    this.constructor.__super__.show.apply(this,arguments);
    this.open();
  },
  open: function() {
    this.element.trigger('click');
  },
  createMenu: function(slide) {
    console.log(this);
    function menu(slide) {
      console.log(this);
    }
  }
});

function ListMenu(slide) {
  this.slide = slide
  this.typeName = 'ListMenu'
  this.itemLength = 0;
  this.items = new Array();

  this.addItem = function (key, text, slide) {
    if (key.length > 1) {
      thisKey = key.shift()
      if (!this.items[thisKey]) {
        this.items[thisKey] = new ListMenu(slide)
      }
      this.items[thisKey].addItem(key, text, slide)
    } else {
      thisKey = key.shift()
      this.items[thisKey] = new ListMenuItem(text, slide)
    }
  }

  this.getList = function() {
    var newMenu = $("<ul>");
    for(var i in this.items) {
      var item = this.items[i];
      var domItem = $("<li>");
      if (item.typeName == 'ListMenu') {
        choice = $("<a rel=\"" + (item.slide - 1) + "\" href=\"#\">" + i + "</a>");
        domItem.append(choice);
        domItem.append(item.getList());
      }
      if (item.typeName == 'ListMenuItem') {
        choice = $("<a rel=\"" + (item.slide - 1) + "\" href=\"#\">" + item.slide + '. ' + item.textName + "</a>");
        domItem.append(choice);
      }
      newMenu.append(domItem);
    }
    return newMenu;
  }

}

function ListMenuItem(t, s) {
  this.typeName = "ListMenuItem"
  this.slide = s
  this.textName = t
}

window.Presentation = Spine.Class.create({
  slidenum: 0,
  presentationFrame: $("#preso"),

  footer: new Footer("#footer","presentation:footer"),
  helpMenu: new HelpMenu("#help","help"),
  pauseScreen: new PauseScreen("#pauseScreen","presentation:pause"),
  debugView: new DebugView("#debugInfo","debug"),
  navigationMenu: new NavigationMenu("#navmenu","presentation:navigation"),

  init: function() {

    var options = { height: this.presentationFrame.height(),
    width: this.presentationFrame.width(),
    urlPrefix: arguments[0] };

    this.slides = new Slides(options,$.proxy(function() {
      this.start();
    },this));
  },
  start: function() {
    console.log("Initializing Presentation");
    this.slides.show();

    this.centerSlides();

    // Copy the slides into the presentation area
    this.presentationFrame.empty();
    this.slides.slides.appendTo(this.presentationFrame);

    // Create the jquery cycle in the presentation frame
    this.presentationFrame.cycle({
      timeout: 0
    });

    this.navigationMenu.hide();

    this.navigationMenu.populate(this.slides);

    $.subscribe("presentation:slide:location:change",$.proxy(function(event,slideIndex) {
      this.gotoSlide(slideIndex);
    },this));

    this.showFirstSlide();

    this.presentationFrame.trigger("parade:loaded");

    $.subscribe("presentation:slide:next",$.proxy(function() {
      this.nextStep();
    },this));

    $.subscribe("presentation:slide:previous",$.proxy(function() {
      this.prevStep();
    },this));

    $.subscribe("presentation:reload",$.proxy(function() {
      // this.loadSlides(loadSlidesBool, loadSlidesPrefix)
      this.loadSlides()

      // FIXME: currently the reload is making the slides appear lower
      //   then where they are originally.
      this.slidenum = 0
      this.showSlide()
    },this));

  },
  centerSlides: function() {
    var presentation = this;
    this.slides.slides.each(function(s,slide) {
      presentation.centerSlide(slide);
    });
  },
  centerSlide: function(slide) {
    var slideObject = $(slide);
    var slideContent = slideObject.children(".content.vertical-center").first();

    if (slideContent) {
      var height = slideContent.height();
      var marginTop = (0.5 * parseFloat(slideObject.height())) - (0.5 * parseFloat(height));
      if (marginTop < 0) {
        marginTop = 0;
      }
      slideContent.css('margin-top', marginTop);
    }

  },
  showFirstSlide: function() {
    this.slidenum = WindowToSlideLocation.location();
    this.updateSlide();
    this.showSlide()
  },
  gotoSlide: function(slideNum) {
   this.slidenum = parseInt(slideNum)
   if (!isNaN(this.slidenum)) {
     this.showSlide()
   }
  },
  slideTotal: function() {
    return this.slides.size();
  },
  nextSlide: function() {
    this.slidenum ++;
    this.updateSlide();
    this.showSlide();
  },
  previousSlide: function() {
    this.slidenum --;
    this.updateSlide();
    this.currentSlide.transition = "none";
    this.currentSlide.showAllIncrements();
    this.showSlide();
  },
  updateSlide: function() {
    this.currentSlide = this.slides.at(this.slidenum);
  },
  showSlide: function() {

    if(this.slidenum < 0) {
      this.slidenum = 0;
      return;
    }

    var maxSlides = this.slideTotal();

    if(this.slidenum > (maxSlides - 1)) {
      this.slidenum = maxSlides - 1;
      return;
    }

    var currentSlide = this.currentSlide;
    var futureSlide = this.slides.at(this.slidenum);

    var transition = futureSlide.transition;

    // currentSlide.trigger("parade:willDisappear");
    // futureSlide.trigger("parade:willAppear");

    $.publish('code:execution:clear');
    $.publish("presentation:slide:willChange",[ this.slidenum, maxSlides ]);
    this.presentationFrame.cycle(this.slidenum, transition);
    this.currentSlide = futureSlide;
    $.publish("presentation:slide:didChange",[ this.slidenum, maxSlides ]);

    currentSlide.trigger("parade:didDisappear");

    this.presentationFrame.css({'width' : '', 'overflow' : ''});

    WindowToSlideLocation.updateLocation(this.slidenum + 1);

    // futureSlide.trigger("parade:didAppear");
    futureSlide.trigger("parade:show");

    return futureSlide.notes();
  },
  nextStep: function() {

    var triggeredEvent = this.currentSlide.trigger("parade:next");

    if (triggeredEvent.isDefaultPrevented()) {
      return;
    }

    if (this.currentSlide.hasIncrement()) {
      this.currentSlide.nextIncrement();
    } else {
      this.nextSlide();
    }
  },
  prevStep: function() {

   var triggeredEvent = this.currentSlide.trigger("parade:prev");

   if (triggeredEvent.isDefaultPrevented()) {
       return;
   }

   this.previousSlide();
  },
  debug: function(text) {
    this.debugView.log(text);
  }
});

window.WindowToSlideLocation = Spine.Class.sub();
WindowToSlideLocation.location = function() {
  var result;
  if (result = window.location.hash.match(/#([0-9]+)/)) {
    return result[result.length - 1] - 1;
  } else {
    return 0;
  }
}
WindowToSlideLocation.updateLocation = function(newLocation) {
  window.location.hash = newLocation;
}

window.LocationWatcher =  Spine.Class.create({
  init: function() {
    var presentation = this;
    this.watchEnabled = false;
    this.lastResult = this.windowLocation();

    $.subscribe("presentation:slide:didChange",$.proxy(function(event,slideCount,maxSlides) {
      this.lastResult = slideCount;
    },this));

  },
  start: function() {
    if (this.watchEnabled === false) {
      this.watchEnabled = true;
      this.perform();
    }
  },
  stop: function() {
    this.watchEnabled = false;
  },
  perform: function() {
    var currentResult = this.windowLocation();
    if (this.lastResult != currentResult) {
      $.publish("presentation:slide:location:change",currentResult);
    }

    this.lastResult = currentResult;

    if (this.watchEnabled) {
      setTimeout($.proxy(this.perform,this), 200);
    }
  },
  windowLocation: function() {
    return WindowToSlideLocation.location();
  }
});

});