import flash.display3D.Context3D;

import flash.xml.XML;
import flash.xml.XMLList;
import flash.xml.XMLParser;

class WMBond extends WMObjBase {
  public var rounded( __getRounded, __setRounded ):Bool;
  public var exclude( __getExclude, __setExclude ):Bool;

  public var pos0( __getPos0, __setPos0 ):Point3D;
  public var pos1( __getPos1, __setPos1 ):Point3D;
  public var opos0( __getOrgPos0, __setOrgPos0 ):Point3D;
  public var opos1( __getOrgPos1, __setOrgPos1 ):Point3D;
  public var dashed( __getDashed, __setDashed ):Int;

  public function new( ?r:Float = 1.0,
                       ?c0:Int = 0x00FF00,
                       ?c1:Int = 0x00FF00,
                       ?a:Float = 1.0,
                       ?o:Float = 0.0,
                       ?q:Int = 0,
                       ?ag:Float = 0.3,
                       ?d:Float = 1.0,
                       ?sp:Float = 0.6,
                       ?gl:Float = 20.0,
                       ?sh:String = "Gouraud" ) {
    super( r, c0, c1, a, o, q, ag, d, sp, gl, sh );
    rounded = false;
    exclude = false;
    pos0 = null;
    pos1 = null;
    opos0 = null;
    opos1 = null;
    dashed = 0;
  }

  public override function clear( ?def:WMDefaults = null ):Void {
    if ( def != null ) {
      copyFrom( def.Bond );
      rounded = def.BondRound;
      exclude = def.BondExclude;
      dashed = def.BondDashed;
    } else {
      radius = 1.0;
      color0 = 0x00FF00;
      color1 = 0x00FF00;
      alpha = 1.0;
      offset = 0.0;
      quality = 0;
      ambient = 0.3;
      diffuse = 1.0;
      specular = 0.6;
      gloss = 20.0;
      shader = "Gouraud";
      rounded = false;
      exclude = false;
    }
    pos0 = null;
    pos1 = null;
    opos0 = null;
    opos1 = null;
    _polygon = null;
  }

  public override function loadFromXml( x:Xml,
                                        ?def:WMDefaults = null ):Void {
    super.loadFromXml( x, def );
    if ( !x.exists( "pos0" ) || !x.exists( "pos1" ) ) {
      trace( "pos0 and pos1 attributes are required for a BOND element." );
      return;
    }
    pos0 = Point3D.fromString( x.get( "pos0" ) );
    pos1 = Point3D.fromString( x.get( "pos1" ) );
    opos0 = pos0;
    opos1 = pos1;
    var strs = [ "rounded", "round" ];
    for ( str in strs ) {
      if ( x.exists( str ) ) {
        rounded = ( Std.parseInt( x.get( str ) ) > 0 );
      }
    }
    strs = [ "exclude", "exc" ];
    for ( str in strs ) {
      if ( x.exists( str ) ) {
        exclude = ( Std.parseInt( x.get( str ) ) > 0 );
      }
    }
    var strs = [ "dash", "dashed" ];
    for ( str in strs ) {
      if ( x.exists( str ) ) {
        dashed = Std.parseInt( x.get( str ) );
      }
    }
  }

  public function gen( c:Context3D ):Void {
    var vec:Point3D = Point3D.getSub( pos1, pos0 );
    var norm:Float = vec.norm() - WMBase.getRelative( offset );
    if ( rounded && dashed == 0 && !exclude ) {
      _polygon = new RoundedCylinder( WMBase.getRelative( radius ),
                                      norm, quality );
    } else {
      _polygon = new Cylinder( WMBase.getRelative( radius ),
                               norm, quality, exclude, exclude );
    }
    if ( dashed != 0 ) {
      shader = "SimpleUV";
    } else if ( color0 != color1 ) {
      if ( shader == "Gouraud" ) shader = "GouraudUV";
      if ( shader == "Phong" ) shader = "PhongUV";
    }
    _polygon.lookAt( vec );
    _polygon.translateByVec( Point3D.getMiddle( pos0, pos1 ) );
    _polygon.allocate( c, shader, color0, alpha );
    if ( dashed != 0 ) {
      _polygon.createDashedDoubleColoredTexture( c, dashed, color0, color1 );
    } else if ( color0 != color1 ) {
      _polygon.createDoubleColoredTexture( c, color0, color1 );
    }
  }

  public function __getRounded():Bool { return( rounded ); }
  public function __getExclude():Bool { return( exclude ); }
  public function __getPos0():Point3D { return( pos0 ); }
  public function __getPos1():Point3D { return( pos1 ); }
  public function __getOrgPos0():Point3D { return( opos0 ); }
  public function __getOrgPos1():Point3D { return( opos1 ); }
  public function __getDashed():Int { return( dashed ); }
  public function __setRounded( r:Bool ):Bool {
    rounded = r;
    return( rounded );
  }
  public function __setExclude( e:Bool ):Bool {
    exclude = e;
    return( exclude );
  }
  public function __setPos0( p:Point3D ):Point3D {
    if ( p != null ) pos0 = p.clone();
    return( pos0 );
  }
  public function __setPos1( p:Point3D ):Point3D {
    if ( p != null ) pos1 = p.clone();
    return( pos1 );
  }
  public function __setOrgPos0( p:Point3D ):Point3D {
    if ( p != null ) opos0 = p.clone();
    return( opos0 );
  }
  public function __setOrgPos1( p:Point3D ):Point3D {
    if ( p != null ) opos1 = p.clone();
    return( opos1 );
  }
  public function __setDashed( d:Int ):Int {
    dashed = d;
    return( dashed );
  }

  public function num():Int { return( 2 ); }
  public function sumPos():Point3D {
    return( Point3D.getAdd( pos0, pos1 ) );
  }
  public function translate( p:Point3D ):Void {
    pos0.add( p );
    pos1.add( p );
  }
  public function absmax():Point3D {
    var ret:Point3D = new Point3D( 0, 0, 0 );
    ret.x = Math.max( Math.abs( pos0.x ), Math.abs( pos1.x ) );
    ret.y = Math.max( Math.abs( pos0.y ), Math.abs( pos1.y ) );
    ret.z = Math.max( Math.abs( pos0.z ), Math.abs( pos1.z ) );
    return( ret );
  }
  public function scaleCoord( scale:Float ):Void {
    pos0.multiply( scale );
    pos1.multiply( scale );
  }
}
