// Copyright 2022 Michael Fisher <mfisher@lvtk.org>
// SPDX-License-Identifier: ISC

#pragma once

#include "lvtk/ext/extension.hpp"

#include <lv2/state/state.h>

namespace lvtk {

/** Alias of LV2_State_Flags
    @ingroup alias
    @headerfile lvtk/ext/state.hpp
*/
using StateFlags = LV2_State_Flags;

/** Alias of LV2_State_Status
    @ingroup alias
    @headerfile lvtk/ext/state.hpp
 */
using StateStatus = LV2_State_Status;

/** Wrapper struct for state retrieval. This wraps an
    LV2_State_Retrieve_Function and exeucutes via operator ()
    @ingroup utility
    @headerfile lvtk/ext/state.hpp
 */
struct StateRetrieve {
    StateRetrieve (LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle)
        : p_handle (handle), f_retrieve (retrieve) {}

    /** Call the retrieve function.

        @param key
        @param size
        @param type
        @param flags
        @returns Associated 'value' data for the given key
      */
    const void* operator() (uint32_t key,
                            size_t* size    = nullptr,
                            uint32_t* type  = nullptr,
                            uint32_t* flags = nullptr) const {
        return f_retrieve (p_handle, key, size, type, flags);
    }

private:
    LV2_State_Handle p_handle { nullptr };
    LV2_State_Retrieve_Function f_retrieve { nullptr };
};

/** Wrapper struct for state storage. This wraps an
    LV2_State_Store_Function and exeucutes via operator ()
    @ingroup utility
    @headerfile lvtk/ext/state.hpp
 */
struct StateStore {
    StateStore (LV2_State_Store_Function store, LV2_State_Handle handle)
        : p_handle (handle), f_store (store) {}

    /** Execute the store functor.

        @param key
        @param value
        @param size
        @param type
        @param flags
        @return LV2_STATE_SUCCESS on Success
     */
    inline StateStatus operator() (uint32_t key, const void* value,
                                   size_t size, uint32_t type,
                                   uint32_t flags = 0) const {
        return (StateStatus) f_store (p_handle, key, value, size, type, flags);
    }

private:
    LV2_State_Handle p_handle;
    LV2_State_Store_Function f_store;
};

/** Adds LV2 State support to your plugin instance.
    @ingroup ext
    @headerfile lvtk/ext/state.hpp
*/
template <class I>
struct State : Extension<I> {
    /** @private */
    State (const FeatureList&) {}

    /** Called by the host when saving state.

        @param store    Store function object to write keys/values
        @param flags    State flags to check
        @param features Additional features for this operation
    */
    StateStatus save (StateStore& store,
                      uint32_t flags, const FeatureList& features) {
        return LV2_STATE_SUCCESS;
    }

    /** Called by the host when restoring state.

        @param retrieve Retrieve function object to get keys/values
        @param flags    State flags to check
        @param features Additional features for this operation
    */
    StateStatus restore (StateRetrieve& retrieve,
                         uint32_t flags,
                         const FeatureList& features) {
        return LV2_STATE_SUCCESS;
    }

protected:
    /** @private */
    inline static void map_extension_data (ExtensionMap& extensions) {
        static const LV2_State_Interface _state = { _save, _restore };
        extensions[LV2_STATE__interface]        = &_state;
    }

private:
    static LV2_State_Status _save (LV2_Handle instance,
                                   LV2_State_Store_Function store_function,
                                   LV2_State_Handle state_handle,
                                   uint32_t flags,
                                   const LV2_Feature* const* features) {
        auto* const plugin = reinterpret_cast<I*> (instance);
        StateStore store (store_function, state_handle);
        FeatureList flist (features);
        return (LV2_State_Status) plugin->save (store, flags, flist);
    }

    static LV2_State_Status _restore (LV2_Handle instance,
                                      LV2_State_Retrieve_Function retrieve_function,
                                      LV2_State_Handle handle,
                                      uint32_t flags,
                                      const LV2_Feature* const* features) {
        auto* const plugin = static_cast<I*> (instance);
        StateRetrieve retrieve (retrieve_function, handle);
        FeatureList feature_list (features);
        return (LV2_State_Status) plugin->restore (retrieve, flags, feature_list);
    }
};

} // namespace lvtk
