#pragma once
#include <vector>
#include <utility>
#include <string>

#include "InputBuffer.h"

namespace CppModelBase
{
    class Simulation;

    typedef std::map<unsigned long long, nlohmann::json> TimePointList;

    class InputPort
    {
    private:
        unsigned long long time_ms = 0;
        nlohmann::json currentValue;
        TimePointList points;

        inline void SetPointAt(unsigned long long selectedTime, nlohmann::json value) { points[selectedTime] = value; };

    public:
        inline operator nlohmann::json() const { return currentValue; };
        // inline &operator nlohmann::json() { return currentValue; };

        // Set current value and update list
        inline InputPort &operator=(const nlohmann::json newValue)
        {
            currentValue = newValue;
            SetPointAt(time_ms, newValue);

            return *this;
        };

        inline nlohmann::json GetPointAt(unsigned long long selectedTime)
        {
            double key = points.begin()->first;
            for (auto &p : points)
            {
                if (p.first <= selectedTime)
                {
                    key = p.first;
                }
            }
            // return points[selectedTime];
            return points[key];
        };
        inline nlohmann::json &operator[](unsigned long long selectedTime) { return points[selectedTime]; };

        // Update current value from list
        inline void SetTime(unsigned long long newTime)
        {
            time_ms = newTime;
            currentValue = GetPointAt(time_ms);
        };

        inline TimePointList &GetPoints() { return points; };
    };
    typedef std::map<std::string, InputPort> InputPortList;

    class SimulationInputs : public InputBuffer
    {
    private:
        std::vector<std::pair<unsigned long long, PortList>> data;

        InputPortList rawData;
        InputPortList inputPorts;

        unsigned long long latestCallTime;

    protected:
        void CreateInputPort(std::string name);

        Port &GetPortAtTime(std::string name, unsigned long long time);

        Port &GetPortAtTimeSafe(std::string name, unsigned long long time, nlohmann::json fallbackValue);

    public:
        SimulationInputs();
        SimulationInputs(const SimulationInputs &other);

        void LoadFrom(DataAdapter &adapter);
        void RunCyclic(unsigned long long simulationTime_ms);
        Port &operator[](const std::string &name);
        Port &GetSafe(const std::string &name, nlohmann::json fallbackValue);

        unsigned long long simulationTotalTime;

        void SetInitialData(nlohmann::json &initialData);
        bool contains(std::string name);
        nlohmann::json ToJson();

        void Clear();
    };
}
