// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <cstring>

#include "common/assert.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/frontend/applets/profile_select.h"
#include "core/hle/service/acc/errors.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/frontend/applet_profile_select.h"
#include "core/hle/service/am/storage.h"

namespace Service::AM::Frontend {

ProfileSelect::ProfileSelect(Core::System& system_, std::shared_ptr<Applet> applet_,
                             LibraryAppletMode applet_mode_,
                             const Core::Frontend::ProfileSelectApplet& frontend_)
    : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {}

ProfileSelect::~ProfileSelect() = default;

void ProfileSelect::Initialize() {
    complete = false;
    status = ResultSuccess;
    final_data.clear();

    FrontendApplet::Initialize();
    profile_select_version = ProfileSelectAppletVersion{common_args.library_version};

    const std::shared_ptr<IStorage> user_config_storage = PopInData();
    ASSERT(user_config_storage != nullptr);
    const auto& user_config = user_config_storage->GetData();

    LOG_INFO(Service_AM, "Initializing Profile Select Applet with version={}",
             profile_select_version);

    switch (profile_select_version) {
    case ProfileSelectAppletVersion::Version1:
        ASSERT(user_config.size() == sizeof(UiSettingsV1));
        std::memcpy(&config_old, user_config.data(), sizeof(UiSettingsV1));
        break;
    case ProfileSelectAppletVersion::Version2:
    case ProfileSelectAppletVersion::Version3:
        ASSERT(user_config.size() == sizeof(UiSettings));
        std::memcpy(&config, user_config.data(), sizeof(UiSettings));
        break;
    default:
        UNIMPLEMENTED_MSG("Unknown profile_select_version = {}", profile_select_version);
        break;
    }
}

Result ProfileSelect::GetStatus() const {
    return status;
}

void ProfileSelect::ExecuteInteractive() {
    ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
}

void ProfileSelect::Execute() {
    if (complete) {
        PushOutData(std::make_shared<IStorage>(system, std::move(final_data)));
        Exit();
        return;
    }

    Core::Frontend::ProfileSelectParameters parameters{};

    switch (profile_select_version) {
    case ProfileSelectAppletVersion::Version1:
        parameters = {
            .mode = config_old.mode,
            .invalid_uid_list = config_old.invalid_uid_list,
            .display_options = config_old.display_options,
            .purpose = UserSelectionPurpose::General,
        };
        break;
    case ProfileSelectAppletVersion::Version2:
    case ProfileSelectAppletVersion::Version3:
        parameters = {
            .mode = config.mode,
            .invalid_uid_list = config.invalid_uid_list,
            .display_options = config.display_options,
            .purpose = config.purpose,
        };
        break;
    default:
        UNIMPLEMENTED_MSG("Unknown profile_select_version = {}", profile_select_version);
        break;
    }

    frontend.SelectProfile([this](std::optional<Common::UUID> uuid) { SelectionComplete(uuid); },
                           parameters);
}

void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
    UiReturnArg output{};

    if (uuid.has_value() && uuid->IsValid()) {
        output.result = 0;
        output.uuid_selected = *uuid;
    } else {
        status = Account::ResultCancelledByUser;
        output.result = Account::ResultCancelledByUser.raw;
        output.uuid_selected = Common::InvalidUUID;
    }

    final_data = std::vector<u8>(sizeof(UiReturnArg));
    std::memcpy(final_data.data(), &output, final_data.size());

    PushOutData(std::make_shared<IStorage>(system, std::move(final_data)));
    Exit();
}

Result ProfileSelect::RequestExit() {
    frontend.Close();
    R_SUCCEED();
}

} // namespace Service::AM::Frontend
