module Checklist
exports all
definitions

values
    -- Before Start Checklist
    -- Items in Aircraft
    -- Flight Deck... (can't check)
    fuel: Item = mk_Item("Fuek Pump", <SWITCH>, mk_Switch(<OFF>, false));
    pax_sign: Item = mk_Item("Passenger Signs", <SWITCH>, mk_Switch(<OFF>, true));
    windows: Item = mk_Item("Windows", <SWITCH>, mk_Switch(<ON>, false));
    -- Preflight steps
    acol: Item = mk_Item("Anti Collision Lights", <SWITCH>, mk_Switch(<OFF>, false));

    aircraft: Aircraft = [fuel, pax_sign, windows, acol];

    -- Checklist
    -- Flight Deck... (can't check)
    fuel_chkl: ChecklistItem = mk_ChecklistItem(fuel.name, <SWITCH>, <ON>, false);
    pax_sign_chkl: ChecklistItem = mk_ChecklistItem(pax_sign.name, <SWITCH>, <ON>, false);
    windows_chkl: ChecklistItem = mk_ChecklistItem(windows.name, <SWITCH>, <ON>, false);
    -- Preflight steps
    acol_chkl: ChecklistItem = mk_ChecklistItem(acol.name, <SWITCH>, <ON>, false);

    before_start_procedure: Procedure = [fuel_chkl, pax_sign_chkl, windows_chkl, acol_chkl];
types
    String = seq of char;

    -- Aircraft

    -- Switches
    --@doc The state a switch can be in
    -- 1 means off
    SwitchState = <OFF> | <MIDDLE> | <ON>;

    --@doc A switch, with the possible states it can be in, and the state that it is in
    Switch :: 
                position : SwitchState
                middlePosition : bool
                inv s == 
                    if s.middlePosition = false then 
                        s.position <> <MIDDLE>
                    else true;

    -- Knob
    Knob :: 
            position : nat1
            states : seq of int
            inv k == k.position <= len k.states;

    Lever = nat
        inv t == t <= 100;

    Throttle :: 
                thrust: Lever
                reverser: Lever
                inv t ==
                    if t.thrust > 0 then
                        t.reverser = 0
                    else 
                        t.reverser >= 0;

    --@doc The type that the action of the button is
    ItemType = <SWITCH> | <KNOB> | <BUTTON>;

    --@doc Item of a checklist, e.g. Landing gear down
    Item ::
            name : String
            type : ItemType
            object : Switch | Knob | Throttle;

    --@doc Aircraft contains all the controls
    Aircraft = seq of Item;

    -- Checklist

    --@doc Item of a checklist, e.g. Landing gear down
    ChecklistItem ::
                    procedure : String
                    type : ItemType
                    check : SwitchState
                    checked : bool;

    --@doc A section of a checklist, e.g. Landing Checklist
    Procedure = seq of ChecklistItem
        inv p == 
            false not in set { 
                let first = p(x-1).checked, second = p(x).checked in 
                    (first = second) or ((first = true) and (second = false))
                | x in set {2,...,len p}};
    
    --@doc Full checklist, e.g. Startup, Descent, Landing Checklist
    Checklist = seq of Procedure;

functions
    --@doc Finds the index of the next item in the procedure that needs to be completed
    procedure_next_index: Procedure -> nat1
    procedure_next_index(p) ==
        hd [ x | x in set {1,...,len p} & p(x).checked = false]
    pre
        -- Checks to prevent getting the head of an empty sequence
        len p > 0
        and procedure_completed(p) = false
    post
        -- Checks that the index of the item is the next one to be completed
        p(RESULT).checked = false
        and if RESULT > 1 then 
            p(RESULT-1).checked = true
        else 
            true;

    --@doc Checks if the procedure has been completed
    procedure_completed: Procedure -> bool
    procedure_completed(p) ==
        false not in set { p(x).checked | x in set {1,...,len p} };

    --@doc Find index of an item in Aircraft
    index_of_item: String * Aircraft -> nat1
    index_of_item(i, a) ==
        if (hd a).name = i then 1
        else 
            index_of_item(i, tl a) + 1
        pre
            len a > 0
        post
            -- Checks that the index is correct
            --TODO this does not check if it is the first item in the list
            if RESULT > 1 then
                a(RESULT).name = i
            else
                true
        measure len a;

end Checklist
