aboutsummaryrefslogtreecommitdiff
path: root/src/openvic-simulation/history/HistoryMap.hpp
blob: b062b0f53feebf2371cf92dd6a7d95bcbac10852 (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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#pragma once

#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 DefinitionManager;

   namespace _HistoryMapHelperFuncs {
      /* Helper functions to avoid cyclic dependency issues */
      Date _get_start_date(DefinitionManager const& definition_manager);
      Date _get_end_date(DefinitionManager const& definition_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(
         DefinitionManager const& definition_manager, Args... args, Date date, ast::NodeCPtr root
      ) {
         entry_type *const entry = _get_or_make_entry(definition_manager, date);
         if (entry != nullptr) {
            return _load_history_entry(definition_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(
         DefinitionManager const& definition_manager, Args... args, entry_type& entry, ast::NodeCPtr root
      ) = 0;

      bool _load_history_file(DefinitionManager const& definition_manager, Args... args, ast::NodeCPtr root) {
         return _try_load_history_entry(
            definition_manager, args..., _HistoryMapHelperFuncs::_get_start_date(definition_manager), root
         );
      }

      bool _load_history_sub_entry_callback(
         DefinitionManager const& definition_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(definition_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(DefinitionManager const& definition_manager, Date date) {
         const Date end_date = _HistoryMapHelperFuncs::_get_end_date(definition_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:
      void sort_entries() {
         std::vector<Date> keys;
         keys.reserve(entries.size());
         for (typename decltype(entries)::value_type const& entry : entries) {
            keys.push_back(entry.first);
         }
         std::sort(keys.begin(), keys.end());
         ordered_map<Date, std::unique_ptr<entry_type>> new_entries;
         for (Date const& key : keys) {
            new_entries.emplace(key, std::move(entries[key]));
         }
         entries = std::move(new_entries);
      }

      /* 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;
      }
   };
}