import Mission from "../../data/mission_set/mission";
import { jaiaGlobal } from "../../data/jaia_global/jaia-global";
import { missionSet } from "../../data/mission_set/mission-set";
import { missionsManager } from "../../data/missions_manager/missions-manager";
import { taskPackets } from "../../data/task_packets/task-packets";
import { GridPlanningStates } from "../../data/survey_planner/grid-plan";
import { gridLayer } from "../../openlayers/layers/vector/grid-layer";
import { missionLayer } from "../../openlayers/layers/vector/mission-layer";
import { handleMapModeChange } from "../../openlayers/maps/map";
import { NodeTypes } from "../../types/jaia-system-types";
import {
    JaiaContextType,
    JaiaAction,
    ButtonNames,
    TaskPacketVisibility,
} from "../../types/context-types";
import { MapModes } from "../../types/openlayers-types";
import { jaiaAPI } from "../../utils/jaia-api";
import { MAX_WAYPOINTS, UNASSIGNED_ID } from "../../utils/constants";
import { syncTaskLayers } from "./handler-utils";

/**
 * Makes call to add waypoint if mission is in edit mode
 *
 * @param {JaiaContextType} mutableState State object ref for making modifications
 * @param {JaiaAction} action including location with Lat/lon of the click
 * @returns {JaiaContextType} Updated mutable state object
 */
export function handleAddWaypoint(mutableState: JaiaContextType, action: JaiaAction) {
    const missionIDInEditMode = missionSet.getMissionIDInEditMode();
    const selectedNode = jaiaGlobal.getSelectedNode();

    if (
        selectedNode.type === NodeTypes.BOT &&
        missionsManager.getMissionID(selectedNode.id) === UNASSIGNED_ID
    ) {
        // Create new mission and add first waypoint for selected Bot without mission
        const newMission = new Mission();
        const newMissionID = missionSet.addMission(newMission);
        newMission.addWaypoint(action.location);
        missionsManager.assign(selectedNode.id, newMissionID);

        mutableState.missionAccordionStates[newMissionID] = true;
    } else if (missionIDInEditMode !== UNASSIGNED_ID) {
        // Add waypoint to mission in edit mode
        const mission = missionSet.getMission(missionIDInEditMode);
        if (mission.getWaypoints().length < MAX_WAYPOINTS) {
            mission.addWaypoint(action.location);
        }
    }

    missionLayer.updateFeatures();

    return mutableState;
}

/**
 * Makes call to remove a waypoint from a mission
 *
 * @param {JaiaContextType} mutableState State object ref for making modifications
 * @returns {JaiaContextType} Updated mutable state object
 */
export function handleDeleteWaypoint(mutableState: JaiaContextType) {
    const mission = missionSet.getMission(jaiaGlobal.getSelectedWaypoint().missionID);
    mission.deleteWaypoint(jaiaGlobal.getSelectedWaypoint().waypointNum);
    jaiaGlobal.setSelectedWaypoint({
        waypointNum: UNASSIGNED_ID,
        missionID: UNASSIGNED_ID,
        isMoveable: false,
    });

    mutableState.visiblePanel = ButtonNames.NONE;

    missionLayer.updateFeatures();

    return mutableState;
}

/**
 * Makes the calls to move a waypoint to a user set location
 *
 * @param {JaiaContextType} mutableState State object ref for making modifications
 * @param {JaiaAction} action including location New location of the waypoint
 * @returns {JaiaContextType} Updated mutable state object
 */
export function handleMoveWaypoint(mutableState: JaiaContextType, action: JaiaAction) {
    const mission = missionSet.getMission(jaiaGlobal.getSelectedWaypoint().missionID);
    mission.moveWaypoint(
        mutableState.jaiaGlobal.getSelectedWaypoint().waypointNum,
        action.location,
    );

    missionLayer.updateFeatures();

    return mutableState;
}

/**
 * Updates the task associated with a waypoint based on the operator's selection
 *
 * @param {JaiaContextType} mutableState State object ref for making modifications
 * @param {JaiaAction} action including taskType Name of the task selected
 * @returns {JaiaContextType} Updated mutable state object
 */
