#pragma once

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

#include <QVariant>


class Vehicle;
class QGeoCoordinate;



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 Makes the SimpleMissionItem \p item a takeoff command.
//! \param item SimpleMissionItem
//! \param vehilce Vehicle.
//! \return Returns true if successfull, false either.
//!
bool makeTakeOffCmd(SimpleMissionItem *item, Vehicle *vehicle);

//!
//! \brief Makes the SimpleMissionItem \p item a land command.
//! \param item SimpleMissionItem
//! \param vehilce Vehicle.
//! \return Returns true if successfull, false either.
//!
bool makeLandCmd(SimpleMissionItem *item, Vehicle *vehicle);

//!
//! \brief Makes the SimpleMissionItem \p item a MAV_CMD_DO_CHANGE_SPEED item.
//! \param item SimpleMissionItem.
//! \param speed Speed (must be greater zero).
//!
void makeSpeedCmd(SimpleMissionItem *item, double speed);


//!
//! \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);
//!
//! \brief Performs a list update.
//!
//! Updates the home position of the MissionSettingsItem, the parent child hirarchy and the sequence numbers.
//! \param list List containing VisualMissionItems.
//! \return Returns true if no errors occured, false either.
bool updateList(QmlObjectListModel &list);

//! @brief Updates the sequence numbers of the VisualMissionItems inside \p list.
//!
//! \param list List containing VisualMissionItems.
//! \param firstSeqNumber The sequence number of the fisrt list entry (defaul = 0).
//! \return Returns true if successfull, false either.
//!
//! \note If the list \p list contains only one VisualMissionItem it's sequence number is
//! set to 0.
bool updateSequenceNumbers(QmlObjectListModel &list, size_t firstSeqNumber = 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);

//!
//! \brief Prints the \p list.
//! \param list List containing VisualMissionItems.
void printList(QmlObjectListModel &list);

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

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