 #include "WimaPlaner.h"

#include "CircularSurveyComplexItem.h"



const char* WimaPlaner::wimaFileExtension   = "wima";
const char* WimaPlaner::areaItemsName       = "AreaItems";
const char* WimaPlaner::missionItemsName    = "MissionItems";

WimaPlaner::WimaPlaner(QObject *parent)
    : QObject                       (parent)
    , _currentAreaIndex             (-1)
    , _container                    (nullptr)
    , _joinedArea                   (this)
    , _joinedAreaValid              (false)
    , _measurementArea              (this)
    , _serviceArea                  (this)
    , _corridor                     (this)
    , _circularSurvey               (nullptr)
    , _surveyRefChanging            (false)
    , _measurementAreaChanging      (false)
    , _corridorChanging             (false)
    , _serviceAreaChanging          (false)
    , _syncronizedWithController    (false)
    , _readyForSync                 (false)
{
    _lastMeasurementAreaPath    = _measurementArea.path();
    _lastServiceAreaPath        = _serviceArea.path();
    _lastCorridorPath           = _corridor.path();

    connect(this, &WimaPlaner::currentPolygonIndexChanged, this, &WimaPlaner::recalcPolygonInteractivity);
    connect(&_updateTimer, &QTimer::timeout, this, &WimaPlaner::updateTimerSlot);
    connect(this, &WimaPlaner::joinedAreaValidChanged, this, &WimaPlaner::updateMission);

    _updateTimer.setInterval(500); // 250 ms means: max update time 2*250 ms
    _updateTimer.start();

    // for debugging and testing purpose
    connect(&_autoLoadTimer, &QTimer::timeout, this, &WimaPlaner::autoLoadMission);
    _autoLoadTimer.setSingleShot(true);
    _autoLoadTimer.start(300);
}

QmlObjectListModel* WimaPlaner::visualItems()
{
    return &_visualItems;
}

QStringList WimaPlaner::loadNameFilters() const
{
    QStringList filters;

    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension) <<
               tr("All Files (*.*)");
    return filters;
}

QStringList WimaPlaner::saveNameFilters() const
{
    QStringList filters;

    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension);
    return filters;
}

QGeoCoordinate WimaPlaner::joinedAreaCenter() const
{
    return _joinedArea.center();
}

void WimaPlaner::setMasterController(PlanMasterController *masterC)
{
    _masterController = masterC;
    emit masterControllerChanged();
}

void WimaPlaner::setMissionController(MissionController *missionC)
{
    _missionController = missionC;
    emit missionControllerChanged();
}

void WimaPlaner::setCurrentPolygonIndex(int index)
{
    if(index >= 0 && index < _visualItems.count() && index != _currentAreaIndex){
        _currentAreaIndex = index;

        emit currentPolygonIndexChanged(index);
    }
}

void WimaPlaner::setDataContainer(WimaDataContainer *container)
{
    if (container != nullptr) {
        _container = container;
        emit dataContainerChanged();
    }
}

bool WimaPlaner::syncronizedWithController()
{
    return _syncronizedWithController;
}

bool WimaPlaner::readyForSync()
{
    return _readyForSync;
}

void WimaPlaner::removeArea(int index)
{
    if(index >= 0 && index < _visualItems.count()){
        WimaArea* area = qobject_cast<WimaArea*>(_visualItems.removeAt(index));

        if ( area == nullptr) {
            qWarning("WimaPlaner::removeArea(): nullptr catched, internal error.");
            return;
        }
        area->clear();
        area->borderPolygon()->clear();

        emit visualItemsChanged();

        if (_visualItems.count() == 0) {
            // this branch is reached if all items are removed
            // to guarentee proper behavior, _currentAreaIndex must be set to a invalid value, as on constructor init.
            resetAllInteractive();
            _currentAreaIndex = -1;
            return;
        }

        if(_currentAreaIndex >= _visualItems.count()){
            setCurrentPolygonIndex(_visualItems.count() - 1);
        }else{
            recalcPolygonInteractivity(_currentAreaIndex);
        }
    }else{
        qWarning("Index out of bounds!");
    }

}

