package mpeg4audio

import (
	"fmt"

	"github.com/bluenviron/mediacommon/v2/pkg/bits"
)

// Config is an alias for AudioSpecificConfig.
//
// Deprecated: replaced by AudioSpecificConfig.
type Config = AudioSpecificConfig

// AudioSpecificConfig is an AudioSpecificConfig.
// Specification: ISO 14496-3, 1.6.2.1
type AudioSpecificConfig struct {
	Type         ObjectType
	SampleRate   int
	ChannelCount int

	// SBR / PS specific
	ExtensionType       ObjectType
	ExtensionSampleRate int

	// GASpecificConfig
	FrameLengthFlag    bool
	DependsOnCoreCoder bool
	CoreCoderDelay     uint16
}

// Unmarshal decodes a AudioSpecificConfig.
func (c *AudioSpecificConfig) Unmarshal(buf []byte) error {
	pos := 0
	err := c.unmarshalBits(buf, &pos)
	if err != nil {
		return err
	}

	return nil
}

// UnmarshalFromPos decodes a AudioSpecificConfig.
//
// Deprecated: not meant to be public.
func (c *AudioSpecificConfig) UnmarshalFromPos(buf []byte, pos *int) error {
	return c.unmarshalBits(buf, pos)
}

// unmarshalBits decodes a AudioSpecificConfig.
func (c *AudioSpecificConfig) unmarshalBits(buf []byte, pos *int) error {
	tmp, err := bits.ReadBits(buf, pos, 5)
	if err != nil {
		return err
	}
	c.Type = ObjectType(tmp)

	switch c.Type {
	case ObjectTypeAACLC, ObjectTypeSBR, ObjectTypePS:
	default:
		return fmt.Errorf("unsupported object type: %d", c.Type)
	}

	sampleRateIndex, err := bits.ReadBits(buf, pos, 4)
	if err != nil {
		return err
	}

	switch {
	case sampleRateIndex <= 12:
		c.SampleRate = sampleRates[sampleRateIndex]

	case sampleRateIndex == 0x0F:
		tmp, err = bits.ReadBits(buf, pos, 24)
		if err != nil {
			return err
		}
		c.SampleRate = int(tmp)

	default:
		return fmt.Errorf("invalid sample rate index (%d)", sampleRateIndex)
	}

	channelConfig, err := bits.ReadBits(buf, pos, 4)
	if err != nil {
		return err
	}

	switch {
	case channelConfig == 0:
		return fmt.Errorf("not yet supported")

	case channelConfig >= 1 && channelConfig <= 6:
		c.ChannelCount = int(channelConfig)

	case channelConfig == 7:
		c.ChannelCount = 8

	default:
		return fmt.Errorf("invalid channel configuration (%d)", channelConfig)
	}

	if c.Type == ObjectTypeSBR || c.Type == ObjectTypePS {
		c.ExtensionType = c.Type

		var extensionSamplingFrequencyIndex uint64
		extensionSamplingFrequencyIndex, err = bits.ReadBits(buf, pos, 4)
		if err != nil {
			return err
		}

		switch {
		case extensionSamplingFrequencyIndex <= 12:
			c.ExtensionSampleRate = sampleRates[extensionSamplingFrequencyIndex]

		case extensionSamplingFrequencyIndex == 0x0F:
			tmp, err = bits.ReadBits(buf, pos, 24)
			if err != nil {
				return err
			}
			c.ExtensionSampleRate = int(tmp)

		default:
			return fmt.Errorf("invalid extension sample rate index: %d", extensionSamplingFrequencyIndex)
		}

		tmp, err = bits.ReadBits(buf, pos, 5)
		if err != nil {
			return err
		}
		c.Type = ObjectType(tmp)

		if c.Type != ObjectTypeAACLC {
			return fmt.Errorf("unsupported object type: %d", c.Type)
		}
	}

	// GASpecificConfig

	c.FrameLengthFlag, err = bits.ReadFlag(buf, pos)
	if err != nil {
		return err
	}

	c.DependsOnCoreCoder, err = bits.ReadFlag(buf, pos)
	if err != nil {
		return err
	}

	if c.DependsOnCoreCoder {
		tmp, err = bits.ReadBits(buf, pos, 14)
		if err != nil {
			return err
		}
		c.CoreCoderDelay = uint16(tmp)
	}

	extensionFlag, err := bits.ReadFlag(buf, pos)
	if err != nil {
		return err
	}

	if extensionFlag {
		return fmt.Errorf("extensionFlag is unsupported")
	}

	return nil
}

