// ==========================================================================
//                 SeqAn - The Library for Sequence Analysis
// ==========================================================================
// Copyright (c) 2006-2018, Knut Reinert, FU Berlin
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Knut Reinert or the FU Berlin nor the names of
//       its contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
//
// ==========================================================================
// Author: Rene Rahn <rene.rahn@fu-berlin.de>
// ==========================================================================
// The navigator for the full score dp-matrix. We need two iterators over the
// current column and the previous column. We also store the three neighboring
// cells needed for the recursion formula.
// ==========================================================================

#ifndef SEQAN_INCLUDE_SEQAN_ALIGN_DP_MATRIX_NAVIGATOR_SCORE_H_
#define SEQAN_INCLUDE_SEQAN_ALIGN_DP_MATRIX_NAVIGATOR_SCORE_H_

namespace seqan {

// ============================================================================
// Forwards
// ============================================================================

// ============================================================================
// Tags, Classes, Enums
// ============================================================================

// ----------------------------------------------------------------------------
// Class DPMatrixNavigator                          [FullDPMatrix, ScoreMatrix]
// ----------------------------------------------------------------------------

// The navigator for the score matrix.
//
// This navigator runs on a FullDPMatrix while it navigates column wise.

template <typename TValue, typename THost, typename TNavigationSpec>
class DPMatrixNavigator_<DPMatrix_<TValue, FullDPMatrix, THost>, DPScoreMatrix, TNavigationSpec>
{
public:
    typedef  DPMatrix_<TValue, FullDPMatrix, THost> TDPMatrix_;
    typedef typename Pointer_<TDPMatrix_>::Type TDPMatrixPointer_;
    typedef typename Iterator<TDPMatrix_, Standard>::Type TDPMatrixIterator;

    template <typename TBandSpec,
              std::enable_if_t<std::is_same<TBandSpec, BandOff>::value, int> = 0>
    DPMatrixNavigator_(TDPMatrix_ & matrix,
                       DPBandConfig<TBandSpec> const & /*band*/)
    {
        _ptrDataContainer = &matrix;
        _prevColIteratorOffset = _dataFactors(matrix)[DPMatrixDimension_::HORIZONTAL];
        _activeColIterator = begin(matrix, Standard());
        _prevColIterator = _activeColIterator - _prevColIteratorOffset;
        _laneLeap = 1;
        *_activeColIterator = TValue();
    }

    template <typename TBandSpec,
              std::enable_if_t<!std::is_same<TBandSpec, BandOff>::value, int> = 0>
    DPMatrixNavigator_(TDPMatrix_ & matrix,
                       DPBandConfig<TBandSpec> const & band)
    {
        using TMatrixSize = typename Size<TDPMatrix_>::Type;
        using TSignedSize = std::make_signed_t<TMatrixSize>;

        _ptrDataContainer = &matrix;
        _prevColIteratorOffset = _dataFactors(matrix)[DPMatrixDimension_::HORIZONTAL];
        // Band begins within the first row.
        if (lowerDiagonal(band) >= 0)
        {
            _laneLeap = _min(length(matrix, DPMatrixDimension_::VERTICAL), bandSize(band));
            _activeColIterator = begin(matrix, Standard()) + _dataLengths(matrix)[DPMatrixDimension_::VERTICAL] - 1;
        }
        else if (upperDiagonal(band) <= 0)  // Band begins within the first column.
        {
            _laneLeap = 1;
            _activeColIterator = begin(matrix, Standard());
        }
        else  // Band intersects with the point of origin.
        {
            TMatrixSize lengthVertical = length(matrix, DPMatrixDimension_::VERTICAL);
            int lastPos = _max(-static_cast<TSignedSize>(lengthVertical - 1), lowerDiagonal(band));
            _laneLeap = lengthVertical + lastPos;
            _activeColIterator = begin(matrix, Standard()) + _laneLeap - 1;
        }
        // Set previous iterator to same position, one column left.
        _prevColIterator = _activeColIterator - _prevColIteratorOffset;
        *_activeColIterator = TValue();
    }

