// --------------------------------------------------------------------
// 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 tinylib.Point3D;

/**
  Torsion angle
**/

class RamaAngle {
  /**
    is torsion angle available now?
  **/
  public var isAvailable( get, null ):Bool;
    /**
      getter of `isAvailable`
    **/
    public function get_isAvailable():Bool { return( isAvailable ); }
  /**
    torsion angle
  **/
  @:isVar public var angle( get, set ):Float;
    /**
      getter of `angle`
    **/
    public function get_angle():Float { return( angle ); }
    /**
      setter of `angle`
    **/
    public function set_angle( a:Float ):Float {
      angle = a;
      return( angle );
    }
  // -----------------------------------------------------------------------
  public function new() {
    isAvailable = false;
    angle = 0.0;
  }

  /**
    calculate torstion angle, where `p0`, `p1`, `p2`, and `p3` are the
    positions. Note that this function does not return torsion angle.
  **/
  public function calc( p0:Point3D,
                        p1:Point3D,
                        p2:Point3D,
                        p3:Point3D ) {
    var v01 = Point3D.getSub( p0, p1 );
    var v12 = Point3D.getSub( p1, p2 );
    var v23 = Point3D.getSub( p2, p3 );
    var v01_12 = Point3D.getCross( v01, v12 );
    var v12_23 = Point3D.getCross( v12, v23 );
    var v12_0112 = Point3D.getCross( v12, v01_12 );
    var cosphi = v01_12.dot( v12_23 ) / ( v01_12.norm() * v12_23.norm() );
    cosphi = Math.min( Math.max( cosphi, -1.0 ), 1.0 );
    var sinphi = v12_0112.dot( v12_23 ) / ( v12_0112.norm() * v12_23.norm() );
    sinphi = Math.min( Math.max( sinphi, -1.0 ), 1.0 );
    angle = - Math.atan2( sinphi, cosphi );
    isAvailable = true;
  }
}

/**
  Container for phi and psi angles
**/
class RamaAngles {
  private var phi:RamaAngle; // CA-N
  private var psi:RamaAngle; // CA-C
  // ----------------------------------------------------------------------
  public function new() {
    phi = new RamaAngle();
    psi = new RamaAngle();
  }

  /**
    get phi angle; return value is an anonymous group:

    avail: is the angle available?
    val: phi angle (if avail is false, the value is meaningless)
  **/
  public function getPhi():Dynamic {
    var ret = { avail:phi.isAvailable, val:phi.angle };
    return( ret );
  }

  /**
    get psi angle; return value is an anonymous group:

    avail: is the angle available?
    val: psi angle (if avail is false, the value is meaningless)
  **/
  public function getPsi():Dynamic {
    var ret = { avail:psi.isAvailable, val:psi.angle };
    return( ret );
  }

  /**
    calculate phi angle, return value is an anonymous group. See `getPhi`.
  **/
  public function calcPhi( p0:Point3D,
                           p1:Point3D,
                           p2:Point3D,
                           p3:Point3D ):Dynamic {
    phi.calc( p0, p1, p2, p3 );
    return( getPhi() );
  }

  /**
    calculate psi angle, return value is and anonymous group. See `getPsi`.
  **/
  public function calcPsi( p0:Point3D,
                           p1:Point3D,
                           p2:Point3D,
                           p3:Point3D ):Dynamic {
    psi.calc( p0, p1, p2, p3 );
    return( getPsi() );
  }
}
