// hash key is (residue name)+(residue id). (ex. GLN102, ATP9912)

class PdbChain {
  static public var interpolation:Int = 3;

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

  public var residues( __getResidues, null ):Hash< PdbResidue >;
    public function __getResidues():Hash< PdbResidue > { return( residues ); }

  public var name( __getName, __setName ):String;
    public function __getName():String { return( name ); }
    public function __setName( n:String ):String { return( name ); }

  public var maxindex( __getMaxIndex, __setMaxIndex ):Int;
    public function __getMaxIndex():Int { return( maxindex ); }
    public function __setMaxIndex( i:Int ):Int { maxindex = i; return( maxindex ); }

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

  public function new( ?n:String = " " ) {
    residues = new Hash< PdbResidue >();
    name = n;
    maxindex = 0;
  }

  public function addAtom( resname:String,
                           resid:Int,
                           ishet:Bool,
                           atom:PdbAtom ):Void {
    // hash key for residue
    // NOTCE: never mind residue name, mind only residue number
    var hash_key:String = Std.string( resid );
    if ( !residues.exists( hash_key ) ) {
      residues.set( hash_key, new PdbResidue( ishet, resname, resid ) );
      if ( ishet ) residues.get( hash_key ).secondary = "none";
    }
    var myres:PdbResidue = residues.get( hash_key );
    myres.addAtom( atom );
    maxindex = Std.int( Math.max( maxindex, resid ) );
  }

  public function addTer( resname:String,
                          resid:Int ) {
    var hash_key:String = Std.string( resid );
    if ( !residues.exists( hash_key ) ) {
      trace( "PdbChain: internal error; unexpected inconsistency found." );
      return;
    }
    var myres:PdbResidue = residues.get( hash_key );
    myres.terminal = true;
  }

  public function assignSec( sec:PdbSecondary ):Void {
    for ( i in sec.init ... sec.last ) {
      var hash_key:String = Std.string( i );
      var myres = residues.get( hash_key );
      if ( myres != null ) {
        myres.secondary = sec.sectype;
      }
    }
  }

  public function genXml():String {
    var ret:String = "    <CHAIN N=\"" + Std.string(PdbChain.interpolation) + "\">\n";
    for ( i in 0 ... maxindex + 1 ) {
      var myres = residues.get( Std.string(i) );
      if ( myres == null ) continue;
      if ( myres.hetero ) continue;
      var str:String = myres.genPoint();
      if ( str == "" ) {
        // residues without alpha carbons are assumed to be hetero
        myres.secondary = "none";
        myres.hetero = true;
      } else {
        ret += str;
      }
    }
    ret += __genXmlSecondary();
    ret += "    </CHAIN>\n";
    return( ret );
  }

  private function __genXmlSecondary():String {
    var ret:String = "";
    var secname:String = "";
    var initnum = 0;
    var prevnum:Int = -1;
    // TODO: sophisticate logic
    for ( i in 0 ... maxindex + 1 ) {
      var myres = residues.get( Std.string(i) );
      // nothing to do for non-existing residue
      if ( myres == null ) continue;
      // initialize; first residue and some other cases
      if ( secname == "" ) {
        secname = myres.secondary;
        initnum = myres.index;
      } else {
        // 1. secondary structure type of current residue is different from prev
        // 2. current index is not next to the prev one
        // 3. current residue is terminal residue
        // 4. current residue is the last residue
        if ( secname != myres.secondary ||
             prevnum != myres.index - 1 ||
             myres.terminal ||
             i == maxindex ) {
          var mysecname:String = secname;
          if ( i == maxindex || myres.terminal ) {
            prevnum = myres.index;
            mysecname = myres.secondary;
          }
          if ( mysecname != "none" ) {
            ret += "      <" + mysecname.toUpperCase() +
                   " init=\"" + initnum +
                   "\" last=\"" + prevnum + "\" />\n";
          }
          initnum = myres.index;
          // need to be re-initialized
          if ( i == maxindex || myres.terminal ) {
            secname = "";
          } else {
            secname = myres.secondary;
          }
        }
      }
      if ( myres.hetero ) secname = "";
      prevnum = myres.index;
    }
    return( ret );
  }
}
