// --------------------------------------------------------------------
// wm3d - A Flash Molecular Viewer
//
// Copyright (c) 2011-2014, 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.display3D.Context3D;

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

/**
  Ribbon or Coil object; ribbon or tube

  This class should belong to a `WMChain`.
  See `WMBase` and `WMObjBase` classes for inheritted parameters.
**/

class WMRibbon extends WMObjBase {
  /**
    is this object a ribbon? default is false
  **/
  @:isVar public var isRibbon( get, set ):Bool;
    /**
      getter of `isRibbon`
    **/
    public function get_isRibbon():Bool { return( isRibbon ); }
    /**
      setter of `isRibbon`
    **/
    public function set_isRibbon( i:Bool ):Bool {
      isRibbon = i;
      return( isRibbon );
    }

  /**
    given initial index in the CHAIN
  **/
  @:isVar public var initXML( get, set ):Int;
    /**
      getter of `initXML`
    **/
    public function get_initXML():Int { return( initXML ); }
    /**
      setter of `initXML`
    **/
    public function set_initXML( n:Int ):Int {
      initXML = n;
      return( initXML );
    }
  /**
    given last index in the CHAIN
  **/
  @:isVar public var lastXML( get, set ):Int;
    /**
      getter of `lastXML`
    **/
    public function get_lastXML():Int { return( lastXML ); }
    /**
      setter of `lastXML`
    **/
    public function set_lastXML( n:Int ):Int {
      lastXML = n;
      return( lastXML );
    }
  /**
    actual first index in CHAIN (calculated from `initXML` in `WMChain` class)
  **/
  @:isVar public var init( get, set ):Int;
    /**
      getter of `init`
    **/
    public function get_init():Int { return( init ); }
    /**
      setter of `init`
    **/
    public function set_init( n:Int ):Int {
      init = n;
      return( init );
    }
  /**
    actual last index in CHAIN (calculated from `initXML` in `WMChain` class)
  **/
  @:isVar public var last( get, set ):Int;
    /**
      getter of `last`
    **/
    public function get_last():Int { return( last ); }
    /**
      setter of `last`
    **/
    public function set_last( n:Int ):Int {
      last = n;
      return( last );
    }
  /**
    thickness of ribbon. coil does not use this param. default is 0
  **/
  @:isVar public var thickness( get, set ):Float;
    /**
      getter of `thickness`
    **/
    public function get_thickness():Float { return( thickness ); }
    /**
      setter of `thickness`
    **/
    public function set_thickness( t:Float ):Float {
      thickness = t;
      return( thickness );
    }
  /**
    whether smoothing is performed. default is false.
    This parameter is instrinsically for beta-strands, do not use for
    others.
  **/
  @:isVar public var smoothing( get, set ):Bool;
    /**
      getter of `smoothing`
    **/
    public function get_smoothing():Bool { return( smoothing ); }
    /**
      setter of `smoothing`
    **/
    public function set_smoothing( b:Bool ):Bool {
      smoothing = b;
      return( smoothing );
    }

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

  /**
    Constructor. 
    Constructor. Parameters are listed below, where values in parenthesis
    are default values.

    - i: is this ribbon? (false)
    - r: radius (3.0)
    - c0: color (0x00FF00; lime)
    - c1: color, currently not used (0x00FF00, lime)
    - a: alpha (1.0, opaque)
    - o: offset, not used (0.0)
    - q: quality, higher is better (0), not used.
    - ag: ambient intensity (0.3)
    - d: diffuse intensity (0.6)
    - sp: specular intensity (0.3)
    - gl: gloss (30.0)
    - sh: shader type ("Phong")
  **/
  public function new( ?i:Bool = false,
                       ?r:Float = 3.0,
                       ?c0:Int = 0x00FF00,
                       ?c1:Int = 0x00FF00,
                       ?a:Float = 1.0,
                       ?o:Float = 0.0,
                       ?q:Int = 0,
                       ?ag:Float = 0.3,
                       ?d:Float = 0.6,
                       ?sp:Float = 0.3,
                       ?gl:Float = 30.0,
                       ?sh:String = "Phong" ) {
    super( r, c0, c1, a, o, q, ag, d, sp, gl, sh );
    isRibbon = i;
    initXML = lastXML = -1;
    init = last = -1;
    thickness = 0.0;
    smoothing = false;
  }

