#pragma once
#include <cstring>
#include <ctime>
#include <iostream>
#include <cmath>
#include <optional>

#include "day_of_week.h"
#include "configuration.h"
#include "time_range.h"
#include "payment_method.h"
#include "tariff_business_hours.h"

#include <QDateTime>

using namespace std;

namespace Utilities {

    bool isDayIncluded(uint64_t businessHours, QDateTime const &dt);

	/// <summary>
	/// Get day of week from current date (Zeller's Algorithm), starting day is Sunday
	/// </summary>
	/// <param name="date"></param>
	/// <returns></returns>
	DayOfWeek GetDayOfWeek(struct tm* tm);

	/// <summary>
	/// Date and time parse helper function
	/// </summary>
	/// <returns>Returns time (tm) structure</returns>
	struct tm DateTimeToStructTm(const char* dateTimeStr);

	/// <summary>
	/// Date parse helper function
	/// </summary>
	/// <returns>Returns time (tm) structure</returns>
	struct tm DateToStructTm(const char* dateStr);

	/// <summary>
	/// Time parse helper function
	/// </summary>
	/// <returns>Returns time (tm) structure</returns>
	struct tm TimeToStructTm(const char* timeStr, int year, int mon, int mday, int wday);

	/// <summary>
	/// Get current local time
	/// </summary>
	/// <returns>Returns time_t structure</returns>
	time_t GetCurrentLocalTime();

	/// <summary>
	/// Zeller's algorithm for determining day of week
	/// </summary>
	int ZellersAlgorithm(int day, int month, int year);

	/// <summary>
	///  Checks if current datetime is in range between start and end month of parking worktime
	/// </summary>
	/// <param name="tariff_cfg"></param>
	/// <param name="currentDateTime"></param>
	/// <returns></returns>
	bool IsYearPeriodActive(Configuration* cfg, struct tm* currentDateTime);
    bool IsYearPeriodActive(Configuration const *cfg, QDateTime const &currentDateTime);
    std::optional<ATBPeriodYear> GetYearPeriodActive(Configuration const *cfg, QDateTime const &currentDateTime);

	/// <summary>
	/// Check permissions
	/// </summary>
	bool CheckSpecialDay(Configuration* cfg, const char* currentDateTimeStr, int* specialDayId, double* specialDayPrice);
	bool CheckSpecialDay(Configuration const *cfg,
						 QDateTime const &currentDateTimeS,
						 int* specialDayId, uint32_t *specialDayPrice);

	/// <summary>
	/// Calculates price per unit
	/// </summary>
	/// <param name="pra_price"></param>
	/// <returns></returns>
	double CalculatePricePerUnit(double pra_price, double durationUnit = -1);

    QTime SpecialDaysWorkTimeFrom(Configuration const *cfg, int specialDayId);
    QTime SpecialDaysWorkTimeFrom(Configuration::SpecialDaysWorktimeType::const_iterator const &it);
    QTime SpecialDaysWorkTimeUntil(Configuration const *cfg, int specialDayId);
    QTime SpecialDaysWorkTimeUntil(Configuration::SpecialDaysWorktimeType::const_iterator const &it);
    QTime WeekDaysWorkTimeFrom(std::multimap<int, ATBWeekDaysWorktime>::const_iterator const &itr);
    QTime WeekDaysWorkTimeUntil(std::multimap<int, ATBWeekDaysWorktime>::const_iterator const &itr);
    int WeekDayId(std::multimap<int, ATBWeekDaysWorktime>::const_iterator const &itr);
    // PaymentRate GetPaymentRate(Configuration const *cfg, );
    bool isCarryOverSet(Configuration const *cfg, PaymentMethod paymentMethodId);
    bool isCarryOverNotSet(Configuration const *cfg, PaymentMethod paymentMethodId);
    PaymentMethod getPaymentMethodId(Configuration const *cfg);

    int getMinimalParkingTime(Configuration const *cfg, PaymentMethod methodId);
    int getMaximalParkingTime(Configuration const *cfg, PaymentMethod methodId);
    uint32_t getMinimalParkingPrice(Configuration const *cfg, PaymentMethod methodId);
    uint32_t getMaximalParkingPrice(Configuration const *cfg, PaymentMethod methodId);
    uint32_t getFirstDurationStep(Configuration const *cfg, PaymentMethod methodId);
    uint32_t getTimeRangeStep(Configuration const *cfg, int step, PaymentMethod methodId);
    BusinessHours getBusinessHours(Configuration const *cfg, PaymentMethod methodId);
    uint32_t computeWeekDaysPrice(Configuration const *cfg, PaymentMethod id);
    double computeWeekDaysDurationUnit(Configuration const *cfg, PaymentMethod id);
    QStringList dumpBusinessHours(uint64_t businessHours);
    uint32_t getDailyTicketCardPrice(Configuration const *cfg, PaymentMethod methodId);
}