    TDPMatrixPointer_ _ptrDataContainer{nullptr};   // Pointer to the matrix this navigator is working on.
    int               _laneLeap{0};                 // Stores the jump to the next column
    size_t            _prevColIteratorOffset{0};    // Offset to reset the previous column iterator when going to the next cell.
    TDPMatrixIterator _activeColIterator{};         // The active column iterator.
    TDPMatrixIterator _prevColIterator{};           // The previous column iterator.
};

// ============================================================================
// Metafunctions
// ============================================================================

// ============================================================================
// Functions
// ============================================================================

// ----------------------------------------------------------------------------
// Function _goNextCell                                     [banded, FirstCell]
// ----------------------------------------------------------------------------

// Needed to avoid ambigious overload.
template <typename TValue, typename TDPMatrixSpec, typename THost>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, TDPMatrixSpec, THost>, DPScoreMatrix, NavigateColumnWiseBanded> & /*dpNavigator*/,
            MetaColumnDescriptor<DPInitialColumn, PartialColumnTop> const &,
            FirstCell const &)
{
    // no-op
}

// Needed to avoid ambigious overload.
template <typename TValue, typename TDPMatrixSpec, typename THost>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, TDPMatrixSpec, THost>, DPScoreMatrix, NavigateColumnWiseBanded> & /*dpNavigator*/,
            MetaColumnDescriptor<DPInitialColumn, FullColumn> const &,
            FirstCell const &)
{
    // no-op
}

// specialized for initialization column and all other column locations.
template <typename TValue, typename TDPMatrixSpec, typename THost,
          typename TColumnLocation>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, TDPMatrixSpec, THost>, DPScoreMatrix, NavigateColumnWiseBanded> & /*dpNavigator*/,
            MetaColumnDescriptor<DPInitialColumn, TColumnLocation> const &,
            FirstCell const &)
{
    // no-op
}

//  specialized for all other column types located at the top.
template <typename TValue, typename TDPMatrixSpec, typename THost,
          typename TColumnType>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, TDPMatrixSpec, THost>, DPScoreMatrix, NavigateColumnWiseBanded> & dpNavigator,
            MetaColumnDescriptor<TColumnType, PartialColumnTop> const &,
            FirstCell const &)
{
    --dpNavigator._laneLeap;
    dpNavigator._activeColIterator += dpNavigator._laneLeap;
    dpNavigator._prevColIterator = dpNavigator._activeColIterator - dpNavigator._prevColIteratorOffset + 1;
}

template <typename TValue, typename TDPMatrixSpec, typename THost,
          typename TColumnType>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, TDPMatrixSpec, THost>, DPScoreMatrix, NavigateColumnWiseBanded> & dpNavigator,
            MetaColumnDescriptor<TColumnType, FullColumn> const &,
            FirstCell const &)
{
    dpNavigator._activeColIterator += dpNavigator._laneLeap;
    dpNavigator._prevColIterator = dpNavigator._activeColIterator - dpNavigator._prevColIteratorOffset;
}

// version for all other column types and locations.
template <typename TValue, typename TDPMatrixSpec, typename THost,
          typename TColumnType, typename TColumnLocation>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, TDPMatrixSpec, THost>, DPScoreMatrix, NavigateColumnWiseBanded> & dpNavigator,
            MetaColumnDescriptor<TColumnType, TColumnLocation> const &,
            FirstCell const &)
{
    dpNavigator._activeColIterator += dpNavigator._laneLeap;
    dpNavigator._prevColIterator = dpNavigator._activeColIterator - dpNavigator._prevColIteratorOffset + 1;
}

// ----------------------------------------------------------------------------
// Function _goNextCell                                   [unbanded, FirstCell]
// ----------------------------------------------------------------------------

// specialized for initalization column.
template <typename TValue, typename THost>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, FullDPMatrix, THost>, DPScoreMatrix, NavigateColumnWise> & /*dpNavigator*/,
            MetaColumnDescriptor<DPInitialColumn, FullColumn> const &,
            FirstCell const &)
{
    // no-op
}

