// 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.


#ifndef PROMOD3_SUPERPOSE_HH
#define PROMOD3_SUPERPOSE_HH

#include <ost/geom/vecmat3_op.hh>
#include <ost/geom/mat4.hh>
#include <ost/tri_matrix.hh>

#include <promod3/core/eigen_types.hh>
#include <promod3/core/message.hh>

#include <vector>

namespace promod3 { namespace core {

// Following functions are intended to be used by other code. Fill the eigen
// matrices by themselves to avoid shouffling around too much data.
// NOTE: the EMatX3 objects get altered inside some of these functions!

// CLASSICAL MIN RMSD SUPERPOSITION ALGORITHMS

// Get minimal possible RMSD between points defined in pos_one and pos_two
Real SuperposedRMSD(EMatX3& pos_one, EMatX3& pos_two,
                    bool apply_superposition = false);

// Get transformation to superpose points in pos_one onto the ones in pos_two
geom::Mat4 MinRMSDSuperposition(EMatX3& pos_one, EMatX3& pos_two,
                                bool apply_superposition = false);

// Get both things...
std::pair<geom::Mat4,Real> Superposition(EMatX3& pos_one, EMatX3& pos_two,
                                         bool apply_superposition = false);

// Fill matrix with pairwise RMSD values
// The positions you pass will be centered, their mean will be the origin...
void FillRMSDMatrix(EMatX3List& position_list, ost::TriMatrix<Real>& data);

// ITERATIVE MIN RMSD SUPERPOSITION ALGORITHMS
// You'll always know what indices are actually used for the final
// superposition. If there are less than 3, you can expect, that the
// algorithm didn't converge.

// Get minimal possible RMSD between points defined in pos_one and pos_two
Real SuperposedRMSD(EMatX3& pos_one, EMatX3& pos_two, 
                    uint max_iterations,
                    Real distance_thresh, 
                    std::vector<uint>& indices,
                    bool apply_superposition = false);

// Get transformation to superpose points in pos_one onto the ones in pos_two
geom::Mat4 MinRMSDSuperposition(EMatX3& pos_one, EMatX3& pos_two, 
                                uint max_iterations,
                                Real distance_thresh, 
                                std::vector<uint>& indices,
                                bool apply_superposition = false);

// Get both things...
std::pair<geom::Mat4,Real> Superposition(EMatX3& pos_one, EMatX3& pos_two, 
                                         uint max_iterations,
                                         Real distance_thresh, 
                                         std::vector<uint>& indices,
                                         bool apply_superposition = false);


void RigidBlocks(EMatX3& pos_one, EMatX3& pos_two,
                 uint window_length,
                 uint max_iterations,
                 Real distance_thresh, 
                 std::vector<std::vector<uint> >& indices,
                 std::vector<geom::Mat4>& transformations);

}} //ns

#endif