bool WimaPlaner::addMeasurementArea()
{
    if (!_visualItems.contains(&_measurementArea)) {
        _visualItems.append(&_measurementArea);

        int newIndex = _visualItems.count()-1;
        setCurrentPolygonIndex(newIndex);

        emit visualItemsChanged();
        return true;
    } else {
        return false;
    }
}

bool WimaPlaner::addServiceArea()
{
    if (!_visualItems.contains(&_serviceArea)) {
        _visualItems.append(&_serviceArea);

        int newIndex = _visualItems.count()-1;
        setCurrentPolygonIndex(newIndex);

        emit visualItemsChanged();
        return true;
    } else {
        return false;
    }
}

bool WimaPlaner::addCorridor()
{
    if (!_visualItems.contains(&_corridor)) {
        _visualItems.append(&_corridor);

        int newIndex = _visualItems.count()-1;
        setCurrentPolygonIndex(newIndex);

        emit visualItemsChanged();
        return true;
    } else {
        return false;
    }
}

void WimaPlaner::removeAll()
{
    bool changesApplied = false;
    while (_visualItems.count() > 0) {
        removeArea(0);
        changesApplied = true;
    }

    _missionController->removeAll();

    _currentFile = "";

    _circularSurvey = nullptr;
    _surveyRefChanging = false;


    emit currentFileChanged();
    if ( changesApplied )
         emit visualItemsChanged();
}

bool WimaPlaner::updateMission()
{

   setReadyForSync(false);

    if ( !_joinedAreaValid)
        return false;


    // extract old survey data
    QmlObjectListModel* missionItems        = _missionController->visualItems();

    int surveyIndex = missionItems->indexOf(_circularSurvey);

    // create survey item if not yet present
    if (surveyIndex == -1) {
        _missionController->insertComplexMissionItem(_missionController->circularSurveyComplexItemName(), _measurementArea.center(), missionItems->count());
        _circularSurvey = qobject_cast<CircularSurveyComplexItem*>(missionItems->get(missionItems->count()-1));

        if (_circularSurvey == nullptr){
            qWarning("WimaPlaner::updateMission(): survey == nullptr");
            return false;
        }


        // establish connections
        _circularSurvey->setRefPoint(_measurementArea.center());
        _lastSurveyRefPoint = _measurementArea.center();
        _surveyRefChanging = false;
        _circularSurvey->setIsInitialized(true); // prevents reinitialisation from gui
        connect(_circularSurvey->deltaR(),               &Fact::rawValueChanged, this, &WimaPlaner::calcArrivalAndReturnPath);
        connect(_circularSurvey->deltaAlpha(),           &Fact::rawValueChanged, this, &WimaPlaner::calcArrivalAndReturnPath);
        connect(_circularSurvey->isSnakePath(),          &Fact::rawValueChanged, this, &WimaPlaner::calcArrivalAndReturnPath);
        connect(_circularSurvey->transectMinLength(),    &Fact::rawValueChanged, this, &WimaPlaner::calcArrivalAndReturnPath);
    }

    // update survey area
    _circularSurvey->surveyAreaPolygon()->clear();
    _circularSurvey->surveyAreaPolygon()->appendVertices(_measurementArea.coordinateList());

    calcArrivalAndReturnPath();

    setReadyForSync(true);
    return true;
}

void WimaPlaner::saveToCurrent()
{
    saveToFile(_currentFile);
}

void WimaPlaner::saveToFile(const QString& filename)
{
    if (filename.isEmpty()) {
        return;
    }

    QString planFilename = filename;
    if (!QFileInfo(filename).fileName().contains(".")) {
        planFilename += QString(".%1").arg(wimaFileExtension);
    }

    QFile file(planFilename);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qgcApp()->showMessage(tr("Plan save error %1 : %2").arg(filename).arg(file.errorString()));
        _currentFile.clear();
        emit currentFileChanged();
    } else {
        FileType fileType = FileType::WimaFile;
        if ( planFilename.contains(QString(".%1").arg(wimaFileExtension)) ) {
            fileType = FileType::WimaFile;
        } else if ( planFilename.contains(QString(".%1").arg(AppSettings::planFileExtension)) ) {
            fileType = FileType::PlanFile;
        } else {
            if ( planFilename.contains(".") ) {
                qgcApp()->showMessage(tr("File format not supported"));
            } else {
                qgcApp()->showMessage(tr("File without file extension not accepted."));
                return;
            }
        }

        QJsonDocument saveDoc = saveToJson(fileType);
        file.write(saveDoc.toJson());
        if(_currentFile != planFilename) {
            _currentFile = planFilename;
            emit currentFileChanged();
        }
    }
}

