import flash.text.TextField;
import flash.text.TextFormat;

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

import flash.display.Sprite;

class TextFieldWithScrollBar extends Sprite {
  private var _parent( null, null ):Sprite;
  private var _inputfield( null, null ):TextField;

  private var _scrollfield( null, null ):Sprite;
  private var _scrollbutton( null, null ):Sprite;
  private var _upbutton( null, null ):Sprite;
  private var _downbutton( null, null ):Sprite;

  public function new( ?p:Sprite = null,
                       ?tf:TextFormat = null,
                       ?sizex:Float = 30,
                       ?sizey:Float = 30,
                       ?bs:Float = 6,
                       ?tx:String = "" ) {
    super();
    if ( p != null && tf != null ) initFields( p, tf, sizex, sizey, bs, tx );
  }

  public function getText():String { return( _inputfield.text ); }
  public function setText( s:String ):Void { _inputfield.text = s; }

  public function initFields( p:Sprite,
                              tf:TextFormat,
                              sizex:Float,
                              sizey:Float,
                              bs:Float,
                              tx:String ):Void {
    _parent = p;
    // setup inputfield
    _inputfield = new TextField();
    _inputfield.width = sizex;
    _inputfield.height = sizey;
    _inputfield.setTextFormat( tf );
    _inputfield.defaultTextFormat =  tf;
    _inputfield.background = true;
    _inputfield.border = true;
    _inputfield.wordWrap = true;
    _inputfield.multiline = true;
    // even if i set false for this property, mousewheel still works ...
    // why?
    _inputfield.mouseWheelEnabled = true;
    _inputfield.type = flash.text.TextFieldType.INPUT;
    _inputfield.text = tx;
    this.addChild( _inputfield );
    // setup scroll field, where the scroll-bar moves
    _scrollfield = new Sprite();
    _initializeScrollField( sizex, sizey, bs );
    // up button
    _upbutton = new Sprite();
    _initializeUpButton( sizex, sizey, bs );
    // down button
    _downbutton = new Sprite();
    _initializeDownButton( sizex, sizey, bs );
    // scroll button
    _scrollbutton = new Sprite();
    _initializeScrollButton( sizex, sizey, bs );
  }

  private function _initializeScrollField( sizex:Float,
                                           sizey:Float,
                                           bs:Float ):Void {
    _scrollfield.graphics.beginFill( 0x888888, 1.0 );
    _scrollfield.graphics.lineStyle( 0, 0x000000, 1.0 );
    _scrollfield.graphics.drawRect( 0, 0, bs, sizey - 2 *bs );
    _scrollfield.graphics.endFill();
    this.addChild( _scrollfield );
    _scrollfield.x = sizex;
    _scrollfield.y = bs;
    _scrollfield.addEventListener( MouseEvent.MOUSE_WHEEL, _wheelHandler );
    _scrollfield.addEventListener( MouseEvent.MOUSE_DOWN, _scrollFieldHandler );
  }

  private function _initializeUpButton( sizex:Float,
                                        sizey:Float,
                                        bs:Float ):Void {
    _drawButtonBackground( _upbutton, bs );
    // draw arrow mark
    _upbutton.graphics.lineStyle( 0, 0x333333, 1.0 );
    _upbutton.graphics.beginFill( 0x333333, 1.0 );
    _upbutton.graphics.moveTo( 1, bs - 2 );
    _upbutton.graphics.lineTo( bs / 2, 2 );
    _upbutton.graphics.lineTo( bs - 1, bs - 2 );
    _upbutton.graphics.lineTo( 1, bs - 2 );
    _upbutton.graphics.endFill();
    this.addChild( _upbutton );
    _upbutton.x = sizex;
    _upbutton.y = 0;
    _upbutton.addEventListener( MouseEvent.CLICK, _upHandler );
  }

  private function _initializeDownButton( sizex:Float,
                                          sizey:Float,
                                          bs:Float ):Void {
    _drawButtonBackground( _downbutton, bs );
    _downbutton.graphics.lineStyle( 0, 0x333333, 1.0 );
    _downbutton.graphics.beginFill( 0x333333, 1.0 );
    _downbutton.graphics.moveTo( 1, 3 );
    _downbutton.graphics.lineTo( bs / 2, bs - 1 );
    _downbutton.graphics.lineTo( bs - 1, 2 );
    _downbutton.graphics.lineTo( 1, 3 );
    _downbutton.graphics.endFill();
    this.addChild( _downbutton );
    _downbutton.x = sizex;
    _downbutton.y = sizey - bs;
    _downbutton.addEventListener( MouseEvent.CLICK, _downHandler );
  }