// all other column types.
template <typename TValue, typename THost,
          typename TColumnType>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, FullDPMatrix, THost>, DPScoreMatrix, NavigateColumnWise> & dpNavigator,
            MetaColumnDescriptor<TColumnType, FullColumn> const &,
            FirstCell const &)
{
    // Set to begin of column.
    dpNavigator._activeColIterator += dpNavigator._laneLeap;
    dpNavigator._prevColIterator = dpNavigator._activeColIterator - dpNavigator._prevColIteratorOffset;

}

// ----------------------------------------------------------------------------
// Function _goNextCell                                     [banded, InnerCell]
// ----------------------------------------------------------------------------

// specilized for the initialization column and all column locations.
template <typename TValue, typename TDPMatrixSpec, typename THost,
          typename TColumnLocation>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, TDPMatrixSpec, THost>, DPScoreMatrix, NavigateColumnWiseBanded> & dpNavigator,
            MetaColumnDescriptor<DPInitialColumn, TColumnLocation> const &,
            InnerCell const &)
{
    ++dpNavigator._activeColIterator;
}

// specialized for the standard band processing.
template <typename TValue, typename TDPMatrixSpec, typename THost,
          typename TColumnType, typename TColumnLocation>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, TDPMatrixSpec, THost>, DPScoreMatrix, NavigateColumnWiseBanded> & dpNavigator,
            MetaColumnDescriptor<TColumnType, TColumnLocation> const &,
            InnerCell const &)
{
    ++dpNavigator._activeColIterator;
    ++dpNavigator._prevColIterator;
}

// ----------------------------------------------------------------------------
// Function _goNextCell                                   [unbanded, InnerCell]
// ----------------------------------------------------------------------------

// specialized for the initialization column.
template <typename TValue, typename THost>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, FullDPMatrix, THost>, DPScoreMatrix, NavigateColumnWise> & dpNavigator,
            MetaColumnDescriptor<DPInitialColumn, FullColumn> const &,
            InnerCell const &)
{
    ++dpNavigator._activeColIterator;
}

// version for the all other column types.
template <typename TValue, typename THost,
          typename TColumnType>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, FullDPMatrix, THost>, DPScoreMatrix, NavigateColumnWise> & dpNavigator,
            MetaColumnDescriptor<TColumnType, FullColumn> const &,
            InnerCell const &)
{
    ++dpNavigator._activeColIterator;
    ++dpNavigator._prevColIterator;
}

// ----------------------------------------------------------------------------
// Function _goNextCell                                      [banded, LastCell]
// ----------------------------------------------------------------------------

// specilaized for initialization column and bottom column.
template <typename TValue, typename TDPMatrixSpec, typename THost>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, TDPMatrixSpec, THost>, DPScoreMatrix, NavigateColumnWiseBanded> & dpNavigator,
            MetaColumnDescriptor<DPInitialColumn, PartialColumnBottom> const &,
            LastCell const &)
{
    ++dpNavigator._activeColIterator;
}

// specilaized for initialization column and all other column locations.
template <typename TValue, typename TDPMatrixSpec, typename THost,
          typename TColumnLocation>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, TDPMatrixSpec, THost>, DPScoreMatrix, NavigateColumnWiseBanded> & dpNavigator,
            MetaColumnDescriptor<DPInitialColumn, TColumnLocation> const &,
            LastCell const &)
{
    ++dpNavigator._activeColIterator;
}

// specialized for all column types and bottom column.
template <typename TValue, typename TDPMatrixSpec, typename THost,
          typename TColumnType>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, TDPMatrixSpec, THost>, DPScoreMatrix, NavigateColumnWiseBanded> & dpNavigator,
            MetaColumnDescriptor<TColumnType, PartialColumnBottom> const &,
            LastCell const &)
{
    ++dpNavigator._activeColIterator;
    ++dpNavigator._prevColIterator;
    ++dpNavigator._laneLeap;
}

// generic case for all other column types and column locations.
template <typename TValue, typename TDPMatrixSpec, typename THost,
          typename TColumnType, typename TColumnLocation>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, TDPMatrixSpec, THost>, DPScoreMatrix, NavigateColumnWiseBanded> & dpNavigator,
            MetaColumnDescriptor<TColumnType, TColumnLocation> const &,
            LastCell const &)
{
    ++dpNavigator._activeColIterator;
    ++dpNavigator._prevColIterator;
}