bool WimaPlaner::loadFromCurrent()
{
    return loadFromFile(_currentFile);
}

bool WimaPlaner::loadFromFile(const QString &filename)
{
    #define debug 0
    QString errorString;
    QString errorMessage = tr("Error loading Plan file (%1). %2").arg(filename).arg("%1");

    if (filename.isEmpty()) {
        return false;
    }

    QFileInfo fileInfo(filename);
    QFile file(filename);

    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        errorString = file.errorString() + QStringLiteral(" ") + filename;
        qgcApp()->showMessage(errorMessage.arg(errorString));
        return false;
    }

    if(fileInfo.suffix() == wimaFileExtension) {
        QJsonDocument   jsonDoc;
        QByteArray      bytes = file.readAll();

        if (!JsonHelper::isJsonFile(bytes, jsonDoc, errorString)) {
            qgcApp()->showMessage(errorMessage.arg(errorString));
            return false;
        }

        QJsonObject json = jsonDoc.object();
        // AreaItems
        QJsonArray areaArray = json[areaItemsName].toArray();
        _visualItems.clear();

        int validAreaCounter = 0;
        for( int i = 0; i < areaArray.size() && validAreaCounter < 3; i++) {
            QJsonObject jsonArea = areaArray[i].toObject();

            if (jsonArea.contains(WimaArea::areaTypeName) && jsonArea[WimaArea::areaTypeName].isString()) {
                if ( jsonArea[WimaArea::areaTypeName] == WimaMeasurementArea::WimaMeasurementAreaName) {
                    bool success = _measurementArea.loadFromJson(jsonArea, errorString);

                    if ( !success ) {
                        qgcApp()->showMessage(errorMessage.arg(errorString));
                        return false;
                    }

                    _lastMeasurementAreaPath    = _measurementArea.path(); // prevents error messages at this point
                    validAreaCounter++;
                    _visualItems.append(&_measurementArea);
                    emit visualItemsChanged();
                } else if ( jsonArea[WimaArea::areaTypeName] == WimaServiceArea::wimaServiceAreaName) {
                    bool success = _serviceArea.loadFromJson(jsonArea, errorString);

                    if ( !success ) {
                        qgcApp()->showMessage(errorMessage.arg(errorString));
                        return false;
                    }

                    _lastServiceAreaPath        = _serviceArea.path(); // prevents error messages at this point
                    validAreaCounter++;
                    _visualItems.append(&_serviceArea);
                    emit visualItemsChanged();
                } else if ( jsonArea[WimaArea::areaTypeName] == WimaCorridor::WimaCorridorName) {
                    bool success = _corridor.loadFromJson(jsonArea, errorString);

                    if ( !success ) {
                        qgcApp()->showMessage(errorMessage.arg(errorString));
                        return false;
                    }

                    _lastCorridorPath           = _corridor.path(); // prevents error messages at this point
                    validAreaCounter++;
                    _visualItems.append(&_corridor);
                    emit visualItemsChanged();
                } else {
                    errorString += QString(tr("%s not supported.\n").arg(WimaArea::areaTypeName));
                    qgcApp()->showMessage(errorMessage.arg(errorString));
                    return false;
                }
            } else {
                errorString += QString(tr("Invalid or non existing entry for %s.\n").arg(WimaArea::areaTypeName));
                return false;
            }
        }

        _currentFile.sprintf("%s/%s.%s", fileInfo.path().toLocal8Bit().data(), fileInfo.completeBaseName().toLocal8Bit().data(), wimaFileExtension);

        emit currentFileChanged();

        QJsonObject missionObject = json[missionItemsName].toObject();

        //qWarning() << json[missionItemsName].type();

        QJsonDocument missionJsonDoc = QJsonDocument(missionObject);
        // create temporary file with missionItems
        QFile temporaryFile;
        QString cropedFileName = filename.section("/",0,-2);
        #if debug
            qWarning() << cropedFileName;
        #endif
        QString temporaryFileName;
        for (int i = 0; ; i++) {
            temporaryFileName = cropedFileName + QString("/temp%1.%2").arg(i).arg(AppSettings::planFileExtension);
            // qWarning() << temporaryFileName;

            if ( !QFile::exists(temporaryFileName) ) {
                temporaryFile.setFileName(temporaryFileName);
                if ( temporaryFile.open(QIODevice::WriteOnly | QIODevice::Text) ) {
                    break;
                }
            }

            if ( i > 1000) {
                qWarning("WimaPlaner::loadFromFile(): not able to create temporary file.");
                return false;
            }
        }

        // qWarning() << missionJsonDoc.toVariant().toString();
        temporaryFile.write(missionJsonDoc.toJson());
        temporaryFile.close();

        // load from temporary file
        _masterController->loadFromFile(temporaryFileName);

        QmlObjectListModel *missionItems = _missionController->visualItems();

        _circularSurvey = nullptr;
        for (int i = 0; i < missionItems->count(); i++) {
            _circularSurvey = missionItems->value<CircularSurveyComplexItem *>(i);
            if (_circularSurvey != nullptr) {

                _lastSurveyRefPoint = _circularSurvey->refPoint();
                _surveyRefChanging = false;
                _circularSurvey->setIsInitialized(true); // prevents reinitialisation from gui
                connect(_circularSurvey->deltaR(),               &Fact::rawValueChanged, this, &WimaPlaner::calcArrivalAndReturnPath);
                connect(_circularSurvey->deltaAlpha(),           &Fact::rawValueChanged, this, &WimaPlaner::calcArrivalAndReturnPath);
                connect(_circularSurvey->isSnakePath(),          &Fact::rawValueChanged, this, &WimaPlaner::calcArrivalAndReturnPath);
                connect(_circularSurvey->transectMinLength(),    &Fact::rawValueChanged, this, &WimaPlaner::calcArrivalAndReturnPath);
                break;
            }
        }

        //if (_circularSurvey == nullptr)        
        recalcJoinedArea();
        updateMission();

        // remove temporary file
        if ( !temporaryFile.remove() ){
            qWarning("WimaPlaner::loadFromFile(): not able to remove temporary file.");
        }

        setSyncronizedWithController(false);

        return true;

    } else if ( fileInfo.suffix() == AppSettings::planFileExtension ){
        _masterController->loadFromFile(filename);

        return true;// might be wrong return value
    } else {
        errorString += QString(tr("File extension not supported.\n"));
        qgcApp()->showMessage(errorMessage.arg(errorString));
        return false;
    }
}

