#pragma once

#include "fl/audio/audio_detector.h"
#include "fl/stl/function.h"
#include "fl/stl/shared_ptr.h"
#include "fl/stl/noexcept.h"

namespace fl {
namespace audio {
namespace detector {

/**
 * Note - Musical note detection with MIDI output
 *
 * Converts continuous pitch (from Pitch) to discrete musical notes
 * using 12-tone equal temperament (12-TET). Provides note-on/note-off events
 * with velocity information based on attack strength.
 *
 * Key Features:
 * - Converts Hz to MIDI note numbers (A4 = 440 Hz = MIDI 69)
 * - Note-on/note-off events with hysteresis for stability
 * - Velocity calculation based on attack strength
 * - Configurable note-on/note-off thresholds
 * - Integration with Pitch for fundamental frequency tracking
 * - Support for polyphonic detection (future enhancement)
 *
 * Performance:
 * - Depends on Pitch (no FFT required)
 * - Update time: ~0.05-0.1ms per frame (lightweight decision logic)
 * - Memory: ~100 bytes
 */
class Note : public Detector {
public:
    Note() FL_NOEXCEPT;
    explicit Note(shared_ptr<Pitch> pitchDetector);
    ~Note() FL_NOEXCEPT override;

    void update(shared_ptr<Context> context) override;
    void fireCallbacks() override;
    bool needsFFT() const override { return false; }  // Uses pitch detection
    const char* getName() const override { return "Note"; }
    void reset() override;

    // Callbacks (multiple listeners supported)
    function_list<void(u8 note, u8 velocity)> onNoteOn;   // Note started
    function_list<void(u8 note)> onNoteOff;           // Note ended
    function_list<void(u8 note, u8 velocity)> onNoteChange; // Note changed while held

    // State access
    u8 getCurrentNote() const { return mCurrentNote; }
    u8 getLastVelocity() const { return mLastVelocity; }
    bool isNoteActive() const { return mNoteActive; }
    float getCurrentPitch() const { return mCurrentPitch; }
    float getPitchBend() const { return mPitchBend; }  // Cents from note center

    // Configuration
    void setNoteOnThreshold(float confidenceThreshold) { mNoteOnThreshold = confidenceThreshold; }
    void setNoteOffThreshold(float confidenceThreshold) { mNoteOffThreshold = confidenceThreshold; }
    void setMinNoteDuration(u32 ms) { mMinNoteDuration = ms; }
    void setNoteChangeThreshold(u8 semitones) { mNoteChangeThreshold = semitones; }
    void setVelocitySensitivity(float sensitivity) { mVelocitySensitivity = sensitivity; }

    // Shared pitch detector access
    void setPitchDetector(shared_ptr<Pitch> pitchDetector) { mPitchDetector = pitchDetector; }
    shared_ptr<Pitch> getPitchDetector() const { return mPitchDetector; }

private:
    // Shared pitch detector (may be shared with Processor)
    shared_ptr<Pitch> mPitchDetector;
    bool mOwnsPitchDetector;  // True if we created our own instance

    // Current state
    u8 mCurrentNote;     // Current MIDI note (0-127, 255 = none)
    u8 mLastVelocity;    // Velocity of last note-on event (0-127)
    bool mNoteActive;         // True if note is currently held
    float mCurrentPitch;      // Current pitch in Hz
    float mPitchBend;         // Cents deviation from note center (-50 to +50)
    float mNoteOnEnergy;      // Energy level at note onset

    // Timing
    u32 mNoteOnTime;     // Timestamp of note-on event
    u32 mLastUpdateTime; // Timestamp of last update

    // Configuration
    float mNoteOnThreshold;   // Confidence threshold for note-on (default: 0.6)
    float mNoteOffThreshold;  // Confidence threshold for note-off (default: 0.4)
    u32 mMinNoteDuration;// Minimum note duration to prevent flicker (ms)
    u8 mNoteChangeThreshold; // Semitone threshold for note change event
    float mVelocitySensitivity;   // Velocity calculation sensitivity

    bool mFireNoteOn = false;
    bool mFireNoteOff = false;
    bool mFireNoteChange = false;
    u8 mPendingOnNote = 0;
    u8 mPendingOnVelocity = 0;
    u8 mPendingOffNote = 0;

    // Helper methods
    u8 frequencyToMidiNote(float hz) const;
    float midiNoteToFrequency(u8 note) const;
    float calculatePitchBend(float hz, u8 note) const;
    u8 calculateVelocity(float energy, float confidence) const;
    bool shouldTriggerNoteOn(float confidence, float pitch) const;
    bool shouldTriggerNoteOff(float confidence, bool voiced) const;
    bool shouldTriggerNoteChange(u8 newNote, u8 currentNote) const;

    // Constants
    static constexpr float A4_FREQUENCY = 440.0f;  // A4 reference pitch
    static constexpr u8 A4_MIDI_NOTE = 69;    // A4 MIDI note number
    static constexpr u8 NO_NOTE = 255;        // Sentinel for no active note
};

} // namespace detector
} // namespace audio
} // namespace fl