// ----------------------------------------------------------------------------
// Function _goNextCell                                    [unbanded, LastCell]
// ----------------------------------------------------------------------------

// specilaized for initialization column.
template <typename TValue, typename THost>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, FullDPMatrix, THost>, DPScoreMatrix, NavigateColumnWise> & dpNavigator,
            MetaColumnDescriptor<DPInitialColumn, FullColumn> const &,
            LastCell const &)
{
    ++dpNavigator._activeColIterator;
}

// version for all other column types.
template <typename TValue, typename THost,
          typename TColumnType>
inline void
_goNextCell(DPMatrixNavigator_<DPMatrix_<TValue, FullDPMatrix, THost>, DPScoreMatrix, NavigateColumnWise> & dpNavigator,
            MetaColumnDescriptor<TColumnType, FullColumn> const &,
            LastCell const &)
{
    ++dpNavigator._activeColIterator; // go to next cell.
    ++dpNavigator._prevColIterator; // go to next cell.
}

// ----------------------------------------------------------------------------
// Function _preInitCacheDiagonal()
// ----------------------------------------------------------------------------

template <typename TDPCell,
          typename TValue, typename TMatrixSpec, typename THost,
          typename TColumnType>
inline void
_preInitCacheDiagonal(TDPCell & cacheDiagonal,
                      DPMatrixNavigator_<DPMatrix_<TValue, TMatrixSpec, THost>, DPScoreMatrix, NavigateColumnWiseBanded> const & dpNavigator,
                      MetaColumnDescriptor<TColumnType, PartialColumnMiddle> const & /*tag*/)
{
    _scoreOfCell(cacheDiagonal) = _scoreOfCell(*(dpNavigator._prevColIterator - 1));
}

template <typename TDPCell,
          typename TValue, typename TMatrixSpec, typename THost,
          typename TColumnType>
inline void
_preInitCacheDiagonal(TDPCell & cacheDiagonal,
                      DPMatrixNavigator_<DPMatrix_<TValue, TMatrixSpec, THost>, DPScoreMatrix, NavigateColumnWiseBanded> const & dpNavigator,
                      MetaColumnDescriptor<TColumnType, PartialColumnBottom> const & /*tag*/)
{
    _scoreOfCell(cacheDiagonal) = _scoreOfCell(*(dpNavigator._prevColIterator - 1));
}

template <typename TDPCell,
          typename TValue, typename TMatrixSpec, typename THost, typename TNavigationSpec,
          typename TColumnType, typename TColumnLocation>
inline void
_preInitCacheDiagonal(TDPCell & /*cacheDiagonal*/,
                      DPMatrixNavigator_<DPMatrix_<TValue, TMatrixSpec, THost>, DPScoreMatrix, TNavigationSpec> const & /*dpNavigator*/,
                      MetaColumnDescriptor<TColumnType, TColumnLocation> const & /*tag*/)
{
    // no-op
}

// ----------------------------------------------------------------------------
// Function previousCellHorizontal()
// ----------------------------------------------------------------------------

template <typename TDPMatrix, typename TNavigationSpec>
inline typename Reference<DPMatrixNavigator_<TDPMatrix, DPScoreMatrix, TNavigationSpec> >::Type
previousCellHorizontal(DPMatrixNavigator_<TDPMatrix, DPScoreMatrix, TNavigationSpec> & dpNavigator)
{
    return *dpNavigator._prevColIterator;
}

template <typename TDPMatrix, typename TNavigationSpec>
inline typename Reference<DPMatrixNavigator_<TDPMatrix, DPScoreMatrix, TNavigationSpec> const>::Type
previousCellHorizontal(DPMatrixNavigator_<TDPMatrix, DPScoreMatrix, TNavigationSpec> const & dpNavigator)
{
    return *dpNavigator._prevColIterator;
}

}  // namespace seqan

#endif  // #ifndef SEQAN_INCLUDE_SEQAN_ALIGN_DP_MATRIX_NAVIGATOR_SCORE_H_