//  server scheduler
//  Copyright (C) 2008, 2010 Tim Blechmann
//
//  This program 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; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program 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; see the file COPYING.  If not, write to
//  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
//  Boston, MA 02111-1307, USA.

#pragma once

#include <mutex>

#include "dsp_thread_queue/dsp_thread.hpp"
#include "group.hpp"
#include "memory_pool.hpp"
#include "utilities/branch_hints.hpp"
#include "utilities/callback_system.hpp"
#include "utilities/static_pooled_class.hpp"

namespace nova {

/** audio-thread synchronization callback
 *
 *  callback for non-rt to rt thread synchronization. since it is using
 *  a locked internal memory pool, instances should not be allocated
 *  from the real-time thread
 *
 * */
struct audio_sync_callback
    : public static_pooled_class<audio_sync_callback, 1 << 20 /* 1mb pool of realtime memory */, true, 16> {
    virtual ~audio_sync_callback() = default;

    virtual void run(void) = 0;
};

/** scheduler class of the nova server
 *
 *  - provides a callback system to place callbacks in the scheduler
 *  - manages dsp threads, which themselves manage the dsp queue interpreter
 *
 *  scheduler_hook: functor to be called when after callbacks have been executed
 *                  and before the threads are executed
 *
 *  thread_init_functor: helper thread initialization functor
 *
 * */
template <class thread_init_functor = nop_thread_init> class scheduler {
    typedef nova::dsp_thread_pool<queue_node, thread_init_functor, rt_pool_allocator<void*>> dsp_thread_pool;
    typedef typename dsp_thread_pool::dsp_thread_queue_ptr dsp_thread_queue_ptr;
    typedef typename dsp_thread_pool::thread_count_t thread_count_t;

    struct reset_queue_cb : public audio_sync_callback {
    public:
        reset_queue_cb(scheduler* sched, dsp_thread_queue_ptr& qptr): sched(sched), qptr(qptr) {}

        void run(void) override {
            sched->reset_queue_sync(qptr);
            /** todo: later free the queue in a helper thread */
        }

    private:
        scheduler* sched;
        dsp_thread_queue_ptr qptr;
    };

protected:
    /* called from the driver callback */
    void reset_queue_sync(dsp_thread_queue_ptr&& qptr) { threads.reset_queue(std::move(qptr)); }

public:
    /* start thread_count - 1 scheduler threads */
    scheduler(thread_count_t thread_count = 1, bool realtime = false, bool pin_threads = false):
        threads(thread_count, !realtime, thread_init_functor(realtime, pin_threads)) {}

    void start_dsp_threads(void) { threads.start_threads(); }

    void terminate() {
        cbs.run_callbacks(); // audio backend must be closed by now
        threads.terminate_threads();
    }

    void add_sync_callback(audio_sync_callback* cb) {
        /* we need to guard, because it can be called from the main (system) thread and the network receiver thread */
        std::lock_guard<std::mutex> lock(sync_mutex);
        cbs.add_callback(cb);
    }

    /* called from the audio driver */
    void run_callbacks() { cbs.run_callbacks(); }

    void compute_audio() { threads.run(); }

    /* schedule to set a new queue */
    void reset_queue(dsp_thread_queue_ptr& qptr) { add_sync_callback(new reset_queue_cb(this, qptr)); }

private:
    callback_system<audio_sync_callback, false> cbs;
    dsp_thread_pool threads;
    std::mutex sync_mutex;
};

} /* namespace nova */
