// data container for pdb file

// data hierarchy
// Pdb - PdbModel - PdbChain(use chain id) - PdbResidue - PdbAtom
//
// PdbModel   : MODELs which are often used for NMR structure
// PdbChain   : A chain
// PdbResidue : A residue
// PdbAtom    : An atom

// keywords to be interpretted are:
// TER, END, ENDMDL, HELIX, SHEET, ATOM, HETATM
import flash.net.URLLoader;

class Pdb {
  // global parameters
  static public var coil_radius:String = "3.5";
  static public var ribbon_radius:String = "10.0";
  // helix params
  static public var helix_color:String = "red";
  static public var helix_thickness:String = "0.2";
  // strand params
  static public var strand_color:String = "blue";
  static public var strand_thickness:String = "0.2";

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

  public var models( __getModels, null ):Array< PdbModel >;
    public function __getModels():Array< PdbModel > { return( models ); }

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

  public function new( ?pdbText:String ) {
    models = new Array< PdbModel >();
    if ( pdbText != null ) read( pdbText );
  }

  public function empty():Bool {
    if ( models.length == 0 ) return( true );
    return( false );
  }

  public function read( ?pdbText:String ):Void {
    var modelnum:Int = 0;
    var secs:Array< PdbSecondary > = new Array< PdbSecondary >();
    var prev:Dynamic = null;
    if ( pdbText == null ) return;
    for ( text in pdbText.split( '\n' ) ) {
      if ( text.length >= 6 ) {
        var fieldname:String = StringTools.trim( text.substr( 0, 6 ) );
        switch ( fieldname ) {
          case "HELIX":
            var sec:PdbSecondary = PdbSecondary.readHelixFromText( text );
            secs.push( sec );
          case "SHEET":
            var sec:PdbSecondary = PdbSecondary.readStrandFromText( text );
            secs.push( sec );
          case "ATOM", "HETATM":
            var an = PdbAtom.readFromText( text );
            addAtom( modelnum, an.ci, an.rn, an.ri, an.het, an.at );
            prev = an;
          case "END", "ENDMDL":
            modelnum++;
          case "TER":
            if ( prev != null ) {
              addTer( modelnum, prev.ci, prev.rn, prev.ri );
            }
        }
      } else if ( text.length >= 3 ) {
        // END statement is assumed to be same as ENDMDL
        if ( text.substr( 0, 3 ) == "END" ) {
          modelnum++;
        }
      }
    }
    // assign secondary structure type
    for ( sec in secs ) {
      // secondary structures are assumed to be common for all the models
      for ( model in models ) {
        model.chains.get( sec.chainid ).assignSec( sec );
      }
    }
  }

  public function addAtom( mn:Int,
                           cid:String,
                           resname:String,
                           resid:Int,
                           ishet:Bool,
                           atom:PdbAtom ) {
    // add models if necessary
    while ( models.length <= mn ) {
      models.push( new PdbModel( models.length + 1 ) );
    }
    models[mn].addAtom( cid, resname, resid, ishet, atom );
  }

  public function addTer( mn:Int,
                          cid:String,
                          resname:String,
                          resid:Int ) {
    models[mn].addTer( cid, resname, resid );
  }

  // generate XML string from PDB data
  public function genXml():String {
    var ret:String = "<WMXML>\n";
    if ( models.length > 2 ) {
      ret += "  <GLOBAL readatonce=\"1\" readchainatonce=\"1\">\n";
    } else {
      ret += "  <GLOBAL readatonce=\"0\">\n";
    }
    ret += "    <RADIUS method=\"absolute\" />\n";
    ret += "    <AUTOSCALE manual=\"1\" />\n";
    ret += "    <RIBBON radius=\"" + Pdb.ribbon_radius + "\" />\n";
    ret += "    <COIL radius=\"" + Pdb.coil_radius + "\" />\n";
    ret += "  </GLOBAL>\n";
    ret += "  <ALIAS>\n";
    ret += "    <STRAND elem=\"RIBBON\" color=\"" + Pdb.strand_color +
                                "\" thickness=\"" + Pdb.strand_thickness +
                                "\" smoothing=\"1\" />\n";
    ret += "    <HELIX elem=\"RIBBON\" color=\"" + Pdb.helix_color +
                               "\" thickness=\"" + Pdb.helix_thickness +
                               "\" />\n";
    ret += "  </ALIAS>\n";
    for ( model in models ) {
      ret += model.genXml();
    }
    ret += "</WMXML>";
    return( ret );
  }

  // query functions
  public function getModelNum():Int { return( models.length ); }
}
