var FrameView = 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
    this.height = height;
    this.offsetX = 0; // The position of the origin within the canvas (in pixels)
    this.offsetY = 0;
    this.zoom = 1;
    this.autoZoom = false;
    this.showStatic = false;

    this.setCurrentFigure( null );
    this.currentNode = null;
    
    if ( ! this.canvas.getContext ) {
      this.log.severe( "Your browser does not support the canvas object.\nYou need to upgrade your web browser to use this page." );
      return;
    }
  
    this.context = this.canvas.getContext('2d');
  },
  // }}}
  
  // {{{ getAnimation
  getAnimation: function() {
    return this.animation;
  },
  // }}}
  
  // {{{ getCurrentFrame
  getCurrentFrame: function() {
    return this.animation.getCurrentFrame();
  },
  // }}}

  // {{{ getHistory
  getHistory: function() {
    return this.animation.history;
  },
  // }}}
  
  // {{{ draw
  draw: function() {
        
    if ( this.autoZoom && ! this.isDragging() ) {
      if ( this.currentFigure != null ) {
        this.zoomToFit( this.currentFigure );
      } else {
        if ( this.getCurrentFrame().figures.length > 0 ) {
          this.zoomToFit( this.getCurrentFrame().figures[0] );
        }
      }
    }
    // Clear
    this.context.fillStyle = "#fff";
    this.context.fillRect( 0,0, this.width, this.height );

    // Draw the figures    
    this.context.save();   
    this.context.translate( this.offsetX, this.offsetY );
    this.context.scale( this.zoom, this.zoom );
    
    this.getCurrentFrame().draw( this.context );
    this.context.restore();

    // Draw a frame around the stickflick world
    var x1 = this.toPixelX( 0 );
    var y1 = this.toPixelY( 0 );
    var x2 = this.toPixelX( this.getAnimation().width );
    var y2 = this.toPixelY( this.getAnimation().height );
    
    this.context.fillStyle = "rgba( 100,100,100,0.95 )";
    this.context.beginPath();
    this.context.moveTo( 0,0 );
    this.context.lineTo( this.width, 0 );
    this.context.lineTo( this.width, this.height );
    this.context.lineTo( 0, this.height );
    this.context.lineTo( 0, 0 );
    this.context.lineTo( x1, y1 );
    this.context.lineTo( x1, y2 );
    this.context.lineTo( x2, y2 );
    this.context.lineTo( x2, y1 );
    this.context.lineTo( x1, y1 );
    this.context.fill();

    
    // Draw grab handles
    this.context.save();    
    this.context.translate( this.offsetX, this.offsetY );
    this.context.scale( this.zoom, this.zoom );

    this.drawHandles();

    this.context.restore();
  },
  // }}}
  
  // {{{ drawHandles
  drawHandles: function() {
    
    if ( this.currentFigure != null ) {
      this.currentFigure.drawBounding( this.context, this.zoom );
      this.currentFigure.drawHandles( this.context, this.zoom, this.showStatic );
    }
    
  },
  // }}}
  
  // {{{ toWorldX
  toWorldX: function( x ) {
    return (x - this.offsetX) / this.zoom;
  },
  // }}} 
  
  // {{{ toWorldY
  toWorldY: function( y ) {
    return (y - this.offsetY) / this.zoom;
  },
  // }}}
  
  // {{{ toPixelX
  toPixelX: function( x ) {
    return x * this.zoom + this.offsetX;
  },
  // }}}
  
  // {{{ toPixelY
  toPixelY: function( y ) {
    return y * this.zoom + this.offsetY;
  },
  // }}}
  
  // {{{ setCurrentFigure
  setCurrentFigure: function( fig ) {
    if ( this.currentFigure != fig ) { 
      this.currentFigure = fig;
      if ( fig == null ) {
        this.figureBeforeChange = null;
      } else {
        this.figureBeforeChange = fig.copy();
      }
    }
  },
  // }}}
  
  // {{{ setCurrentNode
  setCurrentNode: function( node ) {
    this.currentNode = node;
  },
  // }}}
  
  // {{{ checkBeginningDrag
  checkBeginningDrag: function( x, y ) {  
    if ( this.currentFigure ) {
      var node = this.currentFigure.findHandleAt( x, y, this.zoom, this.showStatic );
      if ( node ) {
        this.setCurrentNode( node );
        this.draggingNode = node;
        this.dragOffsetX = this.draggingNode.x - x;
        this.dragOffsetY = this.draggingNode.y - y;
  
        this.animation.notifyFrameChange();
        return true;
      }
    }

    this.currentNode = null;
    return false;
  },
  // }}}
  
  // {{{ isDragging
  isDragging: function() {
    return this.draggingNode != null;
  },
  // }}}
  
  // {{{ changedFigure
  changedFigure: function() {
    this.getHistory().add( new UndoChangeFigure( this.figureBeforeChange, this.currentFigure.copy() ) );
    this.animation.notifyFrameChanged();
    this.figureBeforeChange = this.currentFigure.copy();
  },
  // }}}  
  
  // {{{ onMouseDown
  onMouseDown: function( e ) {
    e = Helpers.resolveMouseEvent( e, this.canvas );

    var x = this.toWorldX( e.relX );
    var y = this.toWorldY( e.relY );
    
    this.dragPixelX = e.relX;
    this.dragPixelY = e.relY;
    
    var me = this;
    document.onmouseup = function(e2) { me.onMouseUp( e2 ); };
    document.onmousemove = function(e3) { me.onMouseMove( e3 ); };

    this.onMouseDownInner( e, x, y );
  },
  // }}}
  
  // {{{ onMouseDownInner
  onMouseDownInner: function( e, x, y ) {
    
    if ( this.checkBeginningDrag( x, y ) ) {
      return;
    }

    this.setCurrentFigure( this.getCurrentFrame().findFigureAt( x, y, this.zoom ) );
    
    if ( this.currentFigure != null ) {
      this.currentFigure.createHandles();
    }
    this.checkBeginningDrag( x, y );

    this.draw();
  },
  // }}}
  
  // {{{ onMouseMove
  onMouseMove: function( e ) {
    
    e = Helpers.resolveMouseEvent( e, this.canvas );
    // this.log.trace( "onMouseMove" , e.relX, e.relY );
    var x = this.toWorldX( e.relX );
    var y = this.toWorldY( e.relY );

    var alternate = e.ctrlKey;
    
    this.onMouseMoveInner( e, x, y, alternate );
  },
  // }}}
  
  // {{{ onMouseMoveInner
  onMouseMoveInner: function( e, x, y, alternate ) {
    
    if ( this.draggingNode ) {
                  
      this.draggingNode.adjustPosition( x + this.dragOffsetX, y + this.dragOffsetY, alternate );
      this.getAnimation().notifyFrameChanged();
      
    } else {
      
      // Must be dragging the world then
      this.offsetX += e.relX - this.dragPixelX;
      this.offsetY += e.relY - this.dragPixelY;
      
      this.dragPixelX = e.relX;
      this.dragPixelY = e.relY;
      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;

    // Undo/Redo    
    if ( this.draggingNode && (this.figureBeforeChange != null) ) {
      this.changedFigure();
    }
    
    this.draggingNode = null;
  },
  // }}}

  // {{{ zoomBy
  zoomBy: function( amount ) {
    // Keep the center of the view at the center, but change the zoom.
    
    var midX = this.toWorldX( this.width / 2 ); 
    var midY = this.toWorldY( this.height / 2 ); 
    
    this.zoom *= amount;
    
    var nowAtX = this.toPixelX( midX );
    var nowAtY = this.toPixelY( midY );
   
    this.offsetX -= ( nowAtX - this.width / 2 );
    this.offsetY -= ( nowAtY - this.height / 2 );
  },
  // }}}

  // {{{ onZoomIn
  onZoomIn: function() {
    this.zoomBy( 1.4 );zoom = 0.33
    this.draw();
    return false;
  },
  // }}}

  // {{{ onZoomOut
  onZoomOut: function() {
    this.zoomBy( 1 / 1.4 );
    this.draw();
    return false;
  },
  // }}}
  
  // {{{ onResetZoom
  onResetZoom: function() {
    this.zoom = 1;
    this.offsetX = ( this.width - this.animation.width ) / 2;
    this.offsetY = ( this.height - this.animation.height ) / 2;
    this.draw();
    return false;
  },
  // }}}
  
  // {{{ zoomToFit
  zoomToFit: function( figure ) {
    var radius = figure.getRadius();
    
    this.zoom = this.width / (radius * 2);
    
    this.offsetX = (radius - figure.root.x) * this.zoom;
    this.offsetY = (radius - figure.root.y) * this.zoom;
    
  },
  // }}}
  
  // {{{ onDeleteFigure
  onDeleteFigure: function( ) {
    
    if ( this.currentFigure == null ) {
      
      alert( "No figure is selected, so no figure has been deleted." );
      
    } else {
      
      var figure = frameView.currentFigure;
      
      var index = this.getCurrentFrame().removeFigure( figure.id );
      if ( index != null ) {
        this.getHistory().add( new UndoDeleteFigure( figure, index ) );
        this.currentFigure = null;
        this.animation.notifyFrameChanged();
      }
    }
    
  },
  // }}}
  
  
  // {{{ onPalette
  onPalette: function( animationController, paletteController ) {
    
    if ( this.currentFigure == null ) {
      
      alert( "No figure is selected." );
      
    } else {
      
      paletteController.show( true );
      paletteController.parent = animationController;
      frameController.show( false );
    }
    
  },
  // }}}

  // {{{ onKeyPressed
  onKeyPressed: function( event ) {
    if ( (event.key == '-') || (event.key == '_') || (event.keyCode == 189) ) {
      this.onZoomOut();
      return true;
    } else if ( (event.key == '+') || (event.key == '=') || (event.keyCode == 187) ) {
      this.onZoomIn();
      return true;
    } else if ( (event.key == '0') ) {
      this.onResetZoom();
      return true;
    }
    
    return false;
  },
  // }}}

  // {{{ onFrameChanged
  onFrameChanged: function() {
    if ( this.currentFigure != null ) {
      this.setCurrentFigure( this.getCurrentFrame().findFigure( this.currentFigure.id ) );
    }
    this.draw();
  },
  // }}}
  
  // {{{ onFrameChange
  onFrameChange: function( oldFrameNumber, newFrameNumber ) {
    if ( this.currentFigure != null ) {
      this.setCurrentFigure( this.getCurrentFrame().findFigure( this.currentFigure.id ) );
    }
    this.draw();
  },
  // }}}

  // {{{ onMajorChange
  onMajorChange: function() {
    this.setCurrentFigure( null );
    this.onResetZoom();
    this.draw();
  },
  // }}}
  
  // {{{ toString
  toString: function() {
    return "FrameView : ";
  }
  // }}}

});

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


