/*
 * Copyright (c) Atmosphère-NX
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
#include 
#if defined(ATMOSPHERE_OS_HORIZON)
extern "C" {
    extern TimeServiceType __nx_time_service_type;
}
#endif
namespace ams::time {
    namespace {
        enum InitializeMode {
            InitializeMode_None,
            InitializeMode_Normal,
            InitializeMode_Menu,
            InitializeMode_System,
            InitializeMode_Repair,
            InitializeMode_SystemUser,
        };
        constinit u32 g_initialize_count = 0;
        constinit InitializeMode g_initialize_mode = InitializeMode_None;
        constinit os::SdkMutex g_initialize_mutex;
        Result InitializeImpl(InitializeMode mode) {
            std::scoped_lock lk(g_initialize_mutex);
            if (g_initialize_count > 0) {
                AMS_ABORT_UNLESS(mode == g_initialize_mode);
                g_initialize_count++;
                R_SUCCEED();
            }
            #if defined(ATMOSPHERE_OS_HORIZON)
            switch (mode) {
                case InitializeMode_Normal:     __nx_time_service_type = ::TimeServiceType_User;       break;
                case InitializeMode_Menu:       __nx_time_service_type = ::TimeServiceType_Menu;       break;
                case InitializeMode_System:     __nx_time_service_type = ::TimeServiceType_System;     break;
                case InitializeMode_Repair:     __nx_time_service_type = ::TimeServiceType_Repair;     break;
                case InitializeMode_SystemUser: __nx_time_service_type = ::TimeServiceType_SystemUser; break;
                AMS_UNREACHABLE_DEFAULT_CASE();
            }
            R_TRY(::timeInitialize());
            #else
            // TODO: Real AMS_ABORT("TODO");
            #endif
            g_initialize_count++;
            g_initialize_mode = mode;
            R_SUCCEED();
        }
    }
    Result Initialize() {
        R_RETURN(InitializeImpl(InitializeMode_Normal));
    }
    Result InitializeForSystem() {
        R_RETURN(InitializeImpl(InitializeMode_System));
    }
    Result InitializeForSystemUser() {
        if (hos::GetVersion() >= hos::Version_9_0_0) {
            R_RETURN(InitializeImpl(InitializeMode_SystemUser));
        } else {
            R_RETURN(InitializeImpl(InitializeMode_Normal));
        }
    }
    Result Finalize() {
        std::scoped_lock lk(g_initialize_mutex);
        if (g_initialize_count > 0) {
            if ((--g_initialize_count) == 0) {
                #if defined(ATMOSPHERE_OS_HORIZON)
                ::timeExit();
                #else
                // TODO: Real AMS_ABORT("TODO");
                #endif
                g_initialize_mode = InitializeMode_None;
            }
        }
        R_SUCCEED();
    }
    bool IsInitialized() {
        std::scoped_lock lk(g_initialize_mutex);
        return g_initialize_count > 0;
    }
    bool IsValidDate(int year, int month, int day) {
        return impl::util::IsValidDate(year, month, day);
    }
    Result GetElapsedSecondsBetween(s64 *out, const SteadyClockTimePoint &from, const SteadyClockTimePoint &to) {
        R_RETURN(impl::util::GetSpanBetween(out, from, to));
    }
}