/// @file eject_filament.cpp
#include "eject_filament.h"
#include "../modules/buttons.h"
#include "../modules/finda.h"
#include "../modules/globals.h"
#include "../modules/idler.h"
#include "../modules/leds.h"
#include "../modules/motion.h"
#include "../modules/permanent_storage.h"
#include "../modules/pulley.h"
#include "../modules/selector.h"
#include "../debug.h"

namespace logic {

EjectFilament ejectFilament;

bool EjectFilament::Reset(uint8_t param) {
    if (!CheckToolIndex(param)) {
        return false;
    }

    error = ErrorCode::RUNNING;
    slot = param;

    if (mg::globals.FilamentLoaded() == mg::FilamentLoadState::NotLoaded) {
        FinishedOK();
        dbg_logic_P(PSTR("Already ejected"));
    } else if (mg::globals.FilamentLoaded() >= mg::FilamentLoadState::AtPulley) {
        state = ProgressCode::UnloadingFilament;
        unl.Reset(param); //@@TODO probably act on active extruder only
    } else {
        MoveSelectorAside();
    }
    return true;
}

void EjectFilament::MoveSelectorAside() {
    state = ProgressCode::ParkingSelector;
    const uint8_t selectorParkedPos = (slot <= 2) ? 4 : 0;
    mi::idler.Engage(slot);
    ms::selector.MoveToSlot(selectorParkedPos);
}

bool EjectFilament::StepInner() {
    switch (state) {
    case ProgressCode::UnloadingFilament:
        if (unl.StepInner()) {
            // unloading sequence finished - basically, no errors can occur here
            // as UnloadFilament should handle all the possible error states on its own
            // There is no way the UnloadFilament to finish in an error state
            MoveSelectorAside();
        }
        break;
    case ProgressCode::ParkingSelector:
        if (mm::motion.QueueEmpty()) { // selector parked aside
            state = ProgressCode::EjectingFilament;
            mpu::pulley.InitAxis();
            mpu::pulley.PlanMove(-config::filamentMinLoadedToMMU, config::pulleySlowFeedrate);
        }
        break;
    case ProgressCode::EjectingFilament:
        if (mm::motion.QueueEmpty()) { // filament ejected
            state = ProgressCode::DisengagingIdler;
            mi::idler.Disengage();
        }
        break;
    case ProgressCode::DisengagingIdler:
        if (mi::idler.Disengaged()) { // idler disengaged
            mpu::pulley.Disable();
            mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::NotLoaded);
            FinishedOK();
        }
        break;
    case ProgressCode::OK:
        dbg_logic_fP(PSTR("FilamentLoadState after Eject %d"), mg::globals.FilamentLoaded());
        return true;
    default: // we got into an unhandled state, better report it
        state = ProgressCode::ERRInternal;
        error = ErrorCode::INTERNAL;
        return true;
    }
    return false;
}

ProgressCode EjectFilament::State() const {
    switch (state) {
    case ProgressCode::UnloadingFilament:
        return unl.State(); // report sub-automaton states properly
    default:
        return state;
    }
}

ErrorCode EjectFilament::Error() const {
    switch (state) {
    case ProgressCode::UnloadingFilament:
        return unl.Error(); // report sub-automaton errors properly
    default:
        return error;
    }
}

} // namespace logic
