// --------------------------------------------------------------------
// wm3d - A Flash Molecular Viewer
//
// Copyright (c) 2011-2013, tamanegi (tamanegi@users.sourceforge.jp)
// All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// --------------------------------------------------------------------

import flash.display.Stage;

import flash.events.Event;
import flash.events.MouseEvent;

/**
  Mouse Events handler of wm3d
**/

class WMMouseEvents {
  private var wm( null, null ):Watermelon;
  private var stage( null, null ):Stage;

  private var __mousex( null, null ):Float;
  private var __mousey( null, null ):Float;
  private var __mousepx( null, null ):Float;
  private var __mousepy( null, null ):Float;

  /**
    Constructor. Parent Watermelon must be given by `w`.
  **/
  public function new( w:Watermelon ) {
    wm = w;
    stage = wm.stage;
  }

  /**
    launch standart event handler

    - (left button) mouse up, mouse down
    - (right button) mouse up, mouse down (if available)
    - wheeling
  **/
  public function beginStandardEvents():Void {
    stage.addEventListener( MouseEvent.MOUSE_DOWN, __mouseDown );
    stage.addEventListener( MouseEvent.MOUSE_UP, __mouseUp );
    // right click is usable only for 11.2 or later
    if ( Main.checkVersionRightClick() ) {
      stage.addEventListener( MouseEvent.RIGHT_MOUSE_DOWN, __mouseRightDown );
      stage.addEventListener( MouseEvent.RIGHT_MOUSE_UP, __mouseRightUp );
    }
    stage.addEventListener( MouseEvent.MOUSE_WHEEL, __mouseWheel );
  }

  /**
    remove standard mouse events.
    Mouse events launched at `beginStandardEvents` and derivatives of them
    are forced to be removed.
  **/
  public function removeStandardEvents():Void {
    if ( stage.hasEventListener( MouseEvent.MOUSE_DOWN ) ) {
      stage.removeEventListener( MouseEvent.MOUSE_DOWN, __mouseDown );
    }
    if ( stage.hasEventListener( MouseEvent.MOUSE_UP ) ) {
      stage.removeEventListener( MouseEvent.MOUSE_UP, __mouseUp );
    }
    if ( stage.hasEventListener( MouseEvent.MOUSE_WHEEL ) ) {
      stage.removeEventListener( MouseEvent.MOUSE_WHEEL, __mouseWheel );
    }
    // new in flash 11.2
    if ( stage.hasEventListener( MouseEvent.RIGHT_MOUSE_DOWN ) ) {
      stage.removeEventListener( MouseEvent.RIGHT_MOUSE_DOWN, __mouseRightDown );
    }
    if ( stage.hasEventListener( MouseEvent.RIGHT_MOUSE_UP ) ) {
      stage.removeEventListener( MouseEvent.RIGHT_MOUSE_UP, __mouseRightUp );
    }
    if ( stage.hasEventListener( MouseEvent.MOUSE_MOVE ) ) {
      try {
        stage.removeEventListener( MouseEvent.MOUSE_MOVE, __mouseMove );
      }
      try {
        stage.removeEventListener( MouseEvent.MOUSE_MOVE, __mouseMoveScale );
      }
    }
  }

  // event handlers
  //// left button down
  private function __mouseDown( event:MouseEvent ):Void {
    // do nothing when editing or doing something
    if ( wm.states.busyNow || wm.states.actionNow ) return;
    // do nothing when button, such as forward or back, is clicked
    if ( wm.onController( event.stageX, event.stageY ) ) return;
    wm.states.stopAutoRotation();
    __mousex = __mousepx = event.stageX;
    __mousey = __mousepy = event.stageY;
    __beginInteractiveTransform();
  }

  //// right button down
  private function __mouseRightDown( event:MouseEvent ):Void {
    // do nothing when editing or doing something
    if ( wm.states.busyNow || wm.states.actionNow ) return;
    // do nothing when button, such as forward or back, is clicked
    if ( wm.onController( event.stageX, event.stageY ) ) return;
    __mousex = __mousepx = event.stageX;
    __mousey = __mousepy = event.stageY;
    __beginInteractiveScaling();
  }

