/*
 * Copyright (C) 2021 Linux Studio Plugins Project <https://lsp-plug.in/>
 *           (C) 2021 Vladimir Sadovnikov <sadko4u@gmail.com>
 *
 * This file is part of lsp-plugins-trigger
 * Created on: 31 июл. 2021 г.
 *
 * lsp-plugins-trigger is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * lsp-plugins-trigger 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with lsp-plugins-trigger. If not, see <https://www.gnu.org/licenses/>.
 */

#ifndef PRIVATE_PLUGINS_TRIGGER_KERNEL_H_
#define PRIVATE_PLUGINS_TRIGGER_KERNEL_H_

#include <lsp-plug.in/plug-fw/plug.h>
#include <lsp-plug.in/dsp-units/ctl/Toggle.h>
#include <lsp-plug.in/dsp-units/ctl/Bypass.h>
#include <lsp-plug.in/dsp-units/ctl/Blink.h>
#include <lsp-plug.in/dsp-units/sampling/Sample.h>
#include <lsp-plug.in/dsp-units/sampling/SamplePlayer.h>
#include <lsp-plug.in/dsp-units/util/Randomizer.h>
#include <lsp-plug.in/lltl/parray.h>
#include <lsp-plug.in/ipc/ITask.h>

#include <private/meta/trigger.h>

namespace lsp
{
    namespace plugins
    {
        /**
         * Sampler implementation for single-channel audio sampler
         */
        class trigger_kernel
        {
            protected:
                struct afile_t;

                class AFLoader: public ipc::ITask
                {
                    private:
                        trigger_kernel         *pCore;
                        afile_t                *pFile;

                    public:
                        explicit AFLoader(trigger_kernel *base, afile_t *descr);
                        virtual ~AFLoader();

                    public:
                        virtual status_t        run();
                        void                    dump(dspu::IStateDumper *v) const;
                };

                class AFRenderer: public ipc::ITask
                {
                    private:
                        trigger_kernel         *pCore;
                        afile_t                *pFile;

                    public:
                        explicit AFRenderer(trigger_kernel *base, afile_t *descr);
                        virtual ~AFRenderer();

                    public:
                        virtual status_t        run();
                        void                    dump(dspu::IStateDumper *v) const;
                };

                class GCTask: public ipc::ITask
                {
                    private:
                        trigger_kernel         *pCore;

                    public:
                        explicit GCTask(trigger_kernel *base);
                        virtual ~GCTask();

                    public:
                        virtual status_t        run();
                        void                    dump(dspu::IStateDumper *v) const;
                };

            protected:
                struct afile_t
                {
                    size_t              nID;                                            // ID of sample
                    AFLoader           *pLoader;                                        // Audio file loader task
                    AFRenderer         *pRenderer;                                      // Audio file renderer task
                    dspu::Toggle        sListen;                                        // Listen toggle
                    dspu::Blink         sNoteOn;                                        // Note on led
                    dspu::Sample       *pOriginal;                                      // Source sample (original, as from source file)
                    dspu::Sample       *pProcessed;                                     // Processed sample
                    float              *vThumbs[meta::trigger_metadata::TRACKS_MAX];    // List of thumbnails

                    size_t              nUpdateReq;                                     // Update request
                    size_t              nUpdateResp;                                    // Update response
                    bool                bSync;                                          // Sync flag
                    float               fVelocity;                                      // Velocity
                    float               fPitch;                                         // Pitch (st)
                    float               fHeadCut;                                       // Head cut (ms)
                    float               fTailCut;                                       // Tail cut (ms)
                    float               fFadeIn;                                        // Fade In (ms)
                    float               fFadeOut;                                       // Fade Out (ms)
                    bool                bReverse;                                       // Reverse sample
                    float               fPreDelay;                                      // Pre-delay
                    float               fMakeup;                                        // Makeup gain
                    float               fGains[meta::trigger_metadata::TRACKS_MAX];     // List of gain values
                    float               fLength;                                        // Length in milliseconds
                    status_t            nStatus;                                        // Loading status
                    bool                bOn;                                            // On flag

