// Copyright (c) 2013-2020, SIB - Swiss Institute of Bioinformatics and
//                          Biozentrum - University of Basel
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


#include <promod3/core/geom_stems.hh>
#include <cmath>
#include <cassert>
#include <promod3/core/message.hh>

namespace promod3 { namespace core {

StemCoords::StemCoords(const ost::mol::ResidueHandle& res) {
  // get atoms
  ost::mol::AtomHandle n = res.FindAtom("N");
  ost::mol::AtomHandle ca = res.FindAtom("CA");
  ost::mol::AtomHandle c = res.FindAtom("C");
  // check them
  if (!(n.IsValid() && ca.IsValid() && c.IsValid())) {
    throw promod3::Error("Cannot extract stems from incomplete residues!");
  }
  // set internal positions
  n_coord = n.GetPos();
  ca_coord = ca.GetPos();
  c_coord  = c.GetPos();
}

StemPairOrientation::StemPairOrientation(const StemCoords& n_stem,
                                         const StemCoords& c_stem) {
  Init(n_stem.n_coord, n_stem.ca_coord, n_stem.c_coord,
       c_stem.n_coord, c_stem.ca_coord, c_stem.c_coord);
}

StemPairOrientation::StemPairOrientation(const geom::Vec3& n_stem_n,
                                         const geom::Vec3& n_stem_ca,
                                         const geom::Vec3& n_stem_c,
                                         const geom::Vec3& c_stem_n,
                                         const geom::Vec3& c_stem_ca,
                                         const geom::Vec3& c_stem_c) {
  Init(n_stem_n, n_stem_ca, n_stem_c, c_stem_n, c_stem_ca, c_stem_c);
}

void StemPairOrientation::Init(const geom::Vec3& n_stem_n,
                               const geom::Vec3& n_stem_ca,
                               const geom::Vec3& n_stem_c,
                               const geom::Vec3& c_stem_n,
                               const geom::Vec3& c_stem_ca,
                               const geom::Vec3& c_stem_c) {
  // get distance from N-stem-C to C-stem-N
  geom::Vec3 N_C_vector = c_stem_n - n_stem_c;
  distance = geom::Length(N_C_vector);

  // the two normals, built by the corresponding backbone atoms of the two stem
  // residues
  geom::Vec3 stem_one_normal
   = geom::Normalize(geom::Cross(n_stem_n - n_stem_ca,
                                 n_stem_c - n_stem_ca));
  geom::Vec3 stem_two_normal
   = geom::Normalize(geom::Cross(c_stem_n - c_stem_ca,
                                 c_stem_c - c_stem_ca));

  geom::Vec3 stem_two_n_onto_plane_one
   = c_stem_n - geom::Dot(N_C_vector, stem_one_normal) * stem_one_normal;
  geom::Vec3 stem_one_c_to_stem_two_n_onto_plane
   = stem_two_n_onto_plane_one - n_stem_c;
  
  geom::Vec3 ref_axis = stem_one_normal;
  angle_one = geom::SignedAngle(stem_one_c_to_stem_two_n_onto_plane,
                                n_stem_c - n_stem_ca, ref_axis);

  ref_axis = geom::Cross(stem_one_normal, stem_one_c_to_stem_two_n_onto_plane);
  angle_two = geom::SignedAngle(stem_one_c_to_stem_two_n_onto_plane,
                                N_C_vector, ref_axis);

  // exactly the same stuff, just relative to stem two
  geom::Vec3 stem_one_c_onto_plane_two
   = n_stem_c - geom::Dot(-N_C_vector, stem_two_normal) * stem_two_normal;
  geom::Vec3 stem_two_n_to_stem_one_c_onto_plane
   = stem_one_c_onto_plane_two - c_stem_n;

  ref_axis = stem_two_normal;
  angle_three = geom::SignedAngle(stem_two_n_to_stem_one_c_onto_plane,
                                  c_stem_n - c_stem_ca, ref_axis);

  ref_axis = geom::Cross(stem_two_normal, stem_two_n_to_stem_one_c_onto_plane);
  angle_four = geom::SignedAngle(stem_two_n_to_stem_one_c_onto_plane,
                                 -N_C_vector, ref_axis);
}

}} // ns