<commit_before>#pragma once

#include <boost/circular_buffer.hpp>
#include <polar/support/action/binding.h>
#include <polar/support/action/lifetime.h>
#include <polar/system/base.h>

namespace polar::system {
	class action : public system::base {
	public:
		using digital            = support::action::digital;
		using analog             = support::action::analog;
		using digital_function_t = support::action::digital_function_t;
		using analog_function_t  = support::action::analog_function_t;
		using analog_predicate_t = support::action::analog_predicate_t;
	private:
		using binding      = support::action::binding;
		using lifetime     = support::action::lifetime;
		using digital_data = support::action::digital_data;
		using analog_state = support::action::analog_state;
		using analog_data  = support::action::analog_data;
		using digital_map  = support::action::digital_map;
		using analog_map   = support::action::analog_map;

		const int fps = 50;
		const DeltaTicks timestep = DeltaTicks(ENGINE_TICKS_PER_SECOND / fps);
		DeltaTicks accumulator;

		struct frame_action {
			IDType objectID;
			std::type_index ti;
			lifetime lt;

			frame_action(IDType objectID, std::type_index ti, lifetime lt) : objectID(objectID), ti(ti), lt(lt) {}
		};

		struct frame {
			std::vector<frame_action> actions;
		};

		boost::circular_buffer<frame> framebuffer = boost::circular_buffer<frame>(100);

		digital_map digitals;
		analog_map analogs;

		std::array<binding::bimap, size_t(lifetime::SIZE)> lt_bindings;
		binding::bimap bindings;

		IDType nextID = 1;
	public:
		static bool supported() { return true; }
		action(core::polar *engine) : base(engine) {}

		inline auto get_framebuffer() const { return framebuffer; }

		inline void force_tick() { tick(); }

		void update(DeltaTicks &dt) override {
			accumulator += dt;
			if(accumulator.Seconds() > 1.0f) { accumulator.SetSeconds(1.0f); }

			while(accumulator >= timestep) {
				tick();
				accumulator -= timestep;
			}
		}

		void tick() {
			for(auto &pair : digitals) {
				for(auto &state : pair.second.states) {
					trigger_digital<false>(state.first, pair.first,
					                       state.second ? lifetime::when
					                                    : lifetime::unless);
				}
			}

			for(auto &pair : analogs) {
				for(auto &state : pair.second.states) {
					trigger_analog(state.first, pair.first);

					/* reset value immediately after triggering
					 * this allows the value to accumulate across the entire frame
					 */
					state.second.previous = state.second.value;
					state.second.value = state.second.initial;
				}
			}

			framebuffer.push_back();
		}

		void apply_frame(frame f) {
			// save accumulated analog values before applying frame
			for(auto &pair : analogs) {
				for(auto &state : pair.second.states) {
					state.second.saved = state.second.value;
				}
			}

			for(auto &a : f.actions) {
				trigger_digital(a.objectID, a.ti, a.lt);
			}

			// XXX: this will still trigger `when` actions active before the revert
			force_tick();

			// load accumulated analog values again
			for(auto &pair : analogs) {
				for(auto &state : pair.second.states) {
					state.second.value = state.second.saved;
				}
			}
		}

		void reg_digital(IDType objectID, std::type_index ti, bool state = false) {
			auto it = digitals.find(ti);
			if(it == digitals.end()) {
				digitals.emplace(ti, digital_data{});
			}
			digitals[ti].states.emplace(objectID, state);
		}

		void reg_analog(IDType objectID, std::type_index ti, Decimal initial = 0) {
			auto it = analogs.find(ti);
			if(it == analogs.end()) {
				analogs.emplace(ti, analog_data{});
			}
			analogs[ti].states.emplace(objectID, analog_state{initial, initial, initial});
		}

		template<typename T>
		typename std::enable_if<std::is_base_of<digital, T>::value>::type
		reg_digital(IDType objectID, bool state = false) { reg_digital(objectID, typeid(T), state); }

		template<typename T>
		typename std::enable_if<std::is_base_of<analog, T>::value>::type
		reg_analog(IDType objectID, Decimal initial = 0) { reg_analog(objectID, typeid(T), initial); }