void WimaPlaner::recalcPolygonInteractivity(int index)
{
    if (index >= 0 && index < _visualItems.count()) {
        resetAllInteractive();
        WimaArea* interactivePoly = qobject_cast<WimaArea*>(_visualItems.get(index));
        interactivePoly->setWimaAreaInteractive(true);
    }
}

bool WimaPlaner::calcArrivalAndReturnPath()
{
    setReadyForSync(false);

    // extract old survey data
    QmlObjectListModel          *missionItems   = _missionController->visualItems();

    int surveyIndex = missionItems->indexOf(_circularSurvey);

    if (surveyIndex == -1) {
        qWarning("WimaPlaner::calcArrivalAndReturnPath(): no survey item");
        return false;
    }

    bool restorePlanViewIndex = false;
    if (surveyIndex == _missionController->currentPlanViewIndex())
        restorePlanViewIndex = true;

    // remove old arrival and return path
    int size = missionItems->count();
    for (int i = surveyIndex+1; i < size; i++)
        _missionController->removeMissionItem(surveyIndex+1);
    for (int i = surveyIndex-1; i > 1; i--)
        _missionController->removeMissionItem(i);

    // set home position to serArea center
    MissionSettingsItem* settingsItem= qobject_cast<MissionSettingsItem*>(missionItems->get(0));
    if (settingsItem == nullptr){
        qWarning("WimaPlaner::calcArrivalAndReturnPath(): settingsItem == nullptr");
        return false;
    }

    // set altitudes, temporary measure to solve bugs
    QGeoCoordinate center = _serviceArea.center();
    center.setAltitude(0);
    _serviceArea.setCenter(center);
    center = _measurementArea.center();
    center.setAltitude(0);
    _measurementArea.setCenter(center);
    center = _corridor.center();
    center.setAltitude(0);
    _corridor.setCenter(center);
    // set HomePos. to serArea center
    settingsItem->setCoordinate(_serviceArea.center());

    // set takeoff position
    bool setCommandNeeded = false;
    if (missionItems->count() < 3) {
        setCommandNeeded = true;
        _missionController->insertSimpleMissionItem(_serviceArea.center(), 1);
    }
    SimpleMissionItem* takeOffItem = qobject_cast<SimpleMissionItem*>(missionItems->get(1));
    if (takeOffItem == nullptr){
        qWarning("WimaPlaner::calcArrivalAndReturnPath(): takeOffItem == nullptr");
        return false;
    }
    if (setCommandNeeded)
        _missionController->setTakeoffCommand(*takeOffItem);
    takeOffItem->setCoordinate(_serviceArea.center());

    if (_circularSurvey->visualTransectPoints().size() == 0) {
        qWarning("WimaPlaner::calcArrivalAndReturnPath(): survey no points.");
        return false;
    }

    // calculate path from take off to survey
    QGeoCoordinate start = _serviceArea.center();
    QGeoCoordinate end = _circularSurvey->coordinate();

    #ifdef QT_DEBUG
    //if (!_visualItems.contains(&_joinedArea))
        //_visualItems.append(&_joinedArea);
    #endif

    QList<QGeoCoordinate> path;
    if ( !calcShortestPath(start, end, path)) {
        qgcApp()->showMessage( QString(tr("Not able to calculate the path from takeoff position to measurement area.")).toLocal8Bit().data());
        return false;
    }
    _arrivalPathLength = path.size()-1;
    int sequenceNumber = 0;
    for (int i = 1; i < path.count()-1; i++) {
        sequenceNumber = _missionController->insertSimpleMissionItem(path.value(i), missionItems->count()-1);
        _missionController->setCurrentPlanViewIndex(sequenceNumber, true);
    }

    // calculate return path
    start   = _circularSurvey->exitCoordinate();
    end     = _serviceArea.center();
    path.clear();
    if ( !calcShortestPath(start, end, path)) {
        qgcApp()->showMessage(QString(tr("Not able to calculate the path from measurement area to landing position.")).toLocal8Bit().data());
        return false;
    }
    _returnPathLength = path.size()-1; // -1: fist item is last measurement point
    for (int i = 1; i < path.count()-1; i++) {
        sequenceNumber = _missionController->insertSimpleMissionItem(path.value(i), missionItems->count());
        _missionController->setCurrentPlanViewIndex(sequenceNumber, true);
    }

    // create land position item
    sequenceNumber = _missionController->insertSimpleMissionItem(_serviceArea.center(), missionItems->count());
    _missionController->setCurrentPlanViewIndex(sequenceNumber, true);
    SimpleMissionItem* landItem = qobject_cast<SimpleMissionItem*>(missionItems->get(missionItems->count()-1));
    if (landItem == nullptr){
        qWarning("WimaPlaner::calcArrivalAndReturnPath(): landItem == nullptr");
        return false;
    } else {
        if (!_missionController->setLandCommand(*landItem))
            return false;
    }

    if (restorePlanViewIndex)
        _missionController->setCurrentPlanViewIndex(missionItems->indexOf(_circularSurvey), false);
    setSyncronizedWithControllerFalse();
    setReadyForSync(true);
    return true;
}