  /**
    returns clone of `this` instance
  **/
  public function clone():WMRibbon {
    var ret:WMRibbon = new WMRibbon( isRibbon, radius, color0, color1, alpha, offset, quality, ambient, diffuse, specular, gloss, shader );
    ret.init = init;
    ret.initXML = initXML;
    ret.last = last;
    ret.lastXML = lastXML;
    ret.thickness = thickness;
    ret.smoothing = smoothing;
    return( ret );
  }

  /**
    initialize values; if `def` is given, that is used as initial value.
  **/
  public override function clear( ?def:WMDefaults = null ):Void {
    if ( def != null ) {
      if ( isRibbon ) {
        copyFrom( def.Ribbon );
        thickness = def.RibbonThickness;
        smoothing = def.RibbonSmoothing;
      } else {
        copyFrom( def.Coil );
      }
    } else {
      isRibbon = false;
      radius = 3.0;
      color0 = 0x00FF00;
      color1 = 0x00FF00;
      alpha = 1.0;
      offset = 0.0;
      quality = 0;
      ambient = 0.3;
      diffuse = 0.6;
      specular = 0.3;
      gloss = 30.0;
      shader = "Gouraud";
      thickness = 0.0;
      smoothing = false;
    }
    _polygon = null;
    initXML = lastXML = -1;
  }

  /**
    load from XML; if `def` is given, that is used as initial value. See also
    `clear` function of this class.
  **/
  public override function loadFromXml( x:Xml,
                                        ?def:WMDefaults = null ):Void {
    clear( def );
    loadFromXmlWOClear( x );
  }

  /**
    read XML while overriding default values; this function does not read
    WMAtom specific fields. Use `loadFromXmlWOClear` when loading WMAtom
    specific fields.
  **/
  public function loadFromXmlOverwrite( x:Xml ):Void {
    super.loadFromXml( x );
    var strs = [ "thick", "thickness" ];
    for ( str in strs ) {
      if ( x.exists( str ) ) {
        thickness = Std.parseFloat( x.get( str ) );
      }
    }
    var strs = [ "smooth", "smoothing" ];
    for ( str in strs ) {
      if ( x.exists( str ) ) {
        smoothing = ( Std.parseInt( x.get( str ) ) > 0 );
      }
    }
  }

  /**
    load XML data; usually this function is called by loadFromXml function
  **/
  public function loadFromXmlWOClear( x:Xml ):Void {
    loadFromXmlOverwrite( x );
    if ( !x.exists( "init" ) || !x.exists( "last" ) ) {
      trace( "init and last attributes are required for a RIBBON or COIL element" );
      return;
    }
    initXML = Std.parseInt( x.get( "init" ) );
    lastXML = Std.parseInt( x.get( "last" ) );
  }

  /**
    generate polygon and set corresponding shaders;
    this function calls `pregen` and `gen2`.
  **/
  public function gen( c:Context3D,
                       chain:WMSmoothChain,
                       ?is_dc_active:Bool = false ):Void {
    pregen( chain );
    gen2( c, is_dc_active );
  }

  /**
    preparation for generating polygon. This function is explicitly used in
    non-primordial Worker, where Context3D of the primordial Worker is not
    available. If Worker is not used, this function should not be called
    explicitly.
  **/
  public function pregen( chain:WMSmoothChain ) {
    if ( color0 != color1 ) {
      if ( shader == "Gouraud" ) shader = "GouraudUV";
      if ( shader == "Phong" ) shader = "PhongUV";
    }
    if ( isRibbon ) {
      _polygon = chain.genRibbon( WMBase.getRelative( radius ),
                                  WMBase.getRelative( thickness ),
                                  init, last + 1, smoothing );
    } else {
      _polygon = chain.genCoil( WMBase.getRelative( radius ), quality,
                                init, last + 1 );
    }
    setShaderParams();
  }

  /**
    generate polygon and shader. Call `pregen` function before.
    This function should not be called explicitly unless you are using
    multiple Workers.
  **/
  public function gen2( c:Context3D,
                        ?is_dc_active:Bool = false ) {
    _polygon.allocate( c, is_dc_active, shader, color0, alpha );
    // replace texture if neccessary
    if ( color0 != color1 ) {
      _polygon.createGradientTexture( c, color0, color1 );
    }
  }
}
