/*
 * Klang – a node+text-based synthesizer library
 *
 * This file is part of the *wellen* library (https://github.com/dennisppaul/wellen).
 * Copyright (c) 2022 Dennis P Paul.
 *
 * This library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3.
 *
 * This library is distributed in the hope that 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/>.
 */

#ifndef AudioBlockPool_hpp
#define AudioBlockPool_hpp

#include "Klang.hpp"

#ifndef KLANG_AUDIO_BLOCKS
#error KLANG_AUDIO_BLOCKS not defined.
#endif

#ifdef KLANG_USE_HEAP_ALLOCATION
#warning KLANG_USE_HEAP_ALLOCATION is enabled.
#endif

namespace klang {
    class AudioBlockPool {
    public:
        static const AUDIO_BLOCK_ID NO_ID = -1;

        static AudioBlockPool& instance() {
            static AudioBlockPool instance;
            return instance;
        }

        ~AudioBlockPool() {
#ifdef KLANG_USE_HEAP_ALLOCATION
            for (AUDIO_BLOCK_ID i = 0; i < KLANG_AUDIO_BLOCKS; i++) {
                delete[] mAudioBlocks[i];
            }
            delete[] mAudioBlocks;
#endif
        }

        AUDIO_BLOCK_ID request() {
            for (AUDIO_BLOCK_ID i = 0; i < KLANG_AUDIO_BLOCKS; i++) {
                if (mAudioBlockAvailableIDs[i]) {
                    mAudioBlockAvailableIDs[i] = false;
                    mAudioBlocksNeeded++;
                    if (mAudioBlocksNeeded > mAudioBlocksNeededMax) {
                        mAudioBlocksNeededMax = mAudioBlocksNeeded;
                    }
                    return i;
                }
            }
#ifdef DEBUG_AUDIO_POOL
            KLANG_LOG_ERR("+++ @AudioBlockPool no more audio blocks available");
#endif
            return NO_ID;
        }

        void release(AUDIO_BLOCK_ID pAudioBlockID) {
            if (pAudioBlockID > NO_ID && pAudioBlockID < KLANG_AUDIO_BLOCKS) {
                mAudioBlockAvailableIDs[pAudioBlockID] = false;
                mAudioBlocksNeeded--;
            }
        }

        float* data(AUDIO_BLOCK_ID pAudioBlockID) {
            if (pAudioBlockID > NO_ID && pAudioBlockID < KLANG_AUDIO_BLOCKS) {
                return mAudioBlocks[pAudioBlockID];
            } else {
                return nullptr;
            }
        }

        void reset() {
            for (AUDIO_BLOCK_ID i = 0; i < KLANG_AUDIO_BLOCKS; i++) {
                mAudioBlockAvailableIDs[i] = true;
            }
            mAudioBlocksNeeded    = 0;
            mAudioBlocksNeededMax = 0;
        }

        uint8_t blocks_available() {
            return KLANG_AUDIO_BLOCKS - mAudioBlocksNeeded;
        }

        uint8_t blocks_used() {
            return mAudioBlocksNeeded;
        }

        uint8_t blocks_used_max() {
            return mAudioBlocksNeededMax;
        }

    private:
        AudioBlockPool(AudioBlockPool const&);
        void operator=(AudioBlockPool const&);

        bool mAudioBlockAvailableIDs[KLANG_AUDIO_BLOCKS];
#ifdef KLANG_USE_HEAP_ALLOCATION
        float** mAudioBlocks;
#else
        // @TODO(see if there is a way to move audioblock buffer to specific memory location in class ( with `static`? ) + test this on desktop version too)
        // static float mAudioBlocks[KLANG_AUDIO_BLOCKS][KLANG_SAMPLES_PER_AUDIO_BLOCK] __attribute__ ((section(".audio_block_buffer"))); // or make this global
        // float mAudioBlocks[KLANG_AUDIO_BLOCKS][KLANG_SAMPLES_PER_AUDIO_BLOCK];
        // @TODO(removed `inline` not sure why it was there in the first place.)
        inline static float mAudioBlocks[KLANG_AUDIO_BLOCKS][KLANG_SAMPLES_PER_AUDIO_BLOCK]
#if (KLST_ARCH == KLST_ARCH_MCU)
            __attribute__((section(".audio_block_buffer")))
        // @TODO(moved memory to RAM_D3 (0x38000000). not really happy about this plattform specificness at this point)
            // @NOTE
            // on KLST_SHEEP audio block buffer is located at 0x38000000 which has 64K of memory.
            // 2048 bytes are used by the DMA buffer. this leaves 63488 bytes for the audio blocks.
            // e.g at an audio block size of 256 bytes this would allow 248 audio blocks.
#endif
            ;
#endif

        uint8_t mAudioBlocksNeeded    = 0;
        uint8_t mAudioBlocksNeededMax = 0;

        AudioBlockPool() {
#ifdef KLANG_USE_HEAP_ALLOCATION
            mAudioBlocks = new float*[KLANG_AUDIO_BLOCKS];
            for (AUDIO_BLOCK_ID i = 0; i < KLANG_AUDIO_BLOCKS; i++) {
                float* mBlock = new float[KLANG_SAMPLES_PER_AUDIO_BLOCK];
                mAudioBlocks[i]     = mBlock;
            }
#endif
            for (AUDIO_BLOCK_ID i = 0; i < KLANG_AUDIO_BLOCKS; i++) {
                mAudioBlockAvailableIDs[i] = true;
            }
            reset();
        }
    };
}  // namespace klang

#endif /* AudioBlockPool_hpp */