bool WimaPlaner::recalcJoinedArea()
{
    setJoinedAreaValid(false);
    // check if at least service area and measurement area are available
    if (_visualItems.indexOf(&_serviceArea) == -1 || _visualItems.indexOf(&_measurementArea) == -1)
        return false;

    // check if area paths form simple polygons
    if ( !_serviceArea.isSimplePolygon() ) {
        qgcApp()->showMessage(tr("Service area is self intersecting and thus not a simple polygon. Only simple polygons allowed.\n"));
        return false;
    }

    if ( !_corridor.isSimplePolygon() && _corridor.count() > 0) {
        qgcApp()->showMessage(tr("Corridor is self intersecting and thus not a simple polygon. Only simple polygons allowed.\n"));
        return false;
    }

    if ( !_measurementArea.isSimplePolygon() ) {
        qgcApp()->showMessage(tr("Measurement area is self intersecting and thus not a simple polygon. Only simple polygons allowed.\n"));
        return false;
    }

    _joinedArea.setPath(_serviceArea.path());
    _joinedArea.join(_corridor);
    if ( !_joinedArea.join(_measurementArea) ) {
        qgcApp()->showMessage(tr("Not able to join areas. Service area and measurement"
                                 " must have a overlapping section, or be connected through a corridor."));
        return false; // this happens if all areas are pairwise disjoint
    }

    // join service area, op area and corridor
    setJoinedAreaValid(true);
    return true;
}