		// digital -> digital function
		template<typename Src,
		         typename = typename std::enable_if<<commit_msg>Fix action replay issues<commit_after>#pragma once

#include <boost/circular_buffer.hpp>
#include <polar/support/action/binding.h>
#include <polar/support/action/lifetime.h>
#include <polar/system/base.h>

namespace polar::system {
	class action : public system::base {
	public:
		using digital            = support::action::digital;
		using analog             = support::action::analog;
		using digital_function_t = support::action::digital_function_t;
		using analog_function_t  = support::action::analog_function_t;
		using analog_predicate_t = support::action::analog_predicate_t;
	private:
		using binding      = support::action::binding;
		using lifetime     = support::action::lifetime;
		using digital_data = support::action::digital_data;
		using analog_state = support::action::analog_state;
		using analog_data  = support::action::analog_data;
		using digital_map  = support::action::digital_map;
		using analog_map   = support::action::analog_map;

		const int fps = 50;
		const DeltaTicks timestep = DeltaTicks(ENGINE_TICKS_PER_SECOND / fps);
		DeltaTicks accumulator;

		struct frame_action {
			IDType objectID;
			std::type_index ti;
			lifetime lt;

			frame_action(IDType objectID, std::type_index ti, lifetime lt) : objectID(objectID), ti(ti), lt(lt) {}
		};

		struct frame {
			digital_map digitals;
			std::vector<frame_action> actions;
		};

		boost::circular_buffer<frame> framebuffer = boost::circular_buffer<frame>(100);

		digital_map digitals;
		analog_map analogs;

		std::array<binding::bimap, size_t(lifetime::SIZE)> lt_bindings;
		binding::bimap bindings;

		IDType nextID = 1;
	public:
		static bool supported() { return true; }
		action(core::polar *engine) : base(engine) {}

		inline auto get_framebuffer() const { return framebuffer; }

		inline void force_tick() { tick(); }

		void update(DeltaTicks &dt) override {
			accumulator += dt;
			if(accumulator.Seconds() > 1.0f) { accumulator.SetSeconds(1.0f); }

			while(accumulator >= timestep) {
				tick();
				accumulator -= timestep;
			}
		}

		void tick() {
			for(auto &pair : digitals) {
				for(auto &state : pair.second.states) {
					trigger_digital<false>(state.first, pair.first,
					                       state.second ? lifetime::when
					                                    : lifetime::unless);
				}
			}

			for(auto &pair : analogs) {
				for(auto &state : pair.second.states) {
					trigger_analog(state.first, pair.first);

					/* reset value immediately after triggering
					 * this allows the value to accumulate across the entire frame
					 */
					state.second.previous = state.second.value;
					state.second.value = state.second.initial;
				}
			}

			framebuffer.back().digitals = digitals;
			framebuffer.push_back();
		}

		void apply_frame(frame f) {
			auto tmp = digitals;
			digitals = f.digitals;

			// save accumulated analog values before applying frame
			for(auto &pair : analogs) {
				for(auto &state : pair.second.states) {
					state.second.saved = state.second.value;
				}
			}

			for(auto &a : f.actions) {
				trigger_digital(a.objectID, a.ti, a.lt);
			}

			force_tick();

			// load accumulated analog values again
			for(auto &pair : analogs) {
				for(auto &state : pair.second.states) {
					state.second.value = state.second.saved;
				}
			}

			digitals = tmp;
		}

		void reg_digital(IDType objectID, std::type_index ti, bool state = false) {
			auto it = digitals.find(ti);
			if(it == digitals.end()) {
				digitals.emplace(ti, digital_data{});
			}
			digitals[ti].states.emplace(objectID, state);
		}

		void reg_analog(IDType objectID, std::type_index ti, Decimal initial = 0) {
			auto it = analogs.find(ti);
			if(it == analogs.end()) {
				analogs.emplace(ti, analog_data{});
			}
			analogs[ti].states.emplace(objectID, analog_state{initial, initial, initial});
		}

		template<typename T>
		typename std::enable_if<std::is_base_of<digital, T>::value>::type
		reg_digital(IDType objectID, bool state = false) { reg_digital(objectID, typeid(T), state); }

		template<typename T>
		typename std::enable_if<std::is_base_of<analog, T>::value>::type
		reg_analog(IDType objectID, Decimal initial = 0) { reg_analog(objectID, typeid(T), initial); }

		// digital -> digital function
		template<typename Src,
		         typename = typename std::enable_if<
{'fraction_non_alphanumeric': 0.12656597588104437, 'fraction_numerical': 0.0028099754127151387, 'mean_word_length': 3.617297297297297, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6174915', 'n_tokens_mistral': 3277, 'n_tokens_neox': 2719, 'n_words': 590}