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 function __getRounded():Bool { return( rounded ); }
    public function __setRounded( r:Bool ):Bool {
      rounded = r;
      return( rounded );
    }
  public var exclude( __getExclude, __setExclude ):Bool;
    public function __getExclude():Bool { return( exclude ); }
    public function __setExclude( e:Bool ):Bool {
      exclude = e;
      return( exclude );
    }

  public var pos0( __getPos0, __setPos0 ):Point3D;
    public function __getPos0():Point3D { return( pos0 ); }
    public function __setPos0( p:Point3D ):Point3D {
      if ( p != null ) pos0 = p.clone();
      return( pos0 );
    }
  public var pos1( __getPos1, __setPos1 ):Point3D;
    public function __getPos1():Point3D { return( pos1 ); }
    public function __setPos1( p:Point3D ):Point3D {
      if ( p != null ) pos1 = p.clone();
      return( pos1 );
    }
  public var opos0( __getOrgPos0, __setOrgPos0 ):Point3D;
    public function __getOrgPos0():Point3D { return( opos0 ); }
    public function __setOrgPos0( p:Point3D ):Point3D {
      if ( p != null ) opos0 = p.clone();
      return( opos0 );
    }
  public var opos1( __getOrgPos1, __setOrgPos1 ):Point3D;
    public function __getOrgPos1():Point3D { return( opos1 ); }
    public function __setOrgPos1( p:Point3D ):Point3D {
      if ( p != null ) opos1 = p.clone();
      return( opos1 );
    }
  public var dashed( __getDashed, __setDashed ):Int;
    public function __getDashed():Int { return( dashed ); }
    public function __setDashed( d:Int ):Int {
      dashed = d;
      return( dashed );
    }

  // ###################################################################

  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 = true;
    exclude = false;
    pos0 = null;
    pos1 = null;
    opos0 = null;
    opos1 = null;
    dashed = 0;
  }

  public function clone():WMBond {
    var ret:WMBond = new WMBond( radius, color0, color1, alpha, offset, quality, ambient, diffuse, specular, gloss, shader );
    ret.rounded = rounded;
    ret.exclude = exclude;
    ret.dashed = dashed;
    return( ret );
  }

  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 = true;
      exclude = false;
    }
    pos0 = null;
    pos1 = null;
    opos0 = null;
    opos1 = null;
    _polygon = null;
  }

  public override function loadFromXml( x:Xml,
                                        ?def:WMDefaults = null ):Void {
    clear( def );
    loadFromXmlWOClear( x );
  }

  public function loadFromXmlWOClear( x:Xml ):Void {
    loadFromXmlOverwrite( x );
    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;
  }

  public function loadFromXmlOverwrite( x:Xml ):Void {
    super.loadFromXml( x );
    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 override function dump():String {
    var d:Dynamic = Type.getClass( this );
    var type:String = d.id;

    var str:String = " c=" + "\"" + color0 + "\"" +
                     " c0=" + "\"" + color0 + "\"" +
                     " c1=" + "\"" + color1 + "\"" +
                     " a=" + "\"" + alpha + "\"" +
                     " o=" + "\"" + offset + "\"" +
                     " am=" + "\"" + ambient + "\"" +
                     " s=" + "\"" + specular + "\"" +
                     " g=" + "\"" + gloss + "\"" +
                     " dashed=" + "\"" + dashed + "\"" +
                     " type=" + "\"" + type + "\"" +
                     " shader="+ "\"" + shader + "\"";
    return( _polygon.dump( str ) );
  }

  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 );
  }
  public override function getDataSize():Int { return( 15 ); }
}