/*!
 * \fn void WimaPlaner::pushToContainer()
 * Pushes the \c WimaPlanData object generated by \c toPlanData() to the \c WimaDataContainer.
 * Should be called only after \c updateMission() was successful.
 *
 * \sa WimaDataContainer, WimaPlanData
 */
void WimaPlaner::pushToContainer()
{
    if (_container != nullptr) {
        if (!_readyForSync)
            return;
        WimaPlanData planData = toPlanData();
        _container->push(planData);
        setSyncronizedWithController(true);
    } else {
        qWarning("WimaPlaner::uploadToContainer(): no container assigned.");
    }
}

bool WimaPlaner::calcShortestPath(const QGeoCoordinate &start, const QGeoCoordinate &destination, QList<QGeoCoordinate> &path)
{
    using namespace GeoUtilities;
    using namespace PolygonCalculus;
    QList<QPointF> path2D;
    bool retVal = PolygonCalculus::shortestPath(
                                   toQPolygonF(toCartesian2D(_joinedArea.coordinateList(), /*origin*/ start)),
                                   /*start point*/ QPointF(0,0),
                                   /*destination*/ toCartesian2D(destination, start),
                                   /*shortest path*/ path2D);
    path.append(toGeo(path2D, /*origin*/ start));

    return  retVal;
}

void WimaPlaner::resetAllInteractive()
{
    // Marks all areas as inactive (area.interactive == false)
    int itemCount = _visualItems.count();
    if (itemCount > 0){
        for (int i = 0; i < itemCount; i++) {
            WimaArea* iteratorPoly = qobject_cast<WimaArea*>(_visualItems.get(i));
            iteratorPoly->setWimaAreaInteractive(false);
        }
    }
}

void WimaPlaner::setInteractive()
{
    recalcPolygonInteractivity(_currentAreaIndex);
}

/*!
 * \fn WimaPlanData WimaPlaner::toPlanData()
 *
 * Returns a \c WimaPlanData object containing information about the current mission.
 * The \c WimaPlanData object holds only the data which is relevant for the \c WimaController class.
 * Should only be called if updateMission() was successful.
 *
 * \sa WimaController, WimaPlanData
 */
WimaPlanData WimaPlaner::toPlanData()
{
    WimaPlanData planData;

    // store areas
    planData.append(WimaMeasurementAreaData(_measurementArea));
    planData.append(WimaServiceAreaData(_serviceArea));
    planData.append(WimaCorridorData(_corridor));
    planData.append(WimaJoinedAreaData(_joinedArea));

    // convert mission items to mavlink commands
    QObject deleteObject; // used to automatically delete content of rgMissionItems after this function call
    QList<MissionItem*> rgMissionItems;
    MissionController::convertToMissionItems(_missionController->visualItems(), rgMissionItems, &deleteObject);

    // store mavlink commands
    planData.append(rgMissionItems);

    return planData;
}

void WimaPlaner::setSyncronizedWithController(bool sync)
{
    if (_syncronizedWithController != sync) {
        _syncronizedWithController = sync;
        emit syncronizedWithControllerChanged();
    }
}

void WimaPlaner::setReadyForSync(bool ready)
{
    if( _readyForSync != ready) {
        _readyForSync = ready;

        emit readyForSyncChanged();
    }
}

void WimaPlaner::setJoinedAreaValid(bool valid)
{
    if (_joinedAreaValid != valid) {
        _joinedAreaValid = valid;

        emit joinedAreaValidChanged();
    }
}