export function handleSelectTask(mutableState: JaiaContextType, action: JaiaAction) {
    if (action.task) {
        action.task.setType(action.taskType);
    }

    missionLayer.updateFeatures();

    return mutableState;
}

/**
 * Makes call to update the parameters of a task based on user input
 *
 * @param {JaiaContextType} mutableState State object ref for making modifications
 * @param {JaiaAction} action including taskParameterPair with name of input and its value
 * @returns {JaiaContextType} Updated mutable state object
 */
export function handleChangeTaskParameter(mutableState: JaiaContextType, action: JaiaAction) {
    for (const taskParameterPair of action.taskParameterPairs) {
        action.task.setParameter(taskParameterPair);
    }

    if (action.task.getIsSurveyTask()) {
        gridLayer.finalizeGrid();
    } else {
        missionLayer.updateFeatures();
    }

    return mutableState;
}

/**
 * Makes call to update the dive parameters based on the toggle state
 *
 * @param {JaiaContextType} mutableState State object ref for making modifications
 * @returns {JaiaContextType} Updated mutable state object
 */
export function handleToggleBottomDive(mutableState: JaiaContextType, action: JaiaAction) {
    action.task.setIsBottomDive(!action.task.getIsBottomDive());
    return mutableState;
}

/**
 * Makes call to update the useHydrophone property based on the toggle state
 *
 * @param {JaiaContextType} mutableState State object ref for making modifications
 * @param {JaiaAction} action Provides access to the task
 * @returns {JaiaContextType} Updated mutable state object
 */
export function handleToggleHydrophone(mutableState: JaiaContextType, action: JaiaAction) {
    action.task.setUseHydrophone(!action.task.getUseHydrophone());
    missionLayer.updateFeatures();
    // Refresh the grid to show icon updates
    if (
        jaiaGlobal.getMapMode() === MapModes.SURVEY_PLANNING &&
        mutableState.gridPlan.getState() !== GridPlanningStates.ACCEPTING_START_TASK
    ) {
        gridLayer.finalizeGrid();
    }
    return mutableState;
}

/**
 * Updates the map mode when the constant heading select on map toggle is clicked
 *
 * @param {JaiaContextType} mutableState State object ref for making modifications
 * @param {JaiaAction} action Provides access to the task
 * @returns {JaiaContextType} Updated mutable state object
 */
export function handleToggleConstantHeadingSelect(
    mutableState: JaiaContextType,
    action: JaiaAction,
) {
    const currentMapMode = jaiaGlobal.getMapMode();
    let updatedMapMode = MapModes.DEFAULT;

    if (action.task.getIsSurveyTask()) {
        if (currentMapMode !== MapModes.SURVEY_CONSTANT_HEADING_SELECT) {
            updatedMapMode = MapModes.SURVEY_CONSTANT_HEADING_SELECT;
        } else {
            updatedMapMode = MapModes.SURVEY_PLANNING;
        }
    }

    if (!action.task.getIsSurveyTask() && currentMapMode !== MapModes.CONSTANT_HEADING_SELECT) {
        updatedMapMode = MapModes.CONSTANT_HEADING_SELECT;
        jaiaGlobal.getSelectedWaypoint().isMoveable = false;
    }

    handleMapModeChange(updatedMapMode);
    mutableState.jaiaGlobal.setMapMode(updatedMapMode);
    return mutableState;
}

/**
 * Makes api calls to exclude + include task packets
 *
 * @param {JaiaContextType} mutableState State object ref for making modifications
 * @returns {JaiaContextType} Updated mutable state object
 */
export function handleChangeTaskPacketVisibility(
    mutableState: JaiaContextType,
    action: JaiaAction,
) {
    const include = action.taskPacketVisibility === TaskPacketVisibility.INCLUDE;
    jaiaAPI.postTaskPacketInclude(action.taskPacketID, include).then((response) => {
        jaiaAPI.getTaskPackets().then((response) => {
            taskPackets.setIncludedTaskPackets(response.result.included);
            taskPackets.setExcludedTaskPackets(response.result.excluded);
            syncTaskLayers();
        });
    });
    return mutableState;
}
