export type Plan = {
    name: string,
    id: string,
    steps: PlanElement[],
}

export type PlanElement = Goal | Action
export type PlanElementTypes = ActionTypes | "goal"

export type Goal = {
    id: string,
    name: string,
    plan: Plan,
    can_fail: boolean,
    type: "goal"
}

// Actions
export type Action = SpeechAction | GestureAction | LLMAction
export type SpeechAction = { id: string, text: string, type:"speech" }
export type GestureAction = { id: string, gesture: string, isTag: boolean, type:"gesture" }
export type LLMAction = { id: string, goal: string, type:"llm" }

export type ActionTypes = "speech" | "gesture" | "llm"


// Extract the wanted information from a plan within the reducing of nodes
export function PlanReduce(plan?: Plan) {
    if (!plan) return ""
    return {
        name: plan.name,
        id: plan.id,
        steps: plan.steps.map((x) => StepReduce(x))
    }
}


// Extract the wanted information from a plan element.
function StepReduce(planElement: PlanElement) {
    // We have different types of plan elements, requiring differnt types of output
    switch (planElement.type) {
        case ("speech"):
            return {
                id: planElement.id,
                text: planElement.text,
            }
        case ("gesture"):
            return {
                id: planElement.id,
                gesture: {
                    type: planElement.isTag ? "tag" : "single",
                    name: planElement.gesture 
                },
            }
        case ("llm"):
            return {
                id: planElement.id,
                goal: planElement.goal,
            }
        case ("goal"):
            return {
                id: planElement.id,
                plan: planElement.plan,
                can_fail: planElement.can_fail,
            };
        default:
    }
}

/**
 * Finds out whether the plan can iterate multiple times, or always stops after one action.
 * This comes down to checking if the plan only has speech/ gesture actions, or others as well.
 * @param plan: the plan to check
 * @returns: a boolean 
 */
export function DoesPlanIterate(plan?: Plan) : boolean {
    // TODO: should recursively check plans that have goals (and thus more plans) in them.
    if (!plan) return false
    return plan.steps.filter((step) => step.type == "llm").length > 0 || (plan.steps.filter((x) => x.type == "goal").map((x) => DoesPlanIterate(x.plan))).includes(true);
}

/**
 * Returns the value of the action.
 * Since typescript can't polymorphicly access the value field,
 * we need to switch over the types and return the correct field.
 * @param action: action to retrieve the value from 
 * @returns string | undefined
 */
export function GetActionValue(action: Action) {
    let returnAction;
    switch (action.type) {
        case "gesture":
            returnAction = action as GestureAction
            return returnAction.gesture;
        case "speech":
            returnAction = action as SpeechAction
            return returnAction.text;
        case "llm":
            returnAction = action as LLMAction
            return returnAction.goal;
        default:
    }
}