  private function _drawButtonBackground( sp:Sprite,
                                          bs:Float ):Void {
    sp.graphics.beginFill( 0xFFFFFF, 1.0 );
    sp.graphics.lineStyle( 0, 0x000000, 1.0 );
    sp.graphics.drawRect( 0, 0, bs, bs );
    sp.graphics.endFill();
  }

  private function _initializeScrollButton( sizex:Float,
                                            sizey:Float,
                                            bs:Float ):Void {
    var tline:Int = _inputfield.maxScrollV;
    var lines:Int = _inputfield.bottomScrollV - _inputfield.scrollV + 1;
    var sz:Float = _inputfield.height - 2 * bs;
    if ( tline > lines ) sz *= lines / tline;
    // draw button
    _scrollbutton.graphics.beginFill( 0xFFFFFF, 1.0 );
    _scrollbutton.graphics.lineStyle( 0, 0x000000, 1.0 );
    _scrollbutton.graphics.drawRect( 0, 0, bs, sz );
    _scrollbutton.graphics.endFill();
    this.addChild( _scrollbutton );
    _scrollbutton.x = sizex;
    _scrollbutton.y = bs;
    _inputfield.addEventListener( Event.SCROLL, _scrollHandler );
    _scrollbutton.addEventListener( MouseEvent.MOUSE_DOWN, _scrollButtonHandler );
  }

  // event handlers
  private function _scrollHandler( event:Event ) {
    var cl:Int = _inputfield.scrollV;
    var ml:Int = _inputfield.maxScrollV;
    var l:Int = _inputfield.bottomScrollV - _inputfield.scrollV + 1;
    var s:Float = _inputfield.height - 2 * _scrollbutton.width;
    if ( ml > l ) s *= l / ml;
    var ns:Float = s / _scrollbutton.height;
    _scrollbutton.scaleY *= ns;
    _scrollbutton.y = ( _scrollfield.height - _scrollbutton.height )
                    * ( cl - 1 ) / ( ml  - 1 ) + _scrollfield.y;
  }

  private function _wheelHandler( event:MouseEvent ) {
    _inputfield.scrollV -= event.delta;
  }

  private function _upHandler( event:MouseEvent ) {
    _inputfield.scrollV--;
  }

  private function _downHandler( event:MouseEvent ) {
    _inputfield.scrollV++;
  }

  private function _removeMouseMoveHandler( event:MouseEvent ) {
    flash.Lib.current.stage.removeEventListener( MouseEvent.MOUSE_MOVE, _scrollMoveHandler );
    flash.Lib.current.stage.removeEventListener( MouseEvent.MOUSE_UP, _removeMouseMoveHandler );
  }

  private function _scrollButtonHandler( event:MouseEvent ) {
    flash.Lib.current.stage.addEventListener( MouseEvent.MOUSE_MOVE, _scrollMoveHandler );
    flash.Lib.current.stage.addEventListener( MouseEvent.MOUSE_UP, _removeMouseMoveHandler );
  }

  private function _scrollMoveHandler( event:MouseEvent ) {
    var npos:Float = _scrollfield.mouseY;
    var cpos:Int = Std.int( _inputfield.maxScrollV * npos / _scrollfield.height );
    _inputfield.scrollV = cpos;
  }

  private function _scrollFieldHandler( event:MouseEvent ) {
    var my:Float = _scrollfield.mouseY;
    var l:Int = _inputfield.bottomScrollV - _inputfield.scrollV + 1;
    var relpos:Float;
    if ( my > ( _scrollfield.height - _scrollbutton.height ) ) {
      relpos = 1;
    } else {
      relpos = my / ( _scrollfield.height - _scrollbutton.height );
    }
    var relsc:Int = Std.int( relpos * _inputfield.maxScrollV - _inputfield.scrollV );
    if ( relsc < 0 || relsc > l ) {
      _inputfield.scrollV = Std.int( relpos * _inputfield.maxScrollV );
    }
  }
}
