/*******************************************************************************
*  Copyright (c) 2007 TeamPages Inc.
* 
*  Reproduction or disclosure of this file or its contents without the
*  prior written consent of TeamPages is prohibited.
********************************************************************************/

var MONTHS = ['January','February','March','April','May','June','July','August',
              'September','October','November','December'];

var WEEKDAYS = ['Sunday', 'Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];

var TODAY = new Date();
TZ_OFFSET = TODAY.getTimezoneOffset() * 60000;
TODAY = TODAY.getTime() - (TODAY.getTime() % 86400000); // Today at 12am in ms

// -----------------------------------------------------------------------------
// Class: scrollingSchedule
// Description: The entire scroller, encompassing AJAX calls for events,
//        scroll methods, and maintaining the list of DateBlocks.
// -----------------------------------------------------------------------------
var scrollingSchedule = Class.create();
scrollingSchedule.prototype = {
  
  // -------------------------------------------------------------- initialize
  initialize: function(container, dates, entity_info, utc_offset)
  {
    UTC_OFFSET = parseInt(utc_offset);
    this.container = $(container);
    this.dates     = $H( this.fixDates( dates ) );
    this.blocks    = $H();
    this.schedules = $H();
    this.entity    = entity_info;
    this.storeByEntity(entity_info);
    this.contents_top = 0;
    this.container.update();
    this.draw();
    this.alignWithToday();
    this.disableButtons();
  },
  // ------------------------------------------------------------------ fixDates
  // This, dear reader, is a bit of a hack.  Our JSON format is a hash in format:
  //   {  YYYYMMDD => [event array], YYYYMMDD => [event array] }
  // However, it doesn't work as the date used is now always the PST one.  Now,
  // we generated the data structure with the user's local date.
  fixDates: function( dates )
  {
    var newHash = {};
    for( var date in dates )
    {
      for( var i = 0; i < dates[date].length; i++ )
      {
        var event = dates[date][i];
        event.start_datestamp = Date.parse8601( event.start_datestamp );
        var key = this.buildDateKey( event.start_datestamp );
        if( typeof( newHash[key] ) == 'undefined' )
        {
          newHash[key] = [];
        }
        newHash[key][newHash[key].length] = event;
      }
    }
    return newHash;
  },
  // -------------------------------------------------------------- buildDateKey
  buildDateKey: function( date )
  {
    return date.getFullYear() + date.paddedMonth() + date.paddedDate();
  },
  // ------------------------------------------------------------- storeByEntity
  storeByEntity: function(entity) {
    // Saves the list of dates/events by entity
    // Means that on League pages, each division's schedule would only be fetched once, max
    if (entity && entity.entity && entity.id)
    {
      if (!this.schedules.get(entity.entity))
      {
        this.schedules.set(entity.entity, $H());
      }
      this.schedules.get(entity.entity).set(entity.id, this.dates);
    }
  },
  // --------------------------------------------------------- drawEmptySchedule
  drawEmptySchedule: function() 
  {
    var div = $C('div').setStyle({padding: "5px"});
    if (this.entity.admin)
    {
      var a = $C('a', {href: "/events/new?"+this.entity.entity+"_id="+this.entity.id});
      a.appendChild( $T('Schedule your first event') );
      div.appendChild( a );
    }
    else
    {
      div.appendChild( $T('There are no events scheduled.') );
    }
    this.container.appendChild( div );
  },

  // -------------------------------------------------------------------------
  // CONSTRUCTOR METHODS -----------------------------------------------------
  // -------------------------------------------------------------------- draw
  draw: function()
  {
    if (this.dates.values().length == 0)
    {
      this.empty = true;
      this.drawEmptySchedule();
    }
    else
    {
      this.empty = false;
      
      this.container.appendChild( this.getScrollBar() );

      this.contents_window = this.getContentsWindow();
      this.contents = this.getContentsDiv();
      this.dates.each(function(pair)
      {
        this.addDateBlock( new DateBlock(parseInt(pair.key), pair.value) );
      }.bind( this ));
      this.container.appendChild( this.contents_window ).appendChild( this.contents );

      this.window_height = this.contents_window.getHeight();
    }
  },

  // ------------------------------------------------------------ getScrollBar
  getScrollBar: function()
  {
    var div = $C('div');
    div.addClassName('scroll_bar');
    div.appendChild( this.getTopScrollDiv() );
    div.appendChild( this.getBottomScrollDiv() );
    return div;
  },
  // ------------------------------------------------------- getContentsWindow
  getContentsWindow: function()
  {
    var div = $C('div');
    div.addClassName('content_window');
    return div;
  },
  
  // ---------------------------------------------------------- getContentsDiv
  getContentsDiv: function()
  {
    var div = $C('div');
    div.addClassName('contents');
    return div;
  },
  
  // --------------------------------------------------------- getTopScrollDiv
  getTopScrollDiv: function()
  {
    var div = $C('div');
    div.addClassName('scroll_button');
    div.addClassName('up');
    Event.observe(div, 'click', this.scrollUpEvent.bindAsEventListener(this));
    Event.observe(div, 'mouseover', this.mouseOverScrollButton.bindAsEventListener(this));
    Event.observe(div, 'mouseout', this.mouseOutScrollButton.bindAsEventListener(this));
    this.top_scroll_div = div;
    return div;
  },
  
  // ------------------------------------------------------ getBottomScrollDiv
  getBottomScrollDiv: function()
  {
    var div = $C('div');
    div.addClassName('scroll_button');
    div.addClassName('down');
    Event.observe(div, 'click', this.scrollDownEvent.bindAsEventListener(this));
    Event.observe(div, 'mouseover', this.mouseOverScrollButton.bindAsEventListener(this));
    Event.observe(div, 'mouseout', this.mouseOutScrollButton.bindAsEventListener(this));
    this.bottom_scroll_div = div;
    return div;
  },
  
  // -------------------------------------------------------------------------
  // FETCHING METHODS --------------------------------------------------------
  // ------------------------------------------------------------ addDateBlock
  addDateBlock: function(dateBlock)
  {
    var blockDates = this.blocks.keys().sort(); // Array of all timestamps currently drawn
    var dates_dates = this.dates.keys().sort();  // Array of all timestamps in event list
    var datesIndex = parseInt( dates_dates.indexOf( dateBlock.getDateStamp() ));
    
    if ((blockDates.length == 0) || (datesIndex == dates_dates.length-1) || (dateBlock.getDateStamp() > blockDates[blockDates.length-1]))
    {
      // dateBlock is last, or no previous dates, or is the newest to be drawn
      this.contents.appendChild( dateBlock.block );
    }
    else
    {
      // date_block has one below it, insert above
      for (var i = 0, len = blockDates.length; i < len; i++)
      {
        if (blockDates[i] > dateBlock.getDateStamp() )
        {
          this.blocks.get(blockDates[i]).block.insert({before: dateBlock.block});
          break;
        }
      }
    }
    
    this.blocks.set( dateBlock.getDateStamp() + '', dateBlock );
  },
  
  // ---------------------------------------------------------- alignWithToday
  alignWithToday: function()
  {
    var blockDates = this.blocks.keys().sort();
    var datestampToAlign = 0; // The datestamp we want to align to
    
    var today = new Date(); 
    var key = '' + today.getFullYear();
    var month = today.getMonth() + 1;
    key += '' + ( month > 9 ? month : '0' + month );
    key += '' + ( today.getDate() > 9 ? today.getDate() : '0' + today.getDate() );

    var todayString = today.getFull
    // Find the nearest [future/present] datestamp
    var dateStampToAlign = blockDates[blockDates.length - 1];
    for( var i = 0; i < blockDates.length; i++ )
    {
      var dateCode = blockDates[i];
      if( dateCode >= key )
      {
        dateStampToAlign = dateCode;
        break;
      }
    }
    
    this.alignToDatestamp(dateStampToAlign);
  },
  
  // -------------------------------------------------------- alignToDatestamp
  alignToDatestamp: function(datestamp)
  {
    if (!this.blocks.get(datestamp))
    {
      return false;
    }
    this.aligned_datestamp = datestamp;
    this.contents_top = -(this.blocks.get(datestamp).block.positionedOffset().top);
    this.setContentsOffsetTop();
    return true;
  },
  
  // ---------------------------------------------------- setContentsOffsetTop
  setContentsOffsetTop: function()
  {
    this.contents.morph('top: '+this.contents_top+'px;');
  },
  
  // ----------------------------------------------------------- loadNewEvents
  loadNewEvents: function(dates)
  {
    // Erases current schedule and draws new one
    this.dates = dates.clone();
    
    this.blocks = $H();
    this.contents_top = 0;
    this.container.update();
    this.draw();
    this.alignWithToday();
  },
  
  // -------------------------------------------------- AJAXgetEventsForEntity
  AJAXgetEventsForEntity: function(entity, options)
  {
    
    // "options" is technically optional
    if (typeof(options) != 'object')
    {
      options = {};
    }
    else if (options.initialize)
    {
      // Call an optional initialize function
      options.initialize();
    }
    
    // Has this entity's schedule already been fetched?
    if (this.schedules.get(entity.entity) && this.schedules.get(entity.entity).get(entity.id))
    {
      this.loadNewEvents( this.schedules.get(entity.entity).get(entity.id) );
      
      // Run the optional success function
      if (options.complete)
      {
        options.complete();
      }
      
      return;
    }
    
    var params={}, url;
    
    if (entity.entity == 'division')
    {
      url = '/division/AJAX_schedule_titles_json';
      params.division_id = entity.id
    }
    else if (entity.entity == 'league')
    {
      // League schedule AJAX fetching is not currently implemented (no need)
      return;
    }
    else if (entity.entity == 'club')
    {
      // Club schedule AJAX fetching is not currently implemented (no need)
      return;
    }
    else
    {
      return;
    }
    
    var successFunction = function(response) {
      if (!this.schedules.get(entity.entity)) {
        this.schedules.set(entity.entity, $H());
      }
      this.schedules.get(entity.entity).set(entity.id, $H( eval("("+response.responseText+")") ));
      this.loadNewEvents( this.schedules.get(entity.entity).get(entity.id) );
      
      // Run the optional success function
      if (options.complete) {
        options.complete();
      }
    };
    
    var failureFunction = function(response) {
      alert('error');
    };
    
      new Ajax.Request( url,
            { 
              method: 'get',
          parameters: params,
              evalScripts: true,
          evalJSON: true,
              onSuccess: successFunction.bind( this ),
          onFailure: failureFunction.bind( this )
            } );
  },
  
  // -------------------------------------------------------------------------
  // EVENT HANDLER METHODS ---------------------------------------------------
  // ----------------------------------------------------------- scrollUpEvent
  scrollUpEvent: function(event)
  {
    var blockDates = this.blocks.keys().sort();
    var blockIndex = blockDates.indexOf(this.aligned_datestamp);
    
    if (blockIndex == 0)
    {
      return;
    }
    
    this.alignToDatestamp(blockDates[blockIndex-1]);
    this.enableScrollButton( this.bottom_scroll_div );
    
    // Check to see if we've reached the top (disable the button)
    this.disableButtons();
  },
  
  // --------------------------------------------------------- scrollDownEvent
  scrollDownEvent: function(event)
  {
    var blockDates = this.blocks.keys().sort();
    var block_index = blockDates.indexOf(this.aligned_datestamp);
    
    if (block_index == blockDates.length-1)
    {
      return;
    }
    
    this.alignToDatestamp(blockDates[block_index+1]);
    this.enableScrollButton( this.top_scroll_div );
    
    // Check to see if we've reached the bottom (disable the button)
    this.disableButtons();
  },
  
  // --------------------------------------------------- mouseOverScrollButton
  mouseOverScrollButton: function(event)
  {
    Event.element(event).addClassName('hover');
  },
  
  // ---------------------------------------------------- mouseOutScrollButton
  mouseOutScrollButton: function(event){
    Event.element(event).removeClassName('hover');
  },
  
  // ---------------------------------------------------------- disableButtons
  disableButtons: function()
  {
    if (this.empty)
    {
      return;
    }
    var blockDates = this.blocks.keys().sort();
    var datePosition = blockDates.indexOf(this.aligned_datestamp);
    if ( datePosition == 0)
    {
      this.disableScrollButton( this.top_scroll_div );
    }
    if (datePosition == blockDates.length-1)
    {
      this.disableScrollButton( this.bottom_scroll_div );
    }
  },
  // ------------------------------------------------------- disableScrollButton
  // css classes refused to work properly, so here's this to help
  disableScrollButton: function( button )
  {
    var isUpButton = button.hasClassName("up");
    var img;
    if( isUpButton )
    {
      img = '/images/icons/schedule_scroll_up_fade.gif';
    }
    else
    {
      img = '/images/icons/schedule_scroll_down_fade.gif';
    }
    button.setStyle( {'backgroundImage':'url(' + img + ')' } );
  },
  // -------------------------------------------------------- enableScrollButton
  enableScrollButton: function( button )
  {
    var isUpButton = button.hasClassName("up");
    var img;
    if( isUpButton )
    {
      img = '/images/icons/schedule_scroll_up.gif';
    }
    else
    {
      img = '/images/icons/schedule_scroll_down.gif';
    }
    button.setStyle( {'backgroundImage':'url(' + img + ')' } );
  }
};


// -----------------------------------------------------------------------------
// Class: EventBlock
// Description: Handles event details, expanding/contracting, permissions, etc.
// -----------------------------------------------------------------------------
var EventBlock = Class.create();
EventBlock.prototype = {
  
  EVENT_TYPE_COLOURS: {
      'League Game': 'green',
      'Exhibition Game':        'green',
      'Tournament Game':        'green',
      'Pickup Game': 'red',
      'Social':      'blue',
      'Recurring':   'orange'
    },
  
  // -------------------------------------------------------------- initialize
  initialize: function(date_block_object, eventHash)
  {
    this.date_block = date_block_object;
    if( typeof( eventHash['start_datestamp'] ) == 'string' )
    {
      this.startTime = Date.parse8601( eventHash['start_datestamp'] );
    }
    else
    {
      this.startTime = eventHash['start_datestamp'];
    }

    if( eventHash['event_id'] )
    { //preferred
      this.eventId = eventHash['event_id'];
    }
    else
    {
      this.eventId = eventHash['id'];
    }
    this.eventGameId  = eventHash['game_id'];
    this.eventType     = eventHash['type'];
    this.eventTitle    = eventHash['title'];
    this.eventLocation = eventHash['location'];
    this.allDayEvent = eventHash['allDayEvent'];
    this.isAdmin       = eventHash['admin'];
    this.score          = eventHash['score'];
    this.best_time      = eventHash['best_time'];
    this.stats          = eventHash['stats'];
    this.scoreLink     = eventHash['score_link'];
    this.editLink      = eventHash['edit_link'];
    this.block          = this.getBlock();
    this.open           = false;
  },
  
  // -------------------------------------------------------------------------
  // CONSTRUCTOR METHODS -----------------------------------------------------
  // ---------------------------------------------------------------- getBlock
  getBlock: function()
  {
    var div = $C('div');
    div.addClassName('event_block');
    div.addClassName('clearfix');
    div.addClassName(this.date_block.getRowClass());
    
    this.titleBar = this.getTitleBar();
    this.infoBlock = this.getInfoBlock();
    
    div.appendChild( this.titleBar );
    div.appendChild( this.infoBlock );
    return div;
  },
  
  // ------------------------------------------------------------- getTitleBar
  getTitleBar: function()
  {
    var div = $C('div'); 
    div.addClassName('title_bar');
    div.addClassName('clearfix');
    
    // Event type icon
    div.appendChild( this.getEventIconByType() );
    
    // Event time
    div.appendChild( this.getTimeSpan() );
    
    // Event title
    var t = $C('div').addClassName('title').addClassName('blue');
    t.appendChild( $T(this.eventTitle) );
    t.setStyle( { 'whiteSpace': 'normal' } );
    div.appendChild( t);
    
    // Watch for clicking the title
    div.observe( 'click', this.titleClickEvent.bindAsEventListener(this) );
    
    return div;
  },
  
  // ------------------------------------------------------ getEventIconByType
  getEventIconByType: function()
  {
    var div = $C('div');
    div.addClassName('icon');
    div.writeAttribute( {'title': this.eventType } );
    switch (this.eventType)
    {
      case 'League Game':
      case 'Exhibition Game':
      case 'Tournament Game':
      case 'Pickup Game':
        div.addClassName('game');
        div.appendChild( $T('G') );
        break;
      case 'Race':
        div.addClassName('race');
        div.appendChild( $T('R') );
        break;
      case 'Tournament':
        div.addClassName('tournament');
        div.appendChild( $T('T') );
        break;
      case 'Practice':
        div.addClassName('practice');
        div.appendChild( $T('P') );
        break;
      case 'Meeting':
        div.addClassName('social');
        div.appendChild( $T('M') );
        break;
      case 'Social':
        div.addClassName('social');
        div.appendChild( $T('S') );
        break;
      case 'Other':
      default:
        div.addClassName('other');
        div.appendChild( $T('O') );
        break;
    }
    return div;
  },
  // ------------------------------------------------------------- getTimeSpan
  getTimeSpan: function()
  {
    var data; 
    if( this.allDayEvent )
    {
      data = 'All Day';
    }
    else
    {
      var hour     = this.startTime.getHours()
      var minute   = this.startTime.getMinutes();
      var cardinal = ( hour > 11 && hour < 24 ) ? 'pm' : 'am';
      
      if (hour > 12)
      {
        hour = this.startTime.getHours() - 12;
      }
      else if ( hour == 0 )
      {
        hour = '12';
      }
      
      if (minute < 10)
      {
        minute = '0' + minute;
      }
      data = hour + ':' + minute + cardinal;
    }
    
    var div = $C('div');
    div.addClassName('time');
    div.appendChild( $T( data ) );
    return div;
  },
  
  // ------------------------------------------------------------ getInfoBlock
  getInfoBlock: function()
  {
    var div = $C('div');
    div.addClassName('info');
    div.addClassName('clearfix');
    div.hide();
    
    // Location name
    if (this.eventLocation)
    {
      div.appendChild( $C('div') ).appendChild( $T('Location: ' + this.eventLocation.name) );
    }
    var scoreDiv = this.buildScoreSection();
    if( scoreDiv )
    {
      div.appendChild( scoreDiv );
    }
    var statsDiv = this.buildStatsSection();
    if( statsDiv )
    {
      div.appendChild( statsDiv );
    }
    
    // Link to the event page
    div.appendChild( $C('div') ).appendChild( $C('a', { href: '/events/' + this.eventId }) ).appendChild( $T('View Event Info') );
    
    // Link to edit event (if available)
    if (this.editLink)
    {
      div.appendChild( $C('div', {style: 'float: right;'}) ).appendChild( $C('a', {href: this.editLink.href}) ).appendChild( $T(this.editLink.title) );
    }
    
    return div;
  },
  // --------------------------------------------------------- buildStatsSection
  buildStatsSection: function()
  {
    var statsDiv = null;
    // Link to view/edit/print stats
    if (this.stats)
    {
      statsDiv = $C('div');
      if (this.stats_link)
      {
        statsDiv.appendChild( $T(this.stats_link.label+': ') );
        statsDiv.appendChild( $C('a', {href: this.stats_link.href}) ).appendChild( $T(this.stats_link.title) );
      } 
      else if( this.eventGameId )
      {
        statsDiv.appendChild( $T('Statistics: ') );
        statsDiv.appendChild( $C('a', {href: '/statistics/show/?game_id='+this.eventGameId}) ).appendChild( $T('View') );
      }
    }
    return statsDiv;
  },
  // ----------------------------------------------------------- addScoreSection
  buildScoreSection: function()
  {
    var scoreDiv = null;
    // The score or best time, as well as an edit link (if available)
    if (this.scoreLink)
    {
      scoreDiv = $C( 'div' );

      if (this.best_time)
      {
        scoreDiv.appendChild( $T(this.scoreLink.label+': '+this.best_time+'  (') );
        scoreDiv.appendChild( $C('a', { 'href': this.scoreLink.href}) ).appendChild( $T(this.scoreLink.title) );
        scoreDiv.appendChild( $T(')') );
      }
      else if (this.score)
      {
        scoreDiv.appendChild( $T( this.scoreLink.label + ': ' + this.score + '  (') );
        scoreDiv.appendChild( $C('a', { 'href': this.scoreLink.href}) ).appendChild( $T(this.scoreLink.title) );
        scoreDiv.appendChild( $T(')') );
      }
      else
      {
        scoreDiv.appendChild( $T( this.scoreLink.label + ': ' ) );
        scoreDiv.appendChild( $C('a', { 'href': this.scoreLink.href }) ).appendChild( $T(this.scoreLink.title) );
      }
    }
    else if (this.best_time)
    {
      scoreDiv = $C('div');
      scoreDiv.appendChild( $T('Best Time: ' + this.best_time) );
    }
    else if ( this.score )
    {
      scoreDiv = $C('div');
      scoreDiv.appendChild( $T('Score: ' + this.score) );
    }
    return scoreDiv;
  },
  
  // -------------------------------------------------------------------------
  // UTILITY METHODS ---------------------------------------------------------
  // ---------------------------------------------------------------- showInfo
  showInfo: function()
  {
    var duration   = 0.3;
    var transition = Effect.Transitions.sinoidal;
    Effect.BlindDown(this.infoBlock, {duration: duration, transition: transition});
    this.open = true;
  },
  
  // ---------------------------------------------------------------- hideInfo
  hideInfo: function()
  {
    var duration   = 0.2;
    var transition = Effect.Transitions.sinoidal;
    Effect.BlindUp( this.infoBlock, {duration: duration, transition: transition} );
    this.open = false;
  },
  
  // -------------------------------------------------------------------------
  // EVENT HANDLER METHODS ---------------------------------------------------
  // --------------------------------------------------------- titleClickEvent
  titleClickEvent: function(event)
  {
    if (this.open)
    {
      this.hideInfo();
    }
    else
    {
      this.showInfo();
    }
  }
};


// -----------------------------------------------------------------------------
// Class: DateBlock
// Description: Essentially, a group of EventBlocks underneith a date banner
// -----------------------------------------------------------------------------
var DateBlock = Class.create();
DateBlock.prototype = {
  
  EVEN_ODD_CLASSES: ['even', 'odd'],
  
  // -------------------------------------------------------------- initialize
  initialize: function(datestamp, events)
  {
    
    this.even_odd_index = 1;
    
    this.events = [];
    this.event_objects = events;
    this.count  = events.length;
    this.setDateStamp( datestamp ); 
    datestamp = new String( datestamp ); 
    var year = datestamp.substring(0,4);
    var month = datestamp.substring(4,6);
    var day = datestamp.substring(6);
    var date       = new Date( year, month - 1, day );
    this.title     = WEEKDAYS[date.getDay()]+', '+MONTHS[date.getMonth()]+' '+ day +', ' + year;
    
    this.block  = this.getBlock();
  },
  // -------------------------------------------------------------- setDateStamp
  setDateStamp: function(d)
  {
    this.datestamp = d;
  },
  // -------------------------------------------------------------- getDateStamp
  getDateStamp: function( )
  {
    return this.datestamp;
  },
  // -------------------------------------------------------------------------
  // CONSTRUCTOR METHODS -----------------------------------------------------
  // ---------------------------------------------------------------- getBlock
  getBlock: function()
  {
    var div = $C('div');
    div.addClassName('date_block');
    div.addClassName('clearfix');
    div.appendChild( this.getHeader() );
    
    for (var i = 0; i < this.count; i++)
    {
      this.events[i] = new EventBlock(this, this.event_objects[i]);
      div.appendChild( this.events[i].block );
    }
    
    return div;
  },
  
  // --------------------------------------------------------------- getHeader
  getHeader: function()
  {
    var div = $C('div');
    div.addClassName('date_header');
    
    div.appendChild( $T(this.title) );
    return div;
  },
  // --------------------------------------------------------------- getRowClass
  getRowClass: function()
  {
    this.even_odd_index = (this.even_odd_index+1)%this.EVEN_ODD_CLASSES.length
    return this.EVEN_ODD_CLASSES[this.even_odd_index];
  }
  
};
