aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-simulation/history/HistoryMap.hpp
blob: 64d886d2e8d767af42fd4ad32d8405f352501290 (plain) (blame)
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
#pragma once

#include <map>
#include <memory>

#include "openvic-simulation/dataloader/NodeTools.hpp"
#include "openvic-simulation/types/Date.hpp"

namespace OpenVic {

   struct HistoryEntry {
   private:
      Date PROPERTY(date);

   protected:
      HistoryEntry(Date new_date);
   };

   struct GameManager;

   /* 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:
      std::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) {
         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 false;
            }
         }
         return _load_history_entry(game_manager, args..., *it->second, root);
      }

   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..., _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;
            }
            const Date end_date = _get_end_date(game_manager);
            if (sub_date > end_date) {
               Logger::error("History entry ", sub_date, " defined after end date ", end_date);
               return false;
            }
            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);
      }

   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(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;
      }
   };
}