#ifndef __MOTION_DETECT_HPP__
#define __MOTION_DETECT_HPP__

#pragma onece

#include <typeinfo>
#include <sml.hpp>
#include <map>
#include <string>

namespace sml = boost::sml;

namespace md{
    /// types
    struct none{};
    struct motion{};
    struct people{};
    struct mpboth{};
    /// consts
    const std::map<const std::string, int> kNumIn2Post = {{"n", 3}, {"m", 6}, {"p", 10}};
    const std::map<const std::string, int> kNumPost2None = {{"n", 2}, {"m", 4}, {"p", 8}};
    const string kKeyM= "m";
    const string kKeyN= "n";
    const string kKeyP= "p";

    /// helpers
    auto event_inspection(auto e, std::map<std::string,int> &m){
        if(std::is_same<decltype(e), people>::value){
            spdlog::info("type is people: {}",  ++m[kKeyP]);
        }else if(std::is_same<decltype(e), motion>::value){
            spdlog::info("type is motion: {}",  ++m[kKeyM]);
        }else if(std::is_same<decltype(e), none>::value){
            spdlog::info("type is none: {}",  ++m[kKeyN]);
        }
    }

    auto make_guard_fn(const std::map<std::string,int> &konst){
        return [&konst](auto e){
            static std::map<std::string, int>guards = {{"m", 0}, {"p", 0}, {"n", 0}};
            event_inspection(e, guards);
            if(guards[kKeyM] != 0 && guards[kKeyP] != 0){
                guards[kKeyM] = guards[kKeyP] = guards[kKeyN] = 0;
                return false;
            }else if(guards[kKeyP] >= konst[kKeyP]){
                guards[kKeyP]=guards[kKeyN]= 0;
                return true;
            }else if(guards[kKeyM] >= konst[kKeyM]){
                guards[kKeyM]=guards[kKeyN] = 0;
                return true;
            }else if(guards[kKeyN] >= konst[kKeyN]){
                guards[kKeyN] = 0;
                return true;
            }
            return false;
        };
    }

    struct fsm {
        auto operator()()const noexcept {
            using namespace sml;
            const auto guard_in2post = make_guard_fn(kNumIn2Post);
            const auto guard_post2none = make_guard_fn(kNumPost2None);
            return make_transition_table(
                *"init"_s = "none"_s,
                "none"_s + event<none>/[]{spdlog::info("none -> none");} = "none"_s,
                "none"_s + event<motion>/[]{spdlog::info("none -m-> pre");} = "pre"_s,
                "none"_s + event<people>/[]{spdlog::info("none -p-> pre");} = "pre"_s,
                "none"_s + event<mpboth>/[]{spdlog::info("none -b-> pre");} = "in"_s,
                // pre
                "pre"_s + event<none>/[]{spdlog::info("pre -n-> none");} = "none"_s,
                "pre"_s + event<motion>/[]{spdlog::info("pre -m-> pre");} = "pre"_s,
                "pre"_s + event<people>/[]{spdlog::info("pre -p-> in");} = "in"_s,
                "pre"_s + event<mpboth>/[]{spdlog::info("pre -b-> in");} = "in"_s,
                // in
                "in"_s + event<none> [guard_in2post]/[]{spdlog::info("in -n-> post");} = "post"_s,
                "in"_s + event<motion> [guard_in2post]/[]{spdlog::info("in -m-> post");} = "post"_s,
                "in"_s + event<people> [guard_in2post]/[]{spdlog::info("in -p-> post");} = "post"_s,
                "in"_s + event<mpboth>/[]{spdlog::info("in -b-> in");} = "in"_s,
                // post
                "post"_s + event<mpboth>/[]{spdlog::info("post -b-> in");} = "in"_s,
                "post"_s + event<people> [guard_post2none]/[]{spdlog::info("post -p-> none");} = "none"_s,
                "post"_s + event<motion> [guard_post2none]/[]{spdlog::info("post -m-> none");} = "none"_s,
                "post"_s + event<none> [guard_post2none]/[]{spdlog::info("post -n-> none");} = "none"_s
            );
        }
    };
}



#endif