// Shining - http://shining.heroku.com
// Copyright (c) 2010 Julio Cesar Ody - See LICENSE.txt
(function($) {
  var KEY = { SPACE: 32, RIGHT: 39, LEFT: 37 };

  Shining = {
    // slides
    slides: {
      length:   function() { return this._slides.length; },
      current:  function() {
        if (arguments.length) {
          this._current = this._slides.indexOf(arguments[0]);
          return this.current();
        } else {
          return (typeof this._current == 'undefined' ? this._slides[0] : this._slides[this._current]);
        }
      },
      all:      function() { return this._slides; },
      first:    function() { return this._slides[0]; },
      last:     function() { return this._slides[ this._slides.length - 1 ]; },
      next:     function() { return this._slides[ this._slides.indexOf(this.current()) + 1 ]; },
      previous: function() {
        var previous = this._slides[ this._slides.indexOf(this.current()) - 1 ];
        return previous || this.first();
      },
      add:      function(slides)  { return Array.prototype.push.apply(this._slides, slides); },
      loaded:   function(name)    { return !!Shining.slides._loaded[name]; },
      totalLoaded: function() { var len = 0; for (var i in this._loaded) len++; return len; },
      _slides: [],
      _loaded: {},
      _current: 0
    },

    // public methods
    firstSlide:     function() { playSlide(Shining.slides.first()); },
    lastSlide:      function() { playSlide(Shining.slides.last()); },
    nextSlide:      function() { trigger('nextslide'); },
    previousSlide:  function() { trigger('previousslide'); },
    setTheme:       setTheme,
    playSlide:      playSlide,
    help:     help,
    note:     note,
    when:     when,
    trigger:  trigger,
    pluginProcesses: {},
    centerSlide: centerSlide,

    // slide scripts
    scripts: {
      LINE: /^(\d[.\d]*),[\s]*(.*)/, parsed: [], processes: [],
      nextSlide: function() { Shining.nextSlide(); },
      previousSlide: function() { Shining.previousSlide(); },
      at: function(seconds, method) {
        Shining.scripts.processes.push(setTimeout(method, parseFloat(seconds) * 1000));
      },
      parse: function(script) {
        var lines = script.split("\n"), tokens, parsed = [];
        for (var i = 0; lines.length > i; i++) {
          if (Shining.scripts.LINE.test(lines[i])) {
            tokens = lines[i].match(Shining.scripts.LINE);
            var time = tokens[1], code = tokens[2];
            parsed.push(time);
            if (code.length) parsed.push(code);
          } else {
            if (isNaN(parsed[parsed.length - 1])) {
              parsed[parsed.length - 1] += ("; " + lines[i]);
            } else {
              parsed.push(lines[i]);
            }
          }
        }
        return parsed;
      },
      runSlide: function(name) {
        var script = Shining.slides._loaded[name].script;
        this.run(script);
      },
      run: function(script) {
        if (typeof script == 'undefined' || script == '') return false;
        var parsed = Shining.scripts.parse(script), all = [];
        for (var i = 0; parsed.length > i; i += 2) {
          all.push(["at(", parsed[i], ", function() { ", parsed[i+1], " })"].join(''));
        }
        with(Shining.scripts) { eval(all.join(';')); };
      },
      reap: function() {
        $(Shining.scripts.processes).each(function() { clearTimeout(this); });
        return Shining.scripts.processes = [];
      }
    }
  };

  // Transitions
  var Transitions = {
    'fade': {
      enter: function() { $('div.slide').fadeIn(200); },
      leave: function() { $('div.slide').fadeOut(200); }
    },
    'none': {
      enter: function() { $('div.slide').show(); },
      leave: function() { $('div.slide').hide(); }
    }
  }

  function help(message, duration, force) {
    if (Shining.config.help == false && force != true) return false;
    $('#help').remove();
    return $('<div id="help"></div>')
      .html(message)
      .appendTo('body')
      .animate({opacity: 1})
      .delay(duration || 500)
      .fadeOut(200, function() { $('#help').remove(); });
  }
  
  function note(message, duration) {
    if (message == false) return $('#note').dequeue();
    $('#note').remove();
    return $('<aside id="note"></div>')
      .html(message)
      .appendTo('body')
      .animate({marginBottom: 0, opacity: 'toggle'})
      .delay(parseInt(duration, 10) || 500)
      .animate({marginBottom: 10, opacity: 'toggle'});
  }
  
  function trigger(name, data)  { $(document).trigger(name + '.shining', data); }
  function when(name, method)   { $(document).bind(name + '.shining', method); }

  function setTitle(title) {
    if (!title || title == '') return false;
    return $.browser.msie ? document.title = title : $('title').text(title);
  }

  function setTheme(name) {
    if (!name || name == "") { name = "default" };
    if ($('link.theme').length) {
       $('link.theme').attr('href', "vendor/themes/" + name + ".css");
    } else {
      $('head').append('<link rel="stylesheet" href="vendor/themes/' + name + '.css" class="theme"/>');
    }
  }

  function slideFormat(name)  {
    if (hasNoExtension(name)) return false;
    var format = name.substr(name.lastIndexOf('.') + 1, name.length).toLowerCase();
    return format;
  }

  function slidePlusExtension(name, extension) {
    return hasNoExtension(name) ? name + '.' + extension : name.substr(0, name.lastIndexOf('.')) + '.' + extension;
  }

  function hasNoExtension(name) { return !(/\.\w+$/.test(name)) }

  function compileSlide(name, data) {
    if (!slideFormat(name)) return false;
    switch(slideFormat(name)) {
      case 'md':
      case 'markdown':
        return new Showdown.converter().makeHtml(data);
      default: // html
        return data;
    }
    return name;
  }
  
  function loadSlides(callback) {    
    $.getJSON(
      '/slides.json',
      function(slides) {
        Shining.slides._loaded = slides;
        for (slide in slides) {
          slides[slide].markup = compileSlide(slide, slides[slide].markup)
        };
        trigger('slidesloaded');
        if ($.isFunction(callback)) callback();
      }
    );
  }

  function playSlide(name) {
    $('body')
      .queue(function() { Transitions[Shining.config.transitions].leave(); $(this).dequeue(); })
      .queue(function() {
        Shining.scripts.reap();
        var slide = Shining.slides._loaded[name];
        Shining.slides.current(name);
        $('body').html('<div class="slide">' + slide.markup + '</div>');
        setTimeout(centerSlide, 500);
        trigger('slideplay', [name]);
        $(this).dequeue();
      })
      .queue(function() { Transitions[Shining.config.transitions].enter(); $(this).dequeue(); centerSlide(); });
  }

  function loadPlugins() {
    if (Shining.config.plugins && Shining.config.plugins.length) {
      $(Shining.config.plugins).each(function() {
        $('head').append('<script type="text/javascript" src="vendor/lib/plugins/' + this + '.js"></script>');
      })
    }
  }

  function loadSlideStyle(name) {
    $('link.slide').remove();
    $('head').append('<link rel="stylesheet" type="text/css" href="slides/' + slidePlusExtension(name, 'css') + '" media="all" class="slide"/>')
  }

  function loadConfig(callback) {
    $.getJSON('config.json', function(config) {
      Shining.config = config;
      Shining.config.transitions = Transitions[Shining.config.transitions] ? Shining.config.transitions : 'none';
      Shining.slides.add(config.slides);
      callback.call();
    });
  }

  function centerSlide() {    
    var top = ($(window).height() - $('div.slide:visible').outerHeight()) / 2;
    if (top < 0) top = 0;
    $('div.slide').css({ top: top });
  }

  function hasPendingStep() {
    return !!$('div.slide .step:not(:visible)').length;
  }

  function runPendingStep() {
    $('div.slide .step:not(:visible):first').show();
  }

  // Now configure everything!
  when('previousslide', function() {
    if (Shining.slides.previous()) {
      help('←');
      document.location.hash = Shining.slides.previous();
    }
  });

  when('nextslide', function() {
    help('→');
    if (hasPendingStep()) {
      runPendingStep();
    } else {
      if (Shining.slides.next()) document.location.hash = Shining.slides.next();
    }
  });

  when('slideplay', function(event, name) {
    note(false);
    $(window).trigger('resize');
    $(window).trigger('resize'); // until I figure out why once won't do
    loadSlideStyle(name);
    Shining.scripts.runSlide(name);
    if (SyntaxHighlighter) SyntaxHighlighter.highlight({gutter: false, toolbar: false});
    if ($('body aside').length) setTimeout(function() { note($('body aside').html(), 5000) }, 500);
  });

  when('slidesloaded', function() {
    help('← (previous slide), → or SPACE BAR (next slide)', 3000);
  })

  $(window)
    .resize(function() { centerSlide(); })
    .bind('hashchange', function() {
      var slide = document.location.hash.replace('#', '');
      if (slide) Shining.playSlide(slide);
    });

  $(document)
    .keydown(function(event) {
      switch(event.keyCode) {
        case KEY.RIGHT:
        case KEY.SPACE:
          Shining.nextSlide();
          break;
        case KEY.LEFT:
          Shining.previousSlide();
          break;
      }
    })
    .ready(function() {
      loadConfig(function() {
        loadPlugins();
        var startAt = document.location.hash.replace('#', ''), first = startAt || Shining.slides.current();
        loadSlides(function() { playSlide(first); });
        setTitle(Shining.config.title);
        setTheme(Shining.config.theme);
      });
    });
})(jQuery);