  //// left button up
  private function __mouseUp( event:MouseEvent ):Void {
    if ( wm.states.busyNow || wm.states.actionNow ) return;
    // this may not happen, but is checked for safety
    if ( !stage.hasEventListener( MouseEvent.MOUSE_MOVE ) ) return;
    __stopInteractiveTransform();
    var movex:Float = event.stageX - __mousepx;
    var movey:Float = event.stageY - __mousepy;
    if ( wm.states.mouseModeL == WMMouseModeL.MOUSE_L_ROTAT_MODE ) {
      var threshold = Math.min( stage.stageWidth, stage.stageHeight ) / 2;
      if ( Math.max( Math.abs( movex ), Math.abs( movey ) ) > threshold ) {
        var dist:Float = Math.sqrt( movex * movex + movey * movey );
        wm.states.arDegreeX = wm.params.arDegree * movey / dist;
        wm.states.arDegreeY = wm.params.arDegree * movex / dist;
        wm.states.arDegreeZ = 0.0;
        wm.states.beginAutoRotation();
      }
    }
  }

  //// right button up
  private function __mouseRightUp( event:MouseEvent ):Void {
    if ( wm.states.busyNow || wm.states.actionNow ) return;
    // this may not happen, but is checked for safety
    if ( !stage.hasEventListener( MouseEvent.MOUSE_MOVE ) ) return;
    __stopInteractiveScaling();
  }

  //// mouse wheel
  private function __mouseWheel( event:MouseEvent ):Void {
    if ( wm.states.busyNow || wm.states.actionNow ) return;
    wm.handleZoom( event.delta );
  }

  // temporary events
  //// mouse move, initiated when left/right button is kept down
  ////// check both of left/right buttons are clicked
  private function __bothOfButtonsClicked():Bool {
    return( stage.hasEventListener( MouseEvent.MOUSE_MOVE ) );
  }

  ////// left button is down
  private function __beginInteractiveTransform():Void {
    if ( !__bothOfButtonsClicked() ) {
      stage.addEventListener( MouseEvent.MOUSE_MOVE, __mouseMove );
    } else {
      // may be both left and right button is clicked, initialize all
      removeStandardEvents();
      wm.visualize();
    }
  }

  private function __stopInteractiveTransform():Void {
    if ( stage.hasEventListener( MouseEvent.MOUSE_MOVE ) ) {
      stage.removeEventListener( MouseEvent.MOUSE_MOVE, __mouseMove );
    }
  }

  ////// right button is down
  private function __beginInteractiveScaling():Void {
    if ( !__bothOfButtonsClicked() ) {
      stage.addEventListener( MouseEvent.MOUSE_MOVE, __mouseMoveScale );
    } else {
      removeStandardEvents();
      wm.visualize();
    }
  }

  private function __stopInteractiveScaling():Void {
    if ( stage.hasEventListener( MouseEvent.MOUSE_MOVE ) ) {
      stage.removeEventListener( MouseEvent.MOUSE_MOVE, __mouseMoveScale );
    }
  }

  //// mouse move (drag) event; left
  private function __mouseMove( event:MouseEvent ):Void {
    if ( wm.states.busyNow ) return;
    var movex:Float = event.stageX - __mousex;
    var movey:Float = event.stageY - __mousey;
    if ( wm.states.mouseModeL == WMMouseModeL.MOUSE_L_ROTAT_MODE ) {
      movex *= wm.params.mouseMoveRotatScale * Math.PI / stage.stageWidth;
      movey *= wm.params.mouseMoveRotatScale * Math.PI / stage.stageHeight;
      wm.states.mpos.appendRotation( movey, flash.geom.Vector3D.X_AXIS );
      wm.states.mpos.appendRotation( movex, flash.geom.Vector3D.Y_AXIS );
    } else if ( wm.states.mouseModeL == WMMouseModeL.MOUSE_L_TRANS_MODE ) {
      wm.states.mpos.appendTranslation( -movex, movey, 0.0 );
    }
    __mousex = event.stageX;
    __mousey = event.stageY;
    wm.states.updateMPos = true;
  }

  //// mouse move (drag) event; right
  private function __mouseMoveScale( event:MouseEvent ):Void {
    if ( wm.states.busyNow ) return;
    var movex:Float = event.stageX - __mousex;
    var movey:Float = event.stageY - __mousey;
    var scale:Int = Std.int( -wm.params.mouseMoveScaleScale * movey / stage.stageHeight );
    if ( scale != 0 ) {
      wm.handleZoom( scale );
      __mousex = event.stageX;
      __mousey = event.stageY;
    }
  }
}
