#pragma once
#include "../../stddef.hpp"
#include "array_range.hpp"
#include "types.hpp"

namespace JabyEngine {
    class LZ4Decompressor {
    public:
        struct Result {
            Progress progress;
            size_t   bytes_ready;

            constexpr operator bool() const {
                return this->progress != Progress::Error;
            }

            static constexpr Result new_error() {
                return {Progress::Error, 0};
            }

            static constexpr Result new_done(size_t bytes_ready) {
                return {Progress::Done, bytes_ready};
            }

            static constexpr Result new_in_progress(size_t bytes_ready) {
                return {Progress::InProgress, bytes_ready};
            }
        };

    private:
        struct State {
            enum struct Step {
                Disabled,
                ReadToken,
                ObtainLiteralLength,
                CopyLiterals,
                ObtainMatchOffset,
                ObtainMatchLength,
                CopyMatch,
            };

            Step     step           = Step::Disabled;
            size_t   literal_length = 0;
            size_t   match_length   = 0;
            uint16_t match_offset   = 0;

            State() = default;

            void enable() {
                this->step           = Step::ReadToken;
                this->literal_length = 0;
                this->match_length   = 0;
                this->match_offset   = 0xFFFF;
            }

            void disable() {
                this->step = Step::Disabled;
            }
        };

    private:
        uint8_t* dst_adr = nullptr;
        State    state;

        static bool obtain_any_length(ArrayRange<const uint8_t>& data, uint32_t &dst_length);

    public:
        LZ4Decompressor() = default;
        LZ4Decompressor(uint8_t* dst_adr) : LZ4Decompressor() {
            LZ4Decompressor::setup(dst_adr);
        }

        void setup(uint8_t* dst_adr);
        void disable();

        Result process(ArrayRange<const uint8_t> data, bool is_last);
    };
}