                    plug::IPort        *pFile;                                          // Audio file port
                    plug::IPort        *pPitch;                                         // Pitch
                    plug::IPort        *pHeadCut;                                       // Head cut
                    plug::IPort        *pTailCut;                                       // Tail cut
                    plug::IPort        *pFadeIn;                                        // Fade in length
                    plug::IPort        *pFadeOut;                                       // Fade out length
                    plug::IPort        *pMakeup;                                        // Makup gain
                    plug::IPort        *pVelocity;                                      // Velocity range top
                    plug::IPort        *pPreDelay;                                      // Pre-delay
                    plug::IPort        *pListen;                                        // Listen trigger
                    plug::IPort        *pReverse;                                       // Reverse sample
                    plug::IPort        *pGains[meta::trigger_metadata::TRACKS_MAX];     // List of gain ports
                    plug::IPort        *pLength;                                        // Length of the file
                    plug::IPort        *pStatus;                                        // Status of the file
                    plug::IPort        *pMesh;                                          // Dump of the file data
                    plug::IPort        *pNoteOn;                                        // Note on flag
                    plug::IPort        *pOn;                                            // Sample on flag
                    plug::IPort        *pActive;                                        // Sample activity flag
                };

            protected:
                ipc::IExecutor     *pExecutor;                                          // Executor service
                afile_t            *vFiles;                                             // List of audio files
                afile_t           **vActive;                                            // List of active audio files
                dspu::Sample       *pGCList;                                            // Garbage collection list
                dspu::SamplePlayer  vChannels[meta::trigger_metadata::TRACKS_MAX];      // List of channels
                dspu::Bypass        vBypass[meta::trigger_metadata::TRACKS_MAX];        // List of bypasses
                dspu::Blink         sActivity;                                          // Note on led for instrument
                dspu::Toggle        sListen;                                            // Listen toggle
                dspu::Randomizer    sRandom;                                            // Randomizer
                GCTask              sGCTask;                                            // Garbage collection task

                size_t              nFiles;                                             // Number of files
                size_t              nActive;                                            // Number of active files
                size_t              nChannels;                                          // Number of audio channels (mono/stereo)
                float              *vBuffer;                                            // Buffer
                bool                bBypass;                                            // Bypass flag
                bool                bReorder;                                           // Reorder flag
                float               fFadeout;                                           // Fadeout in milliseconds
                float               fDynamics;                                          // Dynamics
                float               fDrift;                                             // Time drifting
                size_t              nSampleRate;                                        // Sample rate

                plug::IPort        *pDynamics;                                          // Dynamics port
                plug::IPort        *pDrift;                                             // Time drifting port
                plug::IPort        *pActivity;                                          // Activity port
                plug::IPort        *pListen;                                            // Listen trigger
                uint8_t            *pData;                                              // Pointer to aligned data


            protected:
                static void         unload_afile(afile_t *af);
                static void         destroy_afile(afile_t *af);
                static void         destroy_samples(dspu::Sample *gc_list);
                static void         destroy_sample(dspu::Sample * &sample);

            protected:
                void        destroy_state();
                void        perform_gc();
                status_t    load_file(afile_t *file);
                status_t    render_sample(afile_t *af);
                void        play_sample(const afile_t *af, float gain, size_t delay);
                void        cancel_sample(const afile_t *af, size_t fadeout, size_t delay);

                void        process_file_load_requests();
                void        process_file_render_requests();
                void        process_gc_tasks();
                void        reorder_samples();
                void        process_listen_events();
                void        play_samples(float **outs, const float **ins, size_t samples);
                void        output_parameters(size_t samples);

                template <class T>
                static void commit_afile_value(afile_t *af, T & field, plug::IPort *port);
                static void commit_afile_value(afile_t *af, bool & field, plug::IPort *port);

            protected:
                void        dump_afile(dspu::IStateDumper *v, const afile_t *f) const;

            public:
                explicit trigger_kernel();
                virtual ~trigger_kernel();

            public:
                void        trigger_on(size_t timestamp, float level);
                void        trigger_off(size_t timestamp, float level);
                void        trigger_stop(size_t timestamp);

            public:
                void        set_fadeout(float length);

            public:
                bool        init(ipc::IExecutor *executor, size_t files, size_t channels);
                size_t      bind(plug::IPort **ports, size_t port_id, bool dynamics);
                void        bind_activity(plug::IPort *activity);
                void        destroy();

                void        update_settings();
                void        update_sample_rate(long sr);
                void        sync_samples_with_ui();

                /** Process the sampler kernel
                 *
                 * @param outs list of outputs (should be not the sampe as ins)
                 * @param ins list of inputs, elements may be NULL
                 * @param samples number of samples to process
                 */
                void        process(float **outs, const float **ins, size_t samples);

                void        dump(dspu::IStateDumper *v) const;
        };
    } /* namespace plugins */
} /* namespace lsp */


#endif /* PRIVATE_PLUGINS_TRIGGER_KERNEL_H_ */