func (c AudioSpecificConfig) marshalSizeBits() int {
	n := 5 + 4 + 2 + 1

	_, ok := reverseSampleRates[c.SampleRate]
	if !ok {
		n += 28
	} else {
		n += 4
	}

	if c.ExtensionType == ObjectTypeSBR || c.ExtensionType == ObjectTypePS {
		_, ok = reverseSampleRates[c.ExtensionSampleRate]
		if !ok {
			n += 28
		} else {
			n += 4
		}
		n += 5
	}

	if c.DependsOnCoreCoder {
		n += 14
	}

	return n
}

func (c AudioSpecificConfig) marshalSize() int {
	n := c.marshalSizeBits()

	ret := n / 8
	if (n % 8) != 0 {
		ret++
	}

	return ret
}

// Marshal encodes a AudioSpecificConfig.
func (c AudioSpecificConfig) Marshal() ([]byte, error) {
	buf := make([]byte, c.marshalSize())
	pos := 0

	err := c.marshalToBits(buf, &pos)
	if err != nil {
		return nil, err
	}

	return buf, nil
}

func (c AudioSpecificConfig) marshalToBits(buf []byte, pos *int) error {
	if c.ExtensionType == ObjectTypeSBR || c.ExtensionType == ObjectTypePS {
		bits.WriteBitsUnsafe(buf, pos, uint64(c.ExtensionType), 5)
	} else {
		bits.WriteBitsUnsafe(buf, pos, uint64(c.Type), 5)
	}

	sampleRateIndex, ok := reverseSampleRates[c.SampleRate]
	if !ok {
		bits.WriteBitsUnsafe(buf, pos, uint64(15), 4)
		bits.WriteBitsUnsafe(buf, pos, uint64(c.SampleRate), 24)
	} else {
		bits.WriteBitsUnsafe(buf, pos, uint64(sampleRateIndex), 4)
	}

	var channelConfig int
	switch {
	case c.ChannelCount >= 1 && c.ChannelCount <= 6:
		channelConfig = c.ChannelCount

	case c.ChannelCount == 8:
		channelConfig = 7

	default:
		return fmt.Errorf("invalid channel count (%d)", c.ChannelCount)
	}
	bits.WriteBitsUnsafe(buf, pos, uint64(channelConfig), 4)

	if c.ExtensionType == ObjectTypeSBR || c.ExtensionType == ObjectTypePS {
		sampleRateIndex, ok = reverseSampleRates[c.ExtensionSampleRate]
		if !ok {
			bits.WriteBitsUnsafe(buf, pos, uint64(0x0F), 4)
			bits.WriteBitsUnsafe(buf, pos, uint64(c.ExtensionSampleRate), 24)
		} else {
			bits.WriteBitsUnsafe(buf, pos, uint64(sampleRateIndex), 4)
		}
		bits.WriteBitsUnsafe(buf, pos, uint64(c.Type), 5)
	}

	bits.WriteFlagUnsafe(buf, pos, c.FrameLengthFlag)
	bits.WriteFlagUnsafe(buf, pos, c.DependsOnCoreCoder)

	if c.DependsOnCoreCoder {
		bits.WriteBitsUnsafe(buf, pos, uint64(c.CoreCoderDelay), 14)
	}

	*pos++ // extensionFlag

	return nil
}
