/*
 * Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
 */
#pragma once
#include <stratosphere.hpp>
#include "erpt_srv_allocator.hpp"
#include "erpt_srv_ref_count.hpp"
#include "erpt_srv_journal_record.hpp"
#include "erpt_srv_stream.hpp"

namespace ams::erpt::srv {

    constexpr inline s32 JournalVersion = 1;

    constexpr inline u32 JournalStreamBufferSize = 4_KB;

    struct JournalMeta {
        s32 version;
        u32 transmitted_count[ReportType_Count];
        u32 untransmitted_count[ReportType_Count];
        util::Uuid journal_id;
        u32 reserved[4];
    };
    static_assert(sizeof(JournalMeta) == 0x34);

    class JournalForMeta {
        private:
            static JournalMeta s_journal_meta;
        public:
            static void InitializeJournal();
            static Result CommitJournal(Stream *stream);
            static Result RestoreJournal(Stream *stream);
            static u32 GetTransmittedCount(ReportType type);
            static u32 GetUntransmittedCount(ReportType type);
            static void IncrementCount(bool transmitted, ReportType type);
            static util::Uuid GetJournalId();
    };

    class JournalForReports {
        private:
            using RecordListType = util::IntrusiveListBaseTraits<JournalRecord<ReportInfo>>::ListType;
            static RecordListType s_record_list;
            static u32 s_record_count;
            static u32 s_record_count_by_type[ReportType_Count];
            static u32 s_used_storage;
        private:
            static void EraseReportImpl(JournalRecord<ReportInfo> *record, bool increment_count, bool force_delete_attachments);
        public:
            static void   CleanupReports();
            static Result CommitJournal(Stream *stream);
            static Result DeleteReport(ReportId report_id);
            static Result DeleteReportWithAttachments();
            static s64    GetMaxReportSize();
            static Result GetReportList(ReportList *out, ReportType type_filter);
            static u32    GetStoredReportCount(ReportType type);
            static u32    GetUsedStorage();
            static Result RestoreJournal(Stream *stream);

            static JournalRecord<ReportInfo> *RetrieveRecord(ReportId report_id);
            static Result StoreRecord(JournalRecord<ReportInfo> *record);
    };

    class JournalForAttachments {
        private:
            using AttachmentListType = util::IntrusiveListBaseTraits<JournalRecord<AttachmentInfo>>::ListType;
            static AttachmentListType s_attachment_list;
            static u32 s_attachment_count;
            static u32 s_used_storage;
        public:
            static void CleanupAttachments();
            static Result CommitJournal(Stream *stream);
            static Result DeleteAttachments(ReportId report_id);
            static Result GetAttachmentList(AttachmentList *out, ReportId report_id);
            static u32    GetUsedStorage();
            static Result RestoreJournal(Stream *stream);

            static JournalRecord<AttachmentInfo> *RetrieveRecord(AttachmentId attachment_id);
            static Result SetOwner(AttachmentId attachment_id, ReportId report_id);
            static Result StoreRecord(JournalRecord<AttachmentInfo> *record);

            static Result SubmitAttachment(AttachmentId *out, char *name, const u8 *data, u32 data_size);
    };

    class Journal {
        public:
            static void       CleanupAttachments();
            static void       CleanupReports();
            static Result     Commit();
            static Result     Delete(ReportId report_id);
            static Result     GetAttachmentList(AttachmentList *out, ReportId report_id);
            static util::Uuid GetJournalId();
            static s64        GetMaxReportSize();
            static Result     GetReportList(ReportList *out, ReportType type_filter);
            static u32        GetStoredReportCount(ReportType type);
            static u32        GetTransmittedCount(ReportType type);
            static u32        GetUntransmittedCount(ReportType type);
            static u32        GetUsedStorage();
            static Result     Restore();

            static JournalRecord<ReportInfo> *Retrieve(ReportId report_id);
            static JournalRecord<AttachmentInfo> *Retrieve(AttachmentId attachment_id);

            static Result Store(JournalRecord<ReportInfo> *record);
            static Result Store(JournalRecord<AttachmentInfo> *record);
    };

}
