#ifndef MT_AMRCODEC_H
#define MT_AMRCODEC_H

#include "../engine_config.h"
#include <map>
#include <span>

#include "MT_Codec.h"
#include "../helper/HL_Pointer.h"

# include "opencore-amr/amrnb/interf_enc.h"
# include "opencore-amr/amrnb/interf_dec.h"

# include "opencore-amr/amrwb/if_rom.h"
# include "opencore-amr/amrwb/dec_if.h"

namespace MT
{
struct AmrCodecConfig
{
    bool    mIuUP = false;
    bool    mOctetAligned = false;
    int     mPayloadType = -1;
};

class AmrNbCodec : public Codec
{
protected:
    void* mEncoderCtx;
    void* mDecoderCtx;
    AmrCodecConfig mConfig;
    unsigned mCurrentDecoderTimestamp;
    int mSwitchCounter;
    int mPreviousPacketLength;

public:
    class CodecFactory: public Factory
    {
    public:
        CodecFactory(const AmrCodecConfig& config);

        const char* name() override;
        int samplerate() override;
        int payloadType() override;

        void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
        int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
        void create(CodecMap& codecs) override;

        PCodec create() override;

    protected:
        AmrCodecConfig mConfig;
    };

    AmrNbCodec(const AmrCodecConfig& config);

    virtual ~AmrNbCodec();
    const char* name() override;
    int pcmLength() override;
    int rtpLength() override;
    int frameTime() override;
    int samplerate() override;
    int encode(const void* input, int inputBytes, void* output, int outputCapacity) override;
    int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
    int plc(int lostFrames, void* output, int outputCapacity) override;
    int getSwitchCounter() const;
};

struct AmrWbStatistics
{
    int mDiscarded = 0;
    int mNonParsed = 0;
};
extern AmrWbStatistics GAmrWbStatistics;

class AmrWbCodec : public Codec
{
protected:
    void* mEncoderCtx;
    void* mDecoderCtx;
    AmrCodecConfig mConfig;
    uint64_t mCurrentDecoderTimestamp;
    int mSwitchCounter;
    int mPreviousPacketLength;

    int decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> output);
    int decodePlain(std::span<const uint8_t> input, std::span<uint8_t> output);

public:
    class CodecFactory: public Factory
    {
    public:
        CodecFactory(const AmrCodecConfig& config);

        const char* name() override;
        int samplerate() override;
        int payloadType() override;

        void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
        int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
        void create(CodecMap& codecs) override;

        PCodec create() override;

    protected:
        AmrCodecConfig mConfig;
    };

    AmrWbCodec(const AmrCodecConfig& config);
    virtual ~AmrWbCodec();

    const char* name() override;
    int pcmLength() override;
    int rtpLength() override;
    int frameTime() override;
    int samplerate() override;
    int encode(const void* input, int inputBytes, void* output, int outputCapacity) override;
    int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
    int plc(int lostFrames, void* output, int outputCapacity) override;
    int getSwitchCounter() const;
};

class GsmEfrCodec : public Codec
{
protected:
    void* mEncoderCtx;
    void* mDecoderCtx;
    bool mIuUP;

public:
    class GsmEfrFactory: public Factory
    {
    public:
        GsmEfrFactory(bool iuup, int ptype);

        const char* name() override;
        int samplerate() override;
        int payloadType() override;

        void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
        int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
        void create(CodecMap& codecs) override;

        PCodec create() override;

    protected:
        bool mIuUP;
        int mPayloadType;
    };

    GsmEfrCodec(bool iuup = false);

    virtual ~GsmEfrCodec();
    const char* name() override;
    int pcmLength() override;
    int rtpLength() override;
    int frameTime() override;
    int samplerate() override;
    int encode(const void* input, int inputBytes, void* output, int outputCapacity) override;
    int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
    int plc(int lostFrames, void* output, int outputCapacity) override;
};

} // End of MT namespace


#endif // MT_AMRCODEC_H

