#include <regex>
#include <array>
#include <vector>
#include <chrono>
#include <cassert>
#include <utility>
#include <iostream>

enum Month
{
  Jan = 1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
};

// structured binding, Datastructure for string to num conversion in month(.i.e."Mar" Month to 3)
std::array<std::pair<std::string, Month>, 12> monthinfo = 
{
    std::make_pair("Jan", Month::Jan),
    std::make_pair("Feb", Month::Feb),
    std::make_pair("Mar", Month::Mar),
    std::make_pair("Apr", Month::Apr),
    std::make_pair("May", Month::May),
    std::make_pair("Jun", Month::Jun),
    std::make_pair("Jul", Month::Jul),
    std::make_pair("Aug", Month::Aug),
    std::make_pair("Sep", Month::Sep),
    std::make_pair("Oct", Month::Oct),
    std::make_pair("Nov", Month::Nov),
    std::make_pair("Dec", Month::Dec)
};

// concrete daytime structure to store the data
//template<typename T1, typename T2 = std::string>
template<class T1, class T2 = std::string>
struct DayTime
{
    T1 day      = T1();
    T1 month    = T1();
    T1 year     = T1();
    T1 hour     = T1();
    T1 min      = T1();
    T1 second   = T1();
    T2 daystr   = T2();
    T2 dtstring = T2();
};


// main class which would fetch/parse the current time and provide to the client
class CurrentDateTime
{
   DayTime<std::string> ParseDateTime(const std::string&);
   void StrToNumber(const DayTime<std::string>&);
   Month GetMonth(const std::string&) const;
   DayTime<int> dt;

 public:

    CurrentDateTime();
    virtual ~CurrentDateTime(){};

    int      GetDay() const { return dt.day;    }
    Month  GetMonth() const { return static_cast<Month>(dt.month);  }
    int     GetYear() const { return dt.year;   }
    int     GetHour() const { return dt.hour;   }
    int      GetMin() const { return dt.min;    }
    int   GetSecond() const { return dt.second; }
    std::string   GetDayStr() const { return dt.daystr; }
};

CurrentDateTime::CurrentDateTime()
{
    //fetch/store current local-daytime information
    auto tp = std::chrono::system_clock::now();
    time_t cstyle_t = std::chrono::system_clock::to_time_t(tp);
    char* cstyleinfo = std::ctime(&cstyle_t);
    // copy(deep) the data into the std::string as ::ctime() provides static data 
    // which might be overwritten in case someone call it again.
    std::string currentinfo(cstyleinfo);

    //parse/store  the information
    auto dtstr = ParseDateTime(currentinfo);
    StrToNumber(dtstr);
}


DayTime<std::string> CurrentDateTime::ParseDateTime(const std::string& information)
{
    DayTime<std::string> info;
    std::regex dtimeregex(R"(^(\w{3}) (\w{3}) (\d{2}) (\d{2}):(\d{2}):(\d{2}) (\d{4}))");
    std::smatch match;

    if (std::regex_search(information, match, dtimeregex)) 
    {
        // Match the group and subgroups by regex parser.
        auto index = 0;
        info.dtstring = match[index++];
        info.daystr   = match[index++];
        info.month    = match[index++];
        info.day      = match[index++];
        info.hour     = match[index++];
        info.min      = match[index++];
        info.second   = match[index++];
        info.year     = match[index++];
    }

    return info;
}

Month CurrentDateTime::GetMonth(const std::string& input) const 
{
    for (const auto& itr : monthinfo) 
    {
        if (itr.first == input) {
            return itr.second;
        }
      //return static_cast<Month>(itr.second);
    }
    assert(false && "Invalid month name");
    // Or return a default month if it makes sense.
    // Another possibility is extending the enum Month to 
    // add an invalid dummy value, throwing an exception, etc.
}

void CurrentDateTime::StrToNumber(const DayTime<std::string>& information)
{
    dt.dtstring = information.dtstring;
    dt.daystr   = information.daystr;
    dt.month    = GetMonth(information.month);

    dt.day      = std::stoi(information.day.c_str());
    dt.hour     = std::stoi(information.hour.c_str());
    dt.min      = std::stoi(information.min.c_str());
    dt.second   = std::stoi(information.second.c_str());
    dt.year     = std::stoi(information.year.c_str());
}


int main()
{
  CurrentDateTime current = *new CurrentDateTime();

    std::cout << "\n\tCurrent Day....: " << current.GetDayStr()
              << "\n\tCurrent Date...: " << current.GetDay()
              << "\n\tCurrent Month..: " << current.GetMonth()
              << "\n\tCurrent Year...: " << current.GetYear()
              << "\n\tCurrent Hour...: " << current.GetHour()
              << "\n\tCurrent Min....: " << current.GetMin()
              << "\n\tCurrent Second.: " << current.GetSecond() 
              << "\n\n";
    return 0;
} 