#pragma once
#include <map>
#include <variant>
#include <cstddef>
#include <stdio.h>
#include <algorithm>
#include <rapidjson/rapidjson.h>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
#include "member_type.h"
#include "currency.h"
#include "duration.h"
#include "payment_mtd.h"
#include "payment_opt.h"
#include "spec_days.h"
#include "payment_opt.h"
#include "weekdays.h"
#include "weekdays_worktime.h"
#include "spec_days_worktime.h"
#include "member_type.h"
#include "period_year.h"
#include "payment_rate.h"
#include "atb_project.h"
#include "tariff_daily_ticket.h"
#include "time_range_header.h"
#include "tariff_timestep_config.h"
#include "tariff_product.h"
#include "tariff_interpolation.h"
#include "tariff_prepaid.h"
#include "tariff_carryover.h"
#include "tariff_permit_type.h"

#include <QVector>
#include <optional>

using namespace std;
using namespace rapidjson;

class Calculator;
class Configuration
{
public:
    using SpecialDaysType = std::multimap<int, ATBSpecialDays>;
    using SpecialDaysWorktimeType = std::multimap<int, ATBSpecialDaysWorktime>;
    using TimeRangeType = std::multimap<int, ATBTimeRange>;
    using TariffProductType = std::multimap<int, ATBTariffProduct>;
    using ATBPaymentOptionType = std::multimap<int, ATBPaymentOption>;
    using TariffInterpolationType = std::multimap<int, ATBInterpolation>;
    using TariffPrepaidType = std::multimap<int, ATBPrepaid>;
    using TariffCarryOverType = std::multimap<int, ATBCarryOver>;
    using TariffDurationType = std::multimap<int, ATBDuration>;

    ATBProject project;
    ATBCurrency Currency;
    ATBDuration duration;

    TariffDurationType Duration;
	multimap<int, ATBPaymentMethod> PaymentMethod;
	multimap<int, ATBPaymentRate> PaymentRate;
	SpecialDaysWorktimeType SpecialDaysWorktime;
	SpecialDaysType SpecialDays;
	multimap<int, ATBWeekDays> WeekDays;
	multimap<int, ATBPeriodYear> YearPeriod;
	multimap<int, ATBWeekDaysWorktime> WeekDaysWorktime;
	ATBPaymentOptionType PaymentOption;
	multimap<int, ATBDailyTicket> DailyTicket;
	TimeRangeType TimeRange;
	multimap<int, ATBTimeStepConfig> TimeStepConfig;
	multimap<int, ATBTimeBase> TimeBase;
	multimap<int, ATBCustomer> Customer;
	TariffProductType TariffProduct;
	TariffInterpolationType TariffInterpolations;
	TariffPrepaidType TariffPrepaidOptions;
	TariffCarryOverType TariffCarryOverOptions;

	/// <summary>
	/// Parse JSON string
	/// </summary>
	/// <param name="json"></param>
	/// <returns>Returns operation status bool (OK | FAIL) </returns>
	bool ParseJson(Configuration* cfg, const char* json);

    ATBPaymentOption &getPaymentOptions(int paymentOptionsIndex=0);
    ATBPaymentOption const &getPaymentOptions(int paymentOptionsIndex=0) const;
    QVector<ATBPaymentOption> &getAllPaymentOptions();
    QVector<ATBPaymentOption> const &getAllPaymentOptions() const;
    int getPaymentOptionIndex(QDateTime const &dt) const;
    int getPaymentOptionIndexIfSpecialDay(QDateTime const &dt) const;
    bool isSpecialDay(QDateTime const &dt) const;
    int specialDayId(QDateTime const &dt) const;
    ATBSpecialDays specialDay(QDateTime const &dt) const;
    bool isDayIncluded(uint64_t businessHours, QDateTime const &dt) const;
    bool isDayIncludedAsSpecialDay(uint64_t businessHours, QDateTime const &dt) const;
    bool isDayIncludedAsSpecialDay(uint64_t businessHours, int specialDayId) const;
    std::optional<QVector<ATBPaymentRate>> getPaymentRateForAllKeys() const;
    std::optional<QVector<ATBPaymentOption>> getPaymentOptionsForAllKeys() const;
    std::optional<ATBPaymentOption> getPaymentOptionForId(int key) const;
    std::optional<QVector<ATBPaymentRate>> getPaymentRateForKey(int key) const;
    std::optional<ATBPaymentOption> getPaymentOptionForKey(PERMIT_TYPE permitType) const;
    std::optional<ATBPaymentOption> getPaymentOptionForKey(int permitType) const;
    std::optional<ATBPaymentOption> getPaymentOptionForKey(QString const &permitType) const;
    std::optional<QVector<ATBDailyTicket>> getDailyTicketsForAllKeys() const;
    std::optional<QVector<ATBDailyTicket>> getDailyTicketsForKey(int key) const;
    std::optional<ATBInterpolation> getInterpolationType(int type) const;
    std::optional<QDateTime> getInterpolationEnd(QDateTime const &start, int paymentOptionIndex) const;
    std::optional<ATBPrepaid> getPrepaidType(int type) const;
    std::optional<QVector<ATBTariffProduct>> getTariffProductForAllKeys() const;
    std::optional<QVector<ATBTariffProduct>> getTariffProductForProductId(int id) const;
    std::optional<QVector<ATBTariffProduct>> getTariffProductForProductId(PermitType permitType) const;
    std::optional<QVector<ATBTariffProduct>> getTariffProductForProductTypeName(QString const &permitTypeName) const;
    std::optional<ATBCustomer> getCustomerForType(ATBCustomer::CustomerType customerType);
    std::optional<ATBWeekDaysWorktime> getWeekDayWorkTime(QTime const &time, Qt::DayOfWeek dayOfWeek);
    std::optional<QVector<ATBWeekDaysWorktime>> getAllWeekDayWorkTimes();

    std::optional<QDateTime> prepaidStart(QDateTime const &start, int prepaid_option_id);
    int getPaymentOptionIndex(PERMIT_TYPE permitType);
    int getPaymentOptionIndex(PERMIT_TYPE permitType) const;

private:
	/// <summary>
	/// Identify type of JSON member
	/// </summary>
	/// <param name="member_name"></param>
	/// <returns></returns>
	MemberType IdentifyJsonMember(const char* member_name);

	QVector<ATBPaymentOption> currentPaymentOptions;
};