void WimaPlaner::updateTimerSlot()
{
    // General operation of this function:
    // Check if parameter has changed, wait until it stops changing, update mission

    // circular survey reference point
    if (_missionController != nullptr
            && _missionController->visualItems()->indexOf(_circularSurvey) != -1
            && _circularSurvey != nullptr)
    {
        if (_surveyRefChanging) {
            if (_circularSurvey->refPoint() == _lastSurveyRefPoint) { // is it still changing?
                calcArrivalAndReturnPath();
                _surveyRefChanging = false;
            }
        } else {
            if (_circularSurvey->refPoint() != _lastSurveyRefPoint) // does it started changing?
                _surveyRefChanging = true;
        }
    }

    // measurementArea
    if (_measurementAreaChanging) {
        if (_measurementArea.path() == _lastMeasurementAreaPath) { // is it still changing?
            recalcJoinedArea();
            _measurementAreaChanging = false;
        }
    } else {
        if (_measurementArea.path() != _lastMeasurementAreaPath) // does it started changing?
            _measurementAreaChanging = true;
    }

    // corridor
    if (_corridorChanging) {
        if (_corridor.path() == _lastCorridorPath) { // is it still changing?
            recalcJoinedArea();
            _corridorChanging = false;
        }
    } else {
        if (_corridor.path() != _lastCorridorPath) // does it started changing?
            _corridorChanging = true;
    }

    // service area
    if (_serviceAreaChanging) {
        if (_serviceArea.path() == _lastServiceAreaPath) { // is it still changing?
            recalcJoinedArea();
            _serviceAreaChanging = false;
        }
    } else {
        if (_serviceArea.path() != _lastServiceAreaPath) // does it started changing?
            _serviceAreaChanging = true;
    }


    // update old values
    if (_missionController != nullptr
            && _missionController->visualItems()->indexOf(_circularSurvey) != -1
            && _circularSurvey != nullptr)
        _lastSurveyRefPoint = _circularSurvey->refPoint();

    _lastMeasurementAreaPath    = _measurementArea.path();
    _lastCorridorPath           = _corridor.path();
    _lastServiceAreaPath        = _serviceArea.path();
}

void WimaPlaner::setSyncronizedWithControllerFalse()
{
    setSyncronizedWithController(false);
}

void WimaPlaner::autoLoadMission()
{
    loadFromFile("/home/valentin/Desktop/drones/build-qgroundcontrol-Desktop_Qt_5_11_3_GCC_64bit-Release/release/345.wima");
    pushToContainer();
}

QJsonDocument WimaPlaner::saveToJson(FileType fileType)
{
    /// This function save all areas (of WimaPlaner) and all mission items (of MissionController) to a QJsonDocument
    /// @param fileType is either WimaFile or PlanFile (enum), if fileType == PlanFile only mission items are stored
    QJsonObject json;

    if ( fileType == FileType::WimaFile ) {
        QJsonArray jsonArray;

        for (int i = 0; i < _visualItems.count(); i++) {
            QJsonObject json;

            WimaArea* area = qobject_cast<WimaArea*>(_visualItems.get(i));

            if (area == nullptr) {
                qWarning("WimaPlaner::saveToJson(): Internal error, area == nullptr!");
                return QJsonDocument();
            }

            // check the type of area, create and append the JsonObject to the JsonArray once determined
            WimaMeasurementArea* opArea =  qobject_cast<WimaMeasurementArea*>(area);
            if (opArea != nullptr) {
                opArea->saveToJson(json);
                jsonArray.append(json);
                continue;
            }

            WimaServiceArea* serArea =  qobject_cast<WimaServiceArea*>(area);
            if (serArea != nullptr) {
                serArea->saveToJson(json);
                jsonArray.append(json);
                continue;
            }

            WimaCorridor* corridor =  qobject_cast<WimaCorridor*>(area);
            if (corridor != nullptr) {
                corridor->saveToJson(json);
                jsonArray.append(json);
                continue;
            }

            // if non of the obove branches was trigger, type must be WimaArea
            area->saveToJson(json);
            jsonArray.append(json);
        }

        json[areaItemsName] = jsonArray;
        json[missionItemsName] = _masterController->saveToJson().object();

        return QJsonDocument(json);
    } else if (fileType == FileType::PlanFile) {
        return _masterController->saveToJson();
    }

    return QJsonDocument(json);
}



