/*
 * 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 Klang_hpp
#define Klang_hpp

#include <limits.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

/* --- USER DEFINES --- */

#if __has_include("KlangConfiguration.h")
#include "KlangConfiguration.h"
#endif

/* --- DEBUG --- */

//#define DEBUG_SYNTHESIZER
//#define DEBUG_AUDIO_POOL
//#define DEBUG_CONNECTION_POOL
//#define DEBUG_SYNTHESIZER
//#define DEBUG_DAC
//#define DEBUG_OSC
//#define DEBUG_PRINT_EVENTS
//#define DEBUG_SHOW_DEFAULT_WARNINGS

/* --- DEFAULTS --- */

#ifndef KLANG_DEBUG_LEVEL
#define KLANG_DEBUG_LEVEL 0
#endif

#ifndef KLANG_DEFINES
#include "KlangDefines.hpp"
#endif

#ifndef KLANG_AUDIO_RATE
#define KLANG_AUDIO_RATE 48000
#ifdef DEBUG_SHOW_DEFAULT_WARNINGS
#warning setting KLANG_AUDIO_RATE to default value: 48000
#endif
#endif

#ifndef KLANG_AUDIO_BLOCKS
#define KLANG_AUDIO_BLOCKS 32
#ifdef DEBUG_SHOW_DEFAULT_WARNINGS
#warning setting KLANG_AUDIO_BLOCKS to default value: 32
#endif
#endif

#ifndef KLANG_SAMPLES_PER_AUDIO_BLOCK
#define KLANG_SAMPLES_PER_AUDIO_BLOCK 512
#ifdef DEBUG_SHOW_DEFAULT_WARNINGS
#warning setting KLANG_SAMPLES_PER_AUDIO_BLOCK to default value: 512
#endif
#endif

#if !defined(float_FLOAT) && !defined(float_INT16)
#warning float is not defined properly. choosing `float_FLOAT`. check if `KlangDefines.hpp` is included in `KlangConfiguration.h`?
#endif

#ifndef KLANG_HEAP_ALLOCATION
#define KLANG_HEAP_ALLOCATION 0
#endif
#if KLANG_HEAP_ALLOCATION > 0
#define KLANG_USE_HEAP_ALLOCATION
#endif

/* --- SIGNAL --- */

const static float SIGNAL_MIN = -1.0;
const static float SIGNAL_MAX = 1.0;

#define KLANG_FILL_AUDIO_BUFFER(DST, VAL) memset(DST, VAL, KLANG_SAMPLES_PER_AUDIO_BLOCK * sizeof(float))
#define KLANG_COPY_AUDIO_BUFFER(DST, SRC) memcpy(DST, SRC, KLANG_SAMPLES_PER_AUDIO_BLOCK * sizeof(float))

/* --- CONSTANTS --- */

#define DEFAULT_FREQUENCY (KLANG_AUDIO_RATE / 256.0 * 0.5)
static const CONNECTION_ID KLANG_CONNECTION_ERROR  = -1;
static const CONNECTION_ID KLANG_CREATE_NODE_ERROR = -1;
static const uint16_t      KLANG_AUDIO_RATE_UINT16 = KLANG_AUDIO_RATE;

namespace klang {
    class Node;
    class Connection;

    class Klang {
    public:
        static Klang &instance() {
            static Klang instance;
            return instance;
        }

        static CONNECTION_ID connect(Node &pOutputNode, Node &pInputNode);
        static CONNECTION_ID connect(Node &pOutputNode, CHANNEL_ID pOutputChannel, Node &pInputNode, CHANNEL_ID pInputChannel);
        static bool          disconnect(Node &pOutputNode, CHANNEL_ID pOutputChannel, Node &pInputNode, CHANNEL_ID pInputChannel);
        static bool          disconnect(CONNECTION_ID pConnectionID);

        FRAME_TYPE frame_index();
        void       reset_frame_index();

        static void lock() { instance().mLock = true; }
        static void unlock() { instance().mLock = false; }
        static bool islocked() { return instance().mLock; }

        bool frame_begin();
        void frame_end();

    private:
        Klang(Klang const &);
        void operator=(Klang const &);
        Klang() {}

        FRAME_TYPE    mFrameCounter = 0;
        volatile bool mLock         = false;
#ifdef DEBUG_SYNTHESIZER
        bool mFirstFrame = true;
#endif
    };
}  // namespace klang

#endif /* Klang_hpp */