var TimelineView = Class.extend({
    
  // {{{ init
  init: function ( animation, canvas, width, height ) {
    
    this.log.debug( "init", canvas, width, height );

    this.animation = animation;
    this.canvas = canvas;
        
    this.width = width; // The width of the canvas in pixels
    this.height = height;
    this.offsetX = 0; // The position of the origin within the canvas (in pixels)

    this.margin = 3; // The size of the border around each frame. Note that this is NOT doubled between frames.
    
    this.calcSizes();
    this.context = this.canvas.getContext('2d');
  },
  // }}}
  
  // {{{ calcSizes
  calcSizes: function() {    
    this.thumbWidth = Math.ceil( animation.width * (this.height - 2 * this.margin) / animation.height ); // The width of each frame's thumbnail image
    this.scale = (this.height - 2 * this.margin) / animation.height; // The scale factor for the thumbnails.
  },
  // }}}
  
  // {{{ getCurrentFrame
  getCurrentFrame: function() {
    return this.animation.getCurrentFrame();
  },
  // }}}
    
  // {{{ getCurrentFrameNumber
  getCurrentFrameNumber: function() {
    return this.animation.getCurrentFrameNumber();
  },
  // }}}
  
  // {{{ getAnimation
  getAnimation: function() {
    return this.animation;
  },
  // }}}
  
  // {{{ getHistory
  getHistory: function() {
    return this.animation.history;
  },
  // }}}

  // {{{ draw
  draw: function() {
    this.log.debug( "Draw", this.width, this.height );
    
    // Clear
    this.context.fillStyle = "#ccc";
    this.context.fillRect( 0,0, this.width, this.height );

    var animation = this.getAnimation();

    // Hightlight current frame
    this.context.fillStyle = "#66e";
    this.context.fillRect( this.offsetX + this.getCurrentFrameNumber() * (this.thumbWidth + this.margin), 0, this.thumbWidth + 2 * this.margin, this.height );
    
    var min = this.getFrameNumber( 0 );
    if ( min < 0 ) {
      min = 0;
    }
    var max = this.getFrameNumber( this.width );
    if ( max > this.animation.frames.length - 1 ) {
      max = this.animation.frames.length - 1;
    }
    
    for ( var i = min; i <= max; i ++ ) {
      this.drawFrame( i );
    }
  },
  // }}}
  
  // {{{ drawFrame
  drawFrame: function( frameNumber ) {
    
    var animation = this.getAnimation();
    var frame = animation.frames[ frameNumber ];
    
    this.context.save();
    this.context.translate( this.offsetX + this.margin + ( this.margin + this.thumbWidth ) * frameNumber, this.margin );
    this.context.scale( this.scale, this.scale );
    frame.draw( this.context );
    this.context.restore();
    
  },
  // }}}
  
  // {{{ onMouseDown
  onMouseDown: function( e ) {
    
    e = Helpers.resolveMouseEvent( e, this.canvas );
    this.log.trace( "onMouseDown" , e.relX, e.relY, (e.relX - this.offsetX) / (this.thumbWidth + this.margin) );

    this.dragStartX = e.relX;
    var frameNumber = this.getFrameNumber( e.relX );
    
    var me = this;
    document.onmouseup = function(e2) { me.onMouseUp( e2 ); };
    document.onmousemove = function(e3) { me.onMouseMove( e3 ); };
    
    if ( (frameNumber >= 0) && (frameNumber <= this.animation.frames.length) ) {
      this.getAnimation().setCurrentFrameNumber( frameNumber );
    }
    
  },
  // }}}

  // {{{ getFrameNumber
  getFrameNumber: function( x ) {
    return Math.floor( (x - this.offsetX - this.margin) / (this.thumbWidth + this.margin) );
  },
  // }}}

  // {{{ onMouseMove
  onMouseMove: function( e ) {
    
    e = Helpers.resolveMouseEvent( e, this.canvas );
    // this.log.trace( "onMouseMove" , e.relX, e.relY );

    this.offsetX += (e.relX - this.dragStartX);
    if ( this.offsetX > 0 ) {
      this.offsetX = 0;
    }
    this.dragStartX = e.relX;

    this.draw();    
  },
  // }}}
  
  // {{{ onMouseUp 
  onMouseUp: function( e ) {
    e = Helpers.resolveMouseEvent( e, this.canvas );
    this.log.trace( "onMouseUp" , e.relX, e.relY );
    
    document.onmouseup = null;
    document.onmousemove = null;

  },
  // }}}

  // {{{ checkOffset
  checkOffset: function() {
    // Ensure that the current frame is wholy visible.
    var frameNumber = this.getCurrentFrameNumber();

    var left = this.offsetX + this.margin + ( this.margin + this.thumbWidth ) * frameNumber;
    if ( left < 0 ) {
      this.offsetX -= left;
      this.draw();
    } else {
      var right = this.width - ( left + this.thumbWidth + this.margin );
      if ( right < 0 ) {
        this.offsetX += right;
        this.draw();
      }
    }
  },
  // }}}
  
  // {{{ onAddFrame
  onAddFrame: function() {

    var frame = this.getCurrentFrame().copy();
    var frameNumber = this.getCurrentFrameNumber();
    var animation = this.getAnimation();
    
    animation.addFrame( frame, frameNumber );
    animation.setCurrentFrameNumber( animation.getCurrentFrameNumber() + 1 );

    // Undo/Redo    
    this.getHistory().add( new UndoInsertFrame( animation.getCurrentFrameNumber() ) );    
    
    this.checkOffset();
    return false;
  },
  // }}}  

  // {{{ onDeleteFrame
  onDeleteFrame: function() {
    
    this.getHistory().add( new UndoDeleteFrame( this.getCurrentFrame(), this.getCurrentFrameNumber() ) );
    this.getAnimation().removeFrame( this.getCurrentFrameNumber() );
    
    this.checkOffset();
    return false;
  },
  // }}}
  
  // {{{ onNextFrame
  onNextFrame: function() {
    animation.setCurrentFrameNumber( animation.getCurrentFrameNumber() + 1 );
    this.checkOffset();
  },
  // }}}
  
  // {{{ onPreviousFrame
  onPreviousFrame: function() {
    animation.setCurrentFrameNumber( animation.getCurrentFrameNumber() - 1 );
    this.checkOffset();
  },
  // }}}
  
  // {{{ onKeyPressed
  onKeyPressed: function( event ) {
    if ( (event.key == '.') || (event.key == '>') ) {
      this.onNextFrame();
      return false;
    } else if ( (event.key == ',') || (event.key == '<') ) {
      this.onPreviousFrame();
      return false;
    } else if ( (event.key == 'i') || (event.key == 'I') ) {
      this.onAddFrame();
      return false;
    }
    
    return true;
  },
  // }}}
    
  // {{{ onFrameChanged
  onFrameChanged: function( frameNumber ) {
    this.drawFrame( this.getCurrentFrameNumber() );
  },
  // }}}
    
  // {{{ onFrameChange
  onFrameChange: function( oldFrameNumber, newFrameNumber ) {
    this.draw();
  },
  // }}}
  
  // {{{ onFrameRemoved
  onFrameRemoved: function( frameNumber ) {
    this.draw();
  },
  // }}}
  
  // {{{ onFrameAdded
  onFrameAdded: function( frameNumber ) {
    this.draw();
  },
  // }}}
  
  // {{{ onMajorChange
  onMajorChange: function() {
    this.log.trace( "onMajorChange" );
    this.calcSizes();
    this.offsetX = 0;
    this.draw();
  },
  // }}}

  // {{{ toString
  toString: function() {
    return "TimelineView";
  }
  // }}}

});

TimelineView.prototype.log = new Log().setPrefix( "TimelineView " );

