/*
 * SPDX-License-Identifier: GPL-3.0-only
 * MuseScore-CLA-applies
 *
 * MuseScore
 * Music Composition & Notation
 *
 * Copyright (C) 2025 MuseScore Limited and others
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * 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.  If not, see <https://www.gnu.org/licenses/>.
 */

#pragma once

#include <stdint.h>

typedef void* MuseSamplerLib;

typedef enum ms_Result
{
    ms_Result_OK = 0,
    ms_Result_Error = -1,
    ms_Result_TimeoutErr = -2
} ms_Result;

// Functions to return the major + minor version of the core code. Note that this is independent of the UI version.
typedef int (* ms_get_version_major)();
typedef int (* ms_get_version_minor)();
typedef int (* ms_get_version_revision)();
typedef int (* ms_get_version_build_number)();

/*\\\ TYPES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

typedef void* ms_MuseSampler;
typedef void* ms_PresetList;
typedef int ms_PresetChange; // added in v0.6
typedef void* ms_InstrumentList;
typedef void* ms_InstrumentInfo;
typedef void* ms_Track;

typedef struct ms_OutputBuffer
{
    float** _channels;
    int _num_data_pts;
    int _num_channels; // For sanity checking! Shouldn't change from init!
} ms_OutputBuffer;

// Added in v0.4
typedef struct ms_DynamicsEvent_2
{
    long long _location_us;
    double _value; // 0.0 - 1.0
} ms_DynamicsEvent_2;

// Added in v0.4
typedef struct ms_PedalEvent_2
{
    long long _location_us;
    double _value; // 0.0 - 1.0
} ms_PedalEvent_2;

enum ms_NoteArticulation : uint64_t
{
    ms_NoteArticulation_None = 0,
    ms_NoteArticulation_Staccato = 1LL << 0,
    ms_NoteArticulation_Staccatissimo = 1LL << 1,
    ms_NoteArticulation_Accent = 1LL << 2,
    ms_NoteArticulation_Tenuto = 1LL << 3,
    ms_NoteArticulation_Marcato = 1LL << 4,
    ms_NoteArticulation_Harmonics = 1LL << 5,
    ms_NoteArticulation_Mute = 1LL << 6,
    ms_NoteArticulation_Trill = 1LL << 7,
    ms_NoteArticulation_MordentSemi = 1LL << 8,
    ms_NoteArticulation_MordentWhole = 1LL << 9,
    ms_NoteArticulation_MordentInvertedSemi = 1LL << 10,
    ms_NoteArticulation_MordentInvertedWhole = 1LL << 11,
    ms_NoteArticulation_TurnSemiWhole = 1LL << 12,
    ms_NoteArticulation_TurnSemiSemi = 1LL << 13,
    ms_NoteArticulation_TurnWholeWhole = 1LL << 14,
    ms_NoteArticulation_TurnWholeSemi = 1LL << 15,
    ms_NoteArticulation_TurnInvertedSemiWhole = 1LL << 16,
    ms_NoteArticulation_TurnInvertedSemiSemi = 1LL << 17,
    ms_NoteArticulation_TurnInvertedWholeWhole = 1LL << 18,
    ms_NoteArticulation_TurnInvertedWholeSemi = 1LL << 19,
    ms_NoteArticulation_ArpeggioUp = 1LL << 20,
    ms_NoteArticulation_ArpeggioDown = 1LL << 21,
    ms_NoteArticulation_Tremolo1 = 1LL << 22, // One or two note, depending on fallback notes
    ms_NoteArticulation_Tremolo2 = 1LL << 23, // One or two note, depending on fallback notes
    ms_NoteArticulation_Tremolo3 = 1LL << 24, // One or two note, depending on fallback notes
    ms_NoteArticulation_Scoop = 1LL << 25,
    ms_NoteArticulation_Plop = 1LL << 26,
    ms_NoteArticulation_Doit = 1LL << 27,
    ms_NoteArticulation_Fall = 1LL << 28,
    ms_NoteArticulation_Appoggiatura = 1LL << 29, // Duration is ignored
    ms_NoteArticulation_Acciaccatura = 1LL << 30, // Duration is ignored
    ms_NoteArticulation_Open = 1LL << 31,

    ms_NoteArticulation_Portamento = 1LL << 36,
    ms_NoteArticulation_Pizzicato = 1LL << 37,
    ms_NoteArticulation_Glissando = 1LL << 39,
    ms_NoteArticulation_Pedal = 1LL << 40,
    ms_NoteArticulation_Slur = 1LL << 41,
    ms_NoteArticulation_SnapPizzicato = 1LL << 42,
    ms_NoteArticulation_ColLegno = 1LL << 43,
    ms_NoteArticulation_SulTasto = 1LL << 44,
    ms_NoteArticulation_SulPonticello = 1LL << 45,
    ms_NoteArticulation_LeftHandTapping = 1LL << 46,
    ms_NoteArticulation_RightHandTapping = 1LL << 47,
    ms_NoteArticulation_PalmMute = 1LL << 48,
    ms_NoteArticulation_PinchHarmonic = 1LL << 49,
    ms_NoteArticulation_BuzzTremolo = 1LL << 50,
    // TO BE REPLACED = 1LL << 51,

    ms_NoteArticulation_Martellato = 1LL << 32,
    ms_NoteArticulation_MartellatoLift = 1LL << 33,
    ms_NoteArticulation_HandMartellato = 1LL << 34,
    ms_NoteArticulation_MutedMartellato = 1LL << 35,
    ms_NoteArticulation_MalletBellSuspended = 1LL << 38,
    ms_NoteArticulation_MalletBellOnTable = 1LL << 52,
    ms_NoteArticulation_MalletLift = 1LL << 53,
    ms_NoteArticulation_PluckLift = 1LL << 54,
    ms_NoteArticulation_Gyro = 1LL << 55,
    // More noteheads:
    ms_NoteArticulation_LaissezVibrer = 1LL << 59,
};

enum ms_NoteArticulation2 : uint64_t
{
    ms_NoteArticulation2_None = 0,
    ms_NoteArticulation2_Ring = 1LL << 0,
    ms_NoteArticulation2_ThumbDamp = 1LL << 1,
    ms_NoteArticulation2_BrushDamp = 1LL << 2,
    ms_NoteArticulation2_RingTouch = 1LL << 3,
    ms_NoteArticulation2_Pluck = 1LL << 4,
    ms_NoteArticulation2_SingingBell = 1LL << 5,
    ms_NoteArticulation2_SingingVibrate = 1LL << 6,
    ms_NoteArticulation2_HandbellSwing = 1LL << 7,
    ms_NoteArticulation2_Echo = 1LL << 8,

    ms_NoteArticulation2_FallRough = 1LL << 9,
    ms_NoteArticulation2_PlopRough = 1LL << 10,
    ms_NoteArticulation2_DoitRough = 1LL << 11,
    ms_NoteArticulation2_ScoopRough = 1LL << 12,
    ms_NoteArticulation2_BreathMark = 1LL << 13,
};

// added in v0.6
enum ms_NoteHead : int16_t
{
    ms_NoteHead_Normal,
    ms_NoteHead_XNote,
    ms_NoteHead_LargeX,
    ms_NoteHead_OrnateXNote,
    ms_NoteHead_CircleXNote,
    ms_NoteHead_CircleDot,
    ms_NoteHead_Ghost,
    ms_NoteHead_Circle,
    ms_NoteHead_Diamond,
    ms_NoteHead_Triangle,
    ms_NoteHead_TriangleUp,
    ms_NoteHead_TriangleDown,
    ms_NoteHead_TriangleRight,
    ms_NoteHead_TriangleRoundDown,
    ms_NoteHead_FlatTop,
    ms_NoteHead_Square,
    ms_NoteHead_Slash,
    ms_NoteHead_SlashRightFilled,
    ms_NoteHead_SlashLeftFilled,
    ms_NoteHead_Plus,
};

// Added in v0.6
typedef struct ms_NoteEvent_4
{
    int _voice; // 0-3
    long long _location_us;
    long long _duration_us;
    int _pitch;      // MIDI pitch
    double _tempo;   // BPM
    int _offset_cents; // pitch offset in cents: -50 would be a quarter tone flat
    ms_NoteArticulation _articulation;
    ms_NoteHead _notehead;
} ms_NoteEvent_4;

// Added in version 0.102 // Adds a new ms_NoteArticulation2 enum so we have more bits available
typedef struct ms_NoteEvent_5
{
    int _voice; // 0-3
    long long _location_us;
    long long _duration_us;
    int _pitch;      // MIDI pitch
    double _tempo;   // BPM
    int _offset_cents; // pitch offset in cents: -50 would be a quarter tone flat
    ms_NoteArticulation _articulation;
    ms_NoteArticulation2 _articulation_2;
    ms_NoteHead _notehead;
} ms_NoteEvent_5;

// Added in v0.100 -> changed from ms_AuditionStartNoteEvent3 by adding "_active_syllable"
typedef struct ms_AuditionStartNoteEvent_4
{
    int _pitch;      // MIDI pitch
    int _offset_cents; // pitch offsets in cents ()
    ms_NoteArticulation _articulation;
    ms_NoteHead _notehead;
    double _dynamics;
    const char* _active_presets; // presets that are selected for this note. list is separated by "|". If "" then the
                                 // first available preset is used.
    const char* _active_text_articulation; // text articulation that is active for this note. Can be empty
    const char* _active_syllable;        // currently active lyrics syllable. Can be empty
    bool _articulation_text_starts_at_note;
    bool _syllable_starts_at_note;
} ms_AuditionStartNoteEvent_4;

// Added in version 0.102 // Adds a new ms_NoteArticulation2 enum so we have more bits available
typedef struct ms_AuditionStartNoteEvent_5
{
    int _pitch;      // MIDI pitch
    int _offset_cents; // pitch offsets in cents ()
    ms_NoteArticulation _articulation;
    ms_NoteArticulation2 _articulation_2;
    ms_NoteHead _notehead;
    double _dynamics;
    const char* _active_presets; // presets that are selected for this note. list is separated by "|". If "" then the
                                 // first available preset is used.
    const char* _active_text_articulation; // text articulation that is active for this note. Can be empty
    const char* _active_syllable;        // currently active lyrics syllable. Can be empty
    bool _articulation_text_starts_at_note;
    bool _syllable_starts_at_note;
} ms_AuditionStartNoteEvent_5;

typedef struct ms_AuditionStopNoteEvent
{
    int _pitch; // MIDI pitch
} ms_AuditionStopNoteEvent;

// Added in v0.5
enum ms_PitchBendType
{
    PitchBend_Linear,
    PitchBend_Bezier,
};

typedef struct ms_PitchBendInfo
{
    int64_t event_id;
    long long _start_us; // offset from the start of the note, not absolute time
    long long _duration_us;
    int _offset_cents;
    ms_PitchBendType _type;
} ms_PitchBendInfo;

typedef struct ms_VibratoInfo
{
    int64_t event_id;
    long long _start_us;
    long long _duration_us;
    int _depth_cents;
} ms_VibratoInfo;

// Added in v0.6
typedef struct ms_TextArticulationEvent
{
    long long _start_us;
    const char* _articulation; // articulation name
} ms_TextArticulationEvent;

// Added in v0.100
typedef struct ms_SyllableEvent
{
    const char* _text;
    long long _position_us;
} ms_SyllableEvent;

typedef ms_Result (* ms_init)();
typedef ms_Result (* ms_disable_reverb)();

typedef ms_InstrumentList (* ms_get_instrument_list)();
typedef ms_InstrumentList (* ms_get_matching_instrument_list)(const char* mpe_id, const char* musicxml_id);
typedef ms_InstrumentInfo (* ms_InstrumentList_get_next)(ms_InstrumentList instrument_list);

typedef int (* ms_Instrument_get_id)(ms_InstrumentInfo instrument);
typedef const char*(* ms_Instrument_get_name)(ms_InstrumentInfo);
typedef const char*(* ms_Instrument_get_category)(ms_InstrumentInfo);
typedef const char*(* ms_Instrument_get_musicxml_sound)(ms_InstrumentInfo);
typedef const char*(* ms_Instrument_get_mpe_sound)(ms_InstrumentInfo);
typedef float (* ms_Instrument_get_reverb_level)(ms_InstrumentInfo);

typedef ms_PresetList (* ms_Instrument_get_preset_list)(ms_InstrumentInfo);
typedef const char*(* ms_PresetList_get_next)(ms_PresetList);

typedef ms_MuseSampler (* ms_MuseSampler_create)();
typedef void (* ms_MuseSampler_destroy)(ms_MuseSampler);
typedef ms_Result (* ms_MuseSampler_init)(ms_MuseSampler ms, double sample_rate, int block_size, int channel_count);

// Added in 0.100
typedef ms_Result (* ms_MuseSampler_init_2)(ms_MuseSampler ms, double sample_rate, int block_size, int channel_count);

typedef ms_Result (* ms_MuseSampler_finalize_score)(ms_MuseSampler ms);
typedef ms_Track (* ms_MuseSampler_add_track)(ms_MuseSampler ms, int instrument_id);
typedef ms_Result (* ms_MuseSampler_finalize_track)(ms_MuseSampler ms, ms_Track track);
typedef ms_Result (* ms_MuseSampler_clear_track)(ms_MuseSampler ms, ms_Track track);

// Added in 0.6
typedef ms_Result (* ms_MuseSampler_add_track_note_event_5)(ms_MuseSampler ms, ms_Track track, ms_NoteEvent_4 evt, long long& event_id);
// Added in 0.4
typedef ms_Result (* ms_MuseSampler_add_track_dynamics_event_2)(ms_MuseSampler ms, ms_Track track, ms_DynamicsEvent_2 evt);
// Added in 0.4
typedef ms_Result (* ms_MuseSampler_add_track_pedal_event_2)(ms_MuseSampler ms, ms_Track track, ms_PedalEvent_2 evt);

typedef int (* ms_MuseSampler_is_ranged_articulation)(ms_NoteArticulation);
typedef ms_Result (* ms_MuseSampler_add_track_event_range_start)(ms_MuseSampler, ms_Track, int voice, ms_NoteArticulation);
typedef ms_Result (* ms_MuseSampler_add_track_event_range_end)(ms_MuseSampler, ms_Track, int voice, ms_NoteArticulation);

// Added in 0.5
typedef ms_Result (* ms_MuseSampler_add_pitch_bend)(ms_MuseSampler ms, ms_Track track, ms_PitchBendInfo info);
typedef ms_Result (* ms_MuseSampler_add_vibrato)(ms_MuseSampler ms, ms_Track track, ms_VibratoInfo info);

// Added in 0.3
typedef ms_Result (* ms_MuseSampler_stop_audition_note)(ms_MuseSampler ms, ms_Track track, ms_AuditionStopNoteEvent evt);

// Added in 0.3
typedef ms_Result (* ms_MuseSampler_start_offline_mode)(ms_MuseSampler ms, double sample_rate);
typedef ms_Result (* ms_MuseSampler_stop_offline_mode)(ms_MuseSampler ms);
typedef ms_Result (* ms_MuseSampler_process_offline)(ms_MuseSampler ms, ms_OutputBuffer buffer);

typedef ms_Result (* ms_MuseSampler_process)(ms_MuseSampler, ms_OutputBuffer, long long samples);
typedef void (* ms_MuseSampler_set_position)(ms_MuseSampler, long long samples);
typedef void (* ms_MuseSampler_set_playing)(ms_MuseSampler, int playing);

typedef ms_Result (* ms_MuseSampler_all_notes_off)(ms_MuseSampler);

// Added in 0.6
typedef const char*(* ms_Instrument_get_pack_name)(ms_InstrumentInfo);
typedef const char*(* ms_Instrument_get_vendor_name)(ms_InstrumentInfo);
typedef const char*(* ms_Instrument_get_info_json)(ms_InstrumentInfo);

typedef ms_PresetChange (* ms_MuseSampler_create_preset_change)(ms_MuseSampler ms, ms_Track track, long long location_us);
typedef ms_Result (* ms_MuseSampler_add_preset)(ms_MuseSampler ms, ms_Track track, ms_PresetChange preset_change, const char* preset_name);

typedef const char*(* ms_get_text_articulations)(int instrument_id, const char* preset_name);
typedef ms_Result (* ms_MuseSampler_add_track_text_articulation_event)(ms_MuseSampler ms, ms_Track track, ms_TextArticulationEvent evt);

typedef const char*(* ms_get_drum_mapping)(int instrument_id);
// ------------------------------------------------------------

// Added in 0.100
typedef ms_Result (* ms_reload_all_instruments)(); // Useful for sound developers
typedef ms_Result (* ms_MuseSampler_start_audition_note_4)(ms_MuseSampler ms, ms_Track track, ms_AuditionStartNoteEvent_4 evt);
typedef ms_Result (* ms_MuseSampler_add_track_syllable_event)(ms_MuseSampler ms, ms_Track track, ms_SyllableEvent evt);
// ------------------------------------------------------------

// Added in 0.101
typedef bool (* ms_MuseSampler_ready_to_play)(ms_MuseSampler ms);
// ------------------------------------------------------------

// Added in 0.102
enum ms_ErrorLevel : int16_t
{
    ms_ErrorLevel_Debug = 0,
    ms_ErrorLevel_Info = 1,
    ms_ErrorLevel_Warning = 2,
    ms_ErrorLevel_Error = 3,
};

typedef void (* ms_logging_callback)(ms_ErrorLevel level, const char* msg);
typedef void (* ms_set_logging_callback)(ms_logging_callback callback);

typedef bool (* ms_Instrument_is_online)(ms_InstrumentInfo);

typedef void (* ms_MuseSampler_set_score_id)(ms_MuseSampler ms, const char* score_id);

typedef struct ms_SyllableEvent2
{
    const char* _text;
    long long _position_us;
    bool _hyphened_to_next;
} ms_SyllableEvent2;

typedef ms_Result (* ms_MuseSampler_add_track_syllable_event_2)(ms_MuseSampler ms, ms_Track track, ms_SyllableEvent2 evt);

typedef enum ms_RenderingState
{
    ms_RenderingState_Rendering,
    ms_RenderingState_ErrorRendering,
    ms_RenderingState_ErrorNetwork,
    ms_RenderingState_ErrorFileIO,
    ms_RenderingState_ErrorTimeOut,
    ms_RenderingState_ErrorLimitReached,
    ms_RenderingState_OutOfRange,
} ms_RenderingState;

typedef struct ms_RenderRangeInfo
{
    long long _start_us;
    long long _end_us;
    ms_RenderingState _state;
} ms_RenderRangeInfo;

typedef struct ms_RenderRangeInfo2
{
    long long _start_us;
    long long _end_us;
    ms_RenderingState _state;
    const char* _error_message; // possibly null
} ms_RenderRangeInfo2;

typedef void* ms_RenderingRangeList;

typedef ms_RenderRangeInfo (* ms_RenderProgressInfo_get_next)(ms_RenderingRangeList range_list);

typedef void (* ms_MuseSampler_set_auto_render_interval)(ms_MuseSampler ms, double interval_seconds);
typedef void (* ms_MuseSampler_trigger_render)(ms_MuseSampler ms);
typedef void (* ms_MuseSampler_clear_online_cache)(ms_MuseSampler ms);

typedef ms_Result (* ms_MuseSampler_add_audition_cc_event)(ms_MuseSampler ms, ms_Track track, int cc, float value);

typedef ms_Result (* ms_MuseSampler_add_track_note_event_6)(ms_MuseSampler ms, ms_Track track, ms_NoteEvent_5 evt, long long& event_id);
typedef ms_Result (* ms_MuseSampler_start_audition_note_5)(ms_MuseSampler ms, ms_Track track, ms_AuditionStartNoteEvent_5 evt);
// ------------------------------------------------------------

// Added in 0.103
typedef void (* ms_rendering_state_changed_callback)(void* user_data, ms_RenderingRangeList list, int num_ranges);
typedef void (* ms_MuseSampler_set_rendering_state_changed_callback)(ms_MuseSampler ms, ms_rendering_state_changed_callback callback,
                                                                     void* user_data);
// ------------------------------------------------------------

// added in v0.104
typedef ms_RenderRangeInfo2 (* ms_RenderProgressInfo2_get_next)(ms_RenderingRangeList range_list);
// ------------------------------------------------------------

// added in v0.105
typedef ms_Result (* ms_init_2)();
typedef ms_Result (* ms_deinit)();

typedef void (* ms_MuseSampler_set_lazy_render)(ms_MuseSampler ms, bool enabled);
typedef void (* ms_MuseSampler_set_rendering_state_changed_callback_2)(ms_MuseSampler ms, ms_rendering_state_changed_callback callback,
                                                                       void* user_data);
// ------------------------------------------------------------

namespace muse::musesampler {
using track_idx_t = size_t;
using TrackList = std::vector<ms_Track>;

struct InstrumentInfo {
    int instrumentId = -1;
    ms_InstrumentInfo msInstrument = nullptr;
    bool isOnline = false;

    bool isValid() const
    {
        return instrumentId != -1 && msInstrument != nullptr;
    }
};

struct AuditionStartNoteEvent {
    ms_AuditionStartNoteEvent_5 msEvent;
    ms_Track msTrack = nullptr;
};

struct AuditionStopNoteEvent {
    ms_AuditionStopNoteEvent msEvent;
    ms_Track msTrack = nullptr;
};

struct AuditionCCEvent {
    int cc = 0;
    float value = 0.f;
    ms_Track msTrack = nullptr;
};

using DynamicEvent = ms_DynamicsEvent_2;
using PedalEvent = ms_PedalEvent_2;
using NoteEvent = ms_NoteEvent_5;
using SyllableEvent = ms_SyllableEvent2;
using RenderRangeInfo = ms_RenderRangeInfo2;
}
