package aac

import (
	"encoding/hex"

	"github.com/AlexxIT/go2rtc/pkg/bits"
	"github.com/AlexxIT/go2rtc/pkg/core"
	"github.com/pion/rtp"
)

const ADTSHeaderSize = 7

func ADTSHeaderLen(b []byte) int {
	if HasCRC(b) {
		return 9 // 7 bytes header + 2 bytes CRC
	}
	return ADTSHeaderSize
}

func IsADTS(b []byte) bool {
	// AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ)
	// A	12	Syncword, all bits must be set to 1.
	// C	2	Layer, always set to 0.
	return len(b) >= ADTSHeaderSize && b[0] == 0xFF && b[1]&0b1111_0110 == 0xF0
}

func HasCRC(b []byte) bool {
	// AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ)
	// D	1	Protection absence, set to 1 if there is no CRC and 0 if there is CRC.
	return b[1]&0b1 == 0
}

func ADTSToCodec(b []byte) *core.Codec {
	// 1. Check ADTS header
	if !IsADTS(b) {
		return nil
	}

	// 2. Decode ADTS params
	// https://wiki.multimedia.cx/index.php/ADTS
	rd := bits.NewReader(b)
	_ = rd.ReadBits(12)              // Syncword, all bits must be set to 1
	_ = rd.ReadBit()                 // MPEG Version, set to 0 for MPEG-4 and 1 for MPEG-2
	_ = rd.ReadBits(2)               // Layer, always set to 0
	_ = rd.ReadBit()                 // Protection absence, set to 1 if there is no CRC and 0 if there is CRC
	objType := rd.ReadBits8(2) + 1   // Profile, the MPEG-4 Audio Object Type minus 1
	sampleRateIdx := rd.ReadBits8(4) // MPEG-4 Sampling Frequency Index
	_ = rd.ReadBit()                 // Private bit, guaranteed never to be used by MPEG, set to 0 when encoding, ignore when decoding
	channels := rd.ReadBits8(3)      // MPEG-4 Channel Configuration

	//_ = rd.ReadBit()    // Originality, set to 1 to signal originality of the audio and 0 otherwise
	//_ = rd.ReadBit()    // Home, set to 1 to signal home usage of the audio and 0 otherwise
	//_ = rd.ReadBit()    // Copyright ID bit
	//_ = rd.ReadBit()    // Copyright ID start
	//_ = rd.ReadBits(13) // Frame length
	//_ = rd.ReadBits(11) // Buffer fullness
	//_ = rd.ReadBits(2)  // Number of AAC frames (Raw Data Blocks) in ADTS frame minus 1
	//_ = rd.ReadBits(16) // CRC check

	// 3. Encode RTP config
	wr := bits.NewWriter(nil)
	wr.WriteBits8(objType, 5)
	wr.WriteBits8(sampleRateIdx, 4)
	wr.WriteBits8(channels, 4)
	conf := wr.Bytes()

	codec := &core.Codec{
		Name:      core.CodecAAC,
		ClockRate: sampleRates[sampleRateIdx],
		Channels:  channels,
		FmtpLine:  FMTP + hex.EncodeToString(conf),
	}
	return codec
}

func ReadADTSSize(b []byte) uint16 {
	// AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ)
	_ = b[5] // bounds
	return uint16(b[3]&0b11)<<11 | uint16(b[4])<<3 | uint16(b[5]>>5)
}

func WriteADTSSize(b []byte, size uint16) {
	// AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ)
	_ = b[5] // bounds
	b[3] |= byte(size >> (8 + 3))
	b[4] = byte(size >> 3)
	b[5] |= byte(size << 5)
	return
}

func ADTSTimeSize(b []byte) uint32 {
	var units uint32
	for len(b) > ADTSHeaderSize {
		auSize := ReadADTSSize(b)
		b = b[auSize:]
		units++
	}
	return units * AUTime
}

func CodecToADTS(codec *core.Codec) []byte {
	s := core.Between(codec.FmtpLine, "config=", ";")
	conf, err := hex.DecodeString(s)
	if err != nil {
		return nil
	}

	objType, sampleFreqIdx, channels, _ := DecodeConfig(conf)
	profile := objType - 1

	wr := bits.NewWriter(nil)
	wr.WriteAllBits(1, 12)          // Syncword, all bits must be set to 1
	wr.WriteBit(0)                  // MPEG Version, set to 0 for MPEG-4 and 1 for MPEG-2
	wr.WriteBits8(0, 2)             // Layer, always set to 0
	wr.WriteBit(1)                  // Protection absence, set to 1 if there is no CRC and 0 if there is CRC
	wr.WriteBits8(profile, 2)       // Profile, the MPEG-4 Audio Object Type minus 1
	wr.WriteBits8(sampleFreqIdx, 4) // MPEG-4 Sampling Frequency Index
	wr.WriteBit(0)                  // Private bit, guaranteed never to be used by MPEG, set to 0 when encoding, ignore when decoding
	wr.WriteBits8(channels, 3)      // MPEG-4 Channel Configuration
	wr.WriteBit(0)                  // Originality, set to 1 to signal originality of the audio and 0 otherwise
	wr.WriteBit(0)                  // Home, set to 1 to signal home usage of the audio and 0 otherwise
	wr.WriteBit(0)                  // Copyright ID bit
	wr.WriteBit(0)                  // Copyright ID start
	wr.WriteBits16(0, 13)           // Frame length
	wr.WriteAllBits(1, 11)          // Buffer fullness (variable bitrate)
	wr.WriteBits8(0, 2)             // Number of AAC frames (Raw Data Blocks) in ADTS frame minus 1

	return wr.Bytes()
}

func EncodeToADTS(codec *core.Codec, handler core.HandlerFunc) core.HandlerFunc {
	adts := CodecToADTS(codec)

	return func(packet *rtp.Packet) {
		if !IsADTS(packet.Payload) {
			b := make([]byte, ADTSHeaderSize+len(packet.Payload))
			copy(b, adts)
			copy(b[ADTSHeaderSize:], packet.Payload)
			WriteADTSSize(b, uint16(len(b)))

			clone := *packet
			clone.Payload = b
			handler(&clone)
		} else {
			handler(packet)
		}
	}
}
