1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
#pragma once
#include <map>
#include <memory>
#include "openvic-simulation/dataloader/NodeTools.hpp"
#include "openvic-simulation/types/Date.hpp"
#include "openvic-simulation/types/OrderedContainers.hpp"
namespace OpenVic {
struct HistoryEntry {
private:
Date PROPERTY(date);
protected:
HistoryEntry(Date new_date);
};
struct GameManager;
namespace _HistoryMapHelperFuncs {
/* Helper functions to avoid cyclic dependency issues */
Date _get_start_date(GameManager const& game_manager);
Date _get_end_date(GameManager const& game_manager);
}
template<std::derived_from<HistoryEntry> _Entry, typename... Args>
struct HistoryMap {
using entry_type = _Entry;
private:
ordered_map<Date, std::unique_ptr<entry_type>> PROPERTY(entries);
bool _try_load_history_entry(GameManager const& game_manager, Args... args, Date date, ast::NodeCPtr root) {
entry_type *const entry = _get_or_make_entry(game_manager, date);
if (entry != nullptr) {
return _load_history_entry(game_manager, args..., *entry, root);
} else {
return false;
}
}
protected:
HistoryMap() = default;
virtual std::unique_ptr<entry_type> _make_entry(Date date) const = 0;
virtual bool _load_history_entry(
GameManager const& game_manager, Args... args, entry_type& entry, ast::NodeCPtr root
) = 0;
bool _load_history_file(GameManager const& game_manager, Args... args, ast::NodeCPtr root) {
return _try_load_history_entry(game_manager, args..., _HistoryMapHelperFuncs::_get_start_date(game_manager), root);
}
bool _load_history_sub_entry_callback(
GameManager const& game_manager, Args... args, Date date, ast::NodeCPtr root, std::string_view key,
ast::NodeCPtr value, NodeTools::key_value_callback_t default_callback = NodeTools::key_value_invalid_callback
) {
/* Date blocks (loaded into the corresponding HistoryEntry) */
bool is_date = false;
const Date sub_date { Date::from_string(key, &is_date, true) };
if (is_date) {
if (sub_date < date) {
Logger::error("History entry ", sub_date, " defined before parent entry date ", date);
return false;
}
if (sub_date == date) {
Logger::warning("History entry ", sub_date, " defined with same date as parent entry");
}
if (_try_load_history_entry(game_manager, args..., sub_date, value)) {
return true;
} else {
Logger::error("Failed to load history entry at date ", sub_date);
return false;
}
}
return default_callback(key, value);
}
/* Returns history entry at specific date, if date doesn't have an entry creates one, if that fails returns nullptr. */
entry_type* _get_or_make_entry(GameManager const& game_manager, Date date) {
const Date end_date = _HistoryMapHelperFuncs::_get_end_date(game_manager);
if (date > end_date) {
Logger::error("History entry ", date, " defined after end date ", end_date);
return nullptr;
}
typename decltype(entries)::iterator it = entries.find(date);
if (it == entries.end()) {
const std::pair<typename decltype(entries)::iterator, bool> result = entries.emplace(date, _make_entry(date));
if (result.second) {
it = result.first;
} else {
Logger::error("Failed to create history entry at date ", date);
return nullptr;
}
}
return it->second.get();
}
public:
/* Returns history entry at specific date, if date doesn't have an entry returns nullptr. */
entry_type const* get_entry(Date date) const {
typename decltype(entries)::const_iterator it = entries.find(date);
if (it != entries.end()) {
return it->second.get();
}
return nullptr;
}
/* Returns history entries up to date as an ordered list of entries. */
std::vector<entry_type const*> get_entries_up_to(Date end) const {
std::vector<entry_type const*> ret;
for (typename decltype(entries)::value_type const& entry : entries) {
if (entry.first <= end) {
ret.push_back(entry.second.get());
}
}
std::sort(ret.begin(), ret.end(), [](entry_type const* lhs, entry_type const* rhs) -> bool {
return lhs->get_date() < rhs->get_date();
});
return ret;
}
};
}
|