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

package pdb;

import pdb.PdbAtom;
import pdb.RamaAngles;

import tinylib.Point3D;

/**
  Container of a residue in pdb file.
  Non-amino acid molecules such as waters, co-factors, lipids are considered as
  residues.
**/

class PdbResidue extends pdb.ResidueBase {
  /**
    atoms in this residue, where the map key is the name of the atom.
  **/
  public var atoms( get, null ):Map< String, PdbAtom >;
    /**
      getter of `atoms`
    **/
    public function get_atoms():Map< String, PdbAtom > { return( atoms ); }

  /**
    secondary structure type. See `SecondaryType.hx` for the meaning of the
    values.
  **/
  @:isVar public var secondary( get, set ):Int;
    /**
      gettter of `secondary`
    **/
    public function get_secondary():Int { return( secondary ); }
    /**
      settter of `secondary`
    **/
    public function set_secondary( s:Int ):Int {
      secondary = s;
      return( secondary );
    }

  /**
    phi and psi angle information
  **/
  public var rama( get, null ):RamaAngles;
    /**
      getter of `rama`
    **/
    public function get_rama():RamaAngles { return( rama ); }

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

  /**
    Constructor.

    - n: residue name
    - i: residue index
    - isnt: is this residue the N-terminal?
    - isct: is this residue the C-terminal?
    - sec: seondary structure type (see SecondaryType.hx)
  **/
  public function new( ?n:String = "",
                       ?i:Int = 1,
                       ?isnt:Bool = false,
                       ?isct:Bool = false,
                       ?sec:Int = 0 ) {
    super( n, i, isnt, isct );
    clearAtoms();
    secondary = sec;
    rama = new RamaAngles();
  }

  /**
    remove all atoms from this residue
  **/
  public function clearAtoms() {
    atoms = new Map< String, PdbAtom >();
  }

  /**
    add an atom to this residue
  **/
  public function addAtom( a:PdbAtom ):Void {
    if ( a == null ) return;
    if ( !atoms.exists( a.name ) ) {
      atoms.set( a.name, a.clone() );
    }
    // duplicated atoms are simply ignored
  }

  /**
    get atom of name `a` from this residue
  **/
  public function getAtom( a:String ):PdbAtom {
    if ( !atoms.exists( a ) ) return( null );
    return( atoms.get( a ) );
  }

  /**
    returns whether this residue is amino acids or not
  **/
  public function isAmino():Bool {
    if ( atoms.exists( "N" ) && atoms.exists( "CA" ) &&
         atoms.exists( "C" ) && atoms.exists( "O" ) ) return( true );
    return( false );
  }

  /**
    write position of alpha-carbon, "CA" in POINT element of WMXML xml.
  **/
  public function genPoint():String {
    if ( !isAmino() ) return( "" );
    var atom = atoms.get( "CA" );
    if ( atom == null ) return( "" );
    // atom.pos.toString() must not be used;
    // that function flips z-coordinate
    return( "      <POINT index=\"" + index +
            "\" pos=\"" + atom.pos.x +
            " " + atom.pos.y +
            " " + atom.pos.z + "\" />\n" );
  }

  /**
    generate "H" atom, which binds to backbone "N" atom.
    the parameter c corresponds to the position of "C" atom in the previous
    residue. thus, "H" atom never be created for first residue
  **/
  public function genH( ?r:PdbResidue = null ):Void {
    if ( atoms.exists( "H" ) ) return;
    if ( r == null ) return;
    if ( r.getAtom( "C" ) == null ) return;
    genHatom( r.getAtom( "C" ) );
  }
  /**
    add H atom which binds to backbone N using C atom of adjacent residue
    given by `a`
  **/
  public function genHatom( a:PdbAtom ):Void {
    if ( atoms.exists( "H" ) ) return;
    if ( a == null ) return;
    genHpos( a.pos );
  }
  /**
    generate H atom; generated atom is automatically registered in this instance
  **/
  public function genHpos( c:Point3D ):Void {
    if ( atoms.exists( "H" ) ) return;
    // if not an amino acid, do nothing.
    if ( !isAmino() ) return;
    var pos_nc:Point3D = Point3D.getSub( atoms.get( "N" ).pos, c );
    var pos_nca:Point3D = Point3D.getSub( atoms.get( "N" ).pos, atoms.get( "CA" ).pos );
    pos_nc.normalize();
    pos_nca.normalize();
    pos_nc.add( pos_nca );
    pos_nc.normalize();
    pos_nc.multiply( pdb.HBParams.NH_dist );
    pos_nc.add( atoms.get( "N" ).pos );
    // add created hydrogen atom to this residue
    addAtom( new PdbAtom( "H", 0, pos_nc, 1.0, 0.0, "H" ) );
  }
}
