#ifndef BLANK_AI_AICONTROLLER_HPP_
 #define BLANK_AI_AICONTROLLER_HPP_
 
+#include "../app/IntervalTimer.hpp"
 #include "../world/EntityController.hpp"
 
 #include <glm/glm.hpp>
 
        static glm::vec3 Heading(const EntityState &) noexcept;
 
+       /// schedule a decision in the next minimum ± variance seconds
+       void CueDecision(
+               float minimum,
+               float variance
+       ) noexcept;
+       /// check if the scheduled decision is due already
+       bool DecisionDue() const noexcept;
+       /// random choice of 0 to num_choices - 1
+       unsigned int Decide(unsigned int num_choices) noexcept;
+
+       void EnterHalt(float speed) noexcept;
+       void ExitHalt() noexcept;
+       bool IsHalted() const noexcept;
+
        void StartFleeing(const Entity &, float speed) noexcept;
        void StopFleeing() noexcept;
        bool IsFleeing() const noexcept;
                float displacement = 1.0f
        ) noexcept;
        void StopWandering() noexcept;
+       bool IsWandering() const noexcept;
 
 private:
        GaloisLFSR &random;
        const AIState *state;
 
+       FineTimer decision_timer;
+
+       bool halted;
+       float halt_speed;
+
        const Entity *flee_target;
        float flee_speed;
 
 
 #include "AIController.hpp"
 #include "IdleState.hpp"
+#include "RoamState.hpp"
 
 #include "../model/geometry.hpp"
 #include "../rand/GaloisLFSR.hpp"
 namespace {
 
 IdleState idle;
+RoamState roam;
 
 }
 
 AIController::AIController(GaloisLFSR &rand)
 : random(rand)
 , state(&idle)
+, decision_timer(1.0f)
+, halted(false)
+, halt_speed(1.0f)
 , flee_target(nullptr)
 , flee_speed(5.0f)
 , seek_target(nullptr)
 }
 
 void AIController::Update(Entity &e, float dt) {
-       // movement: for now, wander only
+       decision_timer.Update(dt);
+       state->Update(*this, e, dt);
+
        if (wandering) {
                glm::vec3 displacement(
                        random.SNorm() * wander_disp,
 }
 
 glm::vec3 AIController::ControlForce(const Entity &entity, const EntityState &state) const {
+       if (IsHalted()) {
+               return Halt(state, halt_speed);
+       }
        glm::vec3 force(0.0f);
        if (IsFleeing()) {
                glm::vec3 diff(GetFleeTarget().GetState().Diff(state));
                        return force;
                }
        }
-       if (wandering) {
+       if (IsWandering()) {
                glm::vec3 wander_target(normalize(Heading(state) * wander_dist + wander_pos) * wander_speed);
                if (MaxOutForce(force, TargetVelocity(wander_target, state, 2.0f), entity.MaxControlForce())) {
                        return force;
        }
 }
 
+void AIController::CueDecision(
+       float minimum,
+       float variance
+) noexcept {
+       decision_timer = FineTimer(minimum + variance * random.SNorm());
+       decision_timer.Start();
+}
+
+bool AIController::DecisionDue() const noexcept {
+       return decision_timer.HitOnce();
+}
+
+unsigned int AIController::Decide(unsigned int num_choices) noexcept {
+       return random.Next<unsigned int>() % num_choices;
+}
+
+void AIController::EnterHalt(float speed) noexcept {
+       halted = true;
+       halt_speed = speed;
+}
+
+void AIController::ExitHalt() noexcept {
+       halted = false;
+}
+
+bool AIController::IsHalted() const noexcept {
+       return halted;
+}
+
 void AIController::StartFleeing(const Entity &e, float speed) noexcept {
        flee_target = &e;
        flee_speed = speed;
        wander_radius = radius;
        wander_disp = displacement;
 }
+
 void AIController::StopWandering() noexcept {
        wandering = false;
 }
 
+bool AIController::IsWandering() const noexcept {
+       return wandering;
+}
+
 
 void IdleState::Enter(AIController &ctrl) const {
-       ctrl.StartWandering(1.0f);
+       ctrl.EnterHalt(2.0f);
+       ctrl.StartWandering(0.001f, 1.1f);
+       ctrl.CueDecision(10.0f, 5.0f);
 }
 
 void IdleState::Update(AIController &ctrl, Entity &e, float dt) const {
+       // TODO: check for players
+       if (!ctrl.DecisionDue()) return;
+
+       unsigned int d = ctrl.Decide(10);
+       if (d == 0) {
+               // .1 chance to start going
+               ctrl.SetState(roam);
+       } else if (d < 3) {
+               // .2 chance of looking around
+               ctrl.ExitHalt();
+       } else {
+               ctrl.EnterHalt(2.0f);
+       }
+       ctrl.CueDecision(10.0f, 5.0f);
 }
 
 void IdleState::Exit(AIController &ctrl) const {
+       ctrl.ExitHalt();
+       ctrl.StopWandering();
+}
+
+void RoamState::Enter(AIController &ctrl) const {
+       ctrl.StartWandering(1.0f);
+       ctrl.CueDecision(10.0f, 5.0f);
+}
+
+void RoamState::Update(AIController &ctrl, Entity &e, float dt) const {
+       // TODO: check for players
+       if (!ctrl.DecisionDue()) return;
+
+       unsigned int d = ctrl.Decide(10);
+       if (d == 0) {
+               // .1 chance of idling
+               ctrl.SetState(idle);
+       }
+       ctrl.CueDecision(10.0f, 5.0f);
+}
+
+void RoamState::Exit(AIController &ctrl) const {
        ctrl.StopWandering();
 }