#pragma once

#include "SimpleMissionItem.h"
#include "QmlObjectListModel.h"

#include <QVariant>

namespace WaypointManager {
namespace Utils {


template<class CoordinateType>
CoordinateType getCoordinate(const SimpleMissionItem* item){
    return item->coordinate();
}

template<>
QVariant getCoordinate(const SimpleMissionItem* item);


/// extracts the coordinates stored in missionItems (list of MissionItems) and stores them in coordinateList.
template <class CoordinateType, template <class, class...> class ContainerType>
bool extract(const QmlObjectListModel &missionItems,
             ContainerType<CoordinateType> &coordinateList,
             std::size_t startIndex,
             std::size_t  endIndex)
{

    if (   startIndex < std::size_t(missionItems.count())
        && endIndex   < std::size_t(missionItems.count())
        && missionItems.count() > 0) {
        if (startIndex > endIndex) {
            if ( !extract(missionItems,
                          coordinateList,
                          startIndex,
                          std::size_t(missionItems.count()-1)) /*recursion*/)
                return false;
            if ( !extract(missionItems,
                          coordinateList,
                          0,
                          endIndex) /*recursion*/)
                return false;
        } else {
            for (std::size_t i = startIndex; i <= endIndex; ++i) {
                SimpleMissionItem *mItem = missionItems.value<SimpleMissionItem *>(int(i));

                if (mItem == nullptr) {
                    coordinateList.clear();
                    return false;
                }
                coordinateList.append(getCoordinate<CoordinateType>(mItem));
            }
        }
    } else
        return false;

    return true;
}

/// extracts the coordinates stored in missionItems (list of MissionItems) and stores them in coordinateList.
template <class CoordinateType, template <class, class...> class ContainerType>
bool extract(const QmlObjectListModel &missionItems,
             ContainerType<CoordinateType> &coordinateList)
{

    return extract(missionItems,
                   coordinateList,
                   std::size_t(0),
                   std::size_t(missionItems.count())
                   );
}


/// extracts the coordinates stored in missionItems (list of MissionItems) and stores them in coordinateList.
template <class CoordinateType, template <class, class...> class ContainerType>
bool extract(const ContainerType<CoordinateType>  &source,
             ContainerType<CoordinateType>  &destination,
             std::size_t startIndex,
             std::size_t  endIndex)
{

    if (   startIndex < std::size_t(source.size())
        && endIndex   < std::size_t(source.size())
        && source.size() > 0) {
        if (startIndex > endIndex) {
            if ( !extract(source,
                          destination,
                          startIndex,
                          std::size_t(long(source.size())-1)) /*recursion*/)
                return false;
            if ( !extract(source,
                          destination,
                          0,
                          endIndex) /*recursion*/)
                return false;
        } else {
            for (std::size_t i = startIndex; i <= endIndex; ++i) {
                destination.append(source[i]);
            }
        }
    } else
        return false;

    return true;
}


//!
//! \brief Initializes the list \p list.
//!
//! Adds a MissionSettingsItem to \p list.
//! \param list List of VisualMissionItems.
//! \param vehicle Vehicle.
//! \param flyView Fly- or planview.
void initialize(QmlObjectListModel &list,
                Vehicle *vehicle,
                bool flyView=true);

//! @brief Creates (from QGeoCoordinate) and Inserts a SimpleMissionItem at the given index to list.
//! \param coordinate Coordinate of the SimpleMissionItem.
//! \param index Index to insert SimpleMissionItem.
//! \param list List of SimpleMissionItems.
//! \param vehicle Vehicle.
//! \param flyView Fly- or planview.
//! \param parent Parent of the SimpleMissionItem (Set to &list if nullptr).
//! \param doUpdates Neccessary update are performed to the list \p list if set to true. Set this to true for the last item only,
//! if inserting multiple items.
//! \return Returns true on success, false either.
//!
//! @note The list must be initialized. (\see \fn initialize)
//! @note This function doesn't establish any signal-slot connections (\see \fn MissionController::_initVisualItem).
//! @note This function doesn't set the MissionFlightStatus_t (\see \fn VisualMissionItem::setMissionFlightStatus).
bool insertMissionItem(const QGeoCoordinate &coordinate,
                       long index,
                       QmlObjectListModel &list,
                       Vehicle *vehicle,
                       bool flyView=true,
                       QObject *parent=nullptr,
                       bool doUpdates=true);

bool doUpdate(QmlObjectListModel &list);

namespace detail {
    size_t nextSequenceNumber(QmlObjectListModel &list);
    bool previousAltitude(QmlObjectListModel &list,
                          size_t index,
                          double &altitude,
                          int &altitudeMode);

    //! @brief Updates the sequence numbers of the VisualMissionItems inside \p list beginning from \p startIdx.
    //!
    //! Updates the sequence numbers of the VisualMissionItems inside \p list beginning from \pstartIdx.
    //! \param list List containing VisualMissionItems.
    //! \param startIdx Start index.
    //! \return Returns true if successfull, false either.
    //!
    //! \note The function returns false immediatelly if the list \p list ist empty or
    //! the \p startIdx is out of bounds.
    //! \note If the list \p list contains only one VisualMissionItem it's sequence number is
    //! set to 0.
    bool updateSequenceNumbers(QmlObjectListModel &list, size_t startIdx=0);

    //! @brief Update the parent child item hierarchy of the VisualMissionItems inside \p list.
    //!
    //! \param list List containing VisualMissionItems.
    //! \return Returns true if successfull, false either.
    //!
    //! \note Returns true on success, false either.
    bool updateHirarchy(QmlObjectListModel &list);

    //! @brief Updates the home position to the first valid coordinate of the VisualMissionItems inside \p list.
    //!
    //! \param list List containing VisualMissionItems.
    //! \return Returns true if the home position was updated, false either.
    bool updateHomePosition(QmlObjectListModel &list);

} // namespace detail
} // namespace Utils
} // namespace WaypointManager
