namespace CUETools.Codecs
{
    public class AudioPCMConfig
    {
        public static readonly AudioPCMConfig RedBook = new AudioPCMConfig(16, 2, 44100);
        public enum SpeakerConfig
        {
            SPEAKER_FRONT_LEFT = 0x1,
            SPEAKER_FRONT_RIGHT = 0x2,
            SPEAKER_FRONT_CENTER = 0x4,
            SPEAKER_LOW_FREQUENCY = 0x8,
            SPEAKER_BACK_LEFT = 0x10,
            SPEAKER_BACK_RIGHT = 0x20,
            SPEAKER_FRONT_LEFT_OF_CENTER = 0x40,
            SPEAKER_FRONT_RIGHT_OF_CENTER = 0x80,
            SPEAKER_BACK_CENTER = 0x100,
            SPEAKER_SIDE_LEFT = 0x200,
            SPEAKER_SIDE_RIGHT = 0x400,
            SPEAKER_TOP_CENTER = 0x800,
            SPEAKER_TOP_FRONT_LEFT = 0x1000,
            SPEAKER_TOP_FRONT_CENTER = 0x2000,
            SPEAKER_TOP_FRONT_RIGHT = 0x4000,
            SPEAKER_TOP_BACK_LEFT = 0x8000,
            SPEAKER_TOP_BACK_CENTER = 0x10000,
            SPEAKER_TOP_BACK_RIGHT = 0x20000,

            DIRECTOUT = 0,
            KSAUDIO_SPEAKER_MONO = (SPEAKER_FRONT_CENTER),
            KSAUDIO_SPEAKER_STEREO = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT),
            KSAUDIO_SPEAKER_QUAD = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT),
            KSAUDIO_SPEAKER_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER),
            KSAUDIO_SPEAKER_5POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT),
            KSAUDIO_SPEAKER_5POINT1_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT),
            KSAUDIO_SPEAKER_7POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER),
            KSAUDIO_SPEAKER_7POINT1_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT),

            DVDAUDIO_GR1_0 = SPEAKER_FRONT_CENTER,
            DVDAUDIO_GR1_1 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
            DVDAUDIO_GR1_2 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
            DVDAUDIO_GR1_3 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
            DVDAUDIO_GR1_4 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
            DVDAUDIO_GR1_5 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
            DVDAUDIO_GR1_6 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
            DVDAUDIO_GR1_7 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
            DVDAUDIO_GR1_8 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
            DVDAUDIO_GR1_9 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
            DVDAUDIO_GR1_10 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
            DVDAUDIO_GR1_11 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
            DVDAUDIO_GR1_12 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
            DVDAUDIO_GR1_13 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
            DVDAUDIO_GR1_14 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
            DVDAUDIO_GR1_15 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
            DVDAUDIO_GR1_16 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
            DVDAUDIO_GR1_17 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
            DVDAUDIO_GR1_18 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
            DVDAUDIO_GR1_19 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
            DVDAUDIO_GR1_20 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,

            DVDAUDIO_GR2_0 = 0,
            DVDAUDIO_GR2_1 = 0,
            DVDAUDIO_GR2_2 = SPEAKER_BACK_CENTER,
            DVDAUDIO_GR2_3 = SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
            DVDAUDIO_GR2_4 = SPEAKER_LOW_FREQUENCY,
            DVDAUDIO_GR2_5 = SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_CENTER,
            DVDAUDIO_GR2_6 = SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
            DVDAUDIO_GR2_7 = SPEAKER_FRONT_CENTER,
            DVDAUDIO_GR2_8 = SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER,
            DVDAUDIO_GR2_9 = SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
            DVDAUDIO_GR2_10 = SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY,
            DVDAUDIO_GR2_11 = SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_CENTER,
            DVDAUDIO_GR2_12 = SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
            DVDAUDIO_GR2_13 = SPEAKER_BACK_CENTER,
            DVDAUDIO_GR2_14 = SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
            DVDAUDIO_GR2_15 = SPEAKER_LOW_FREQUENCY,
            DVDAUDIO_GR2_16 = SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_CENTER,
            DVDAUDIO_GR2_17 = SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
            DVDAUDIO_GR2_18 = SPEAKER_LOW_FREQUENCY,
            DVDAUDIO_GR2_19 = SPEAKER_FRONT_CENTER,
            DVDAUDIO_GR2_20 = SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY,
        }

        private int _bitsPerSample;
        private int _channelCount;
        private int _sampleRate;
        private SpeakerConfig _channelMask;

        public int BitsPerSample { get { return _bitsPerSample; } }
        public int ChannelCount { get { return _channelCount; } }
        public int SampleRate { get { return _sampleRate; } }
        public int BlockAlign { get { return _channelCount * ((_bitsPerSample + 7) / 8); } }
        public SpeakerConfig ChannelMask { get { return _channelMask; } }
        public bool IsRedBook { get { return _bitsPerSample == 16 && _channelCount == 2 && _sampleRate == 44100; } }
        public static int ChannelsInMask(SpeakerConfig mask)
        {
            int count = 0;
            while (mask != 0)
            {
                count++;
                mask &= (mask - 1);
            }
            return count;
        }

        public static SpeakerConfig GetDefaultChannelMask(int channelCount)
        {
            switch (channelCount)
            {
                case 1:
                    return SpeakerConfig.KSAUDIO_SPEAKER_MONO;
                case 2:
                    return SpeakerConfig.KSAUDIO_SPEAKER_STEREO;
                case 3:
                    return SpeakerConfig.KSAUDIO_SPEAKER_STEREO | SpeakerConfig.SPEAKER_LOW_FREQUENCY;
                case 4:
                    return SpeakerConfig.KSAUDIO_SPEAKER_QUAD;
                case 5:
                    //return SpeakerConfig.KSAUDIO_SPEAKER_5POINT1 & ~SpeakerConfig.SPEAKER_LOW_FREQUENCY;
                    return SpeakerConfig.KSAUDIO_SPEAKER_5POINT1_SURROUND & ~SpeakerConfig.SPEAKER_LOW_FREQUENCY;
                case 6:
                    //return SpeakerConfig.KSAUDIO_SPEAKER_5POINT1;
                    return SpeakerConfig.KSAUDIO_SPEAKER_5POINT1_SURROUND;
                case 7:
                    return SpeakerConfig.KSAUDIO_SPEAKER_5POINT1_SURROUND | SpeakerConfig.SPEAKER_BACK_CENTER;
                case 8:
                    return SpeakerConfig.KSAUDIO_SPEAKER_7POINT1_SURROUND; 
            }
            return (SpeakerConfig)((1 << channelCount) - 1);
        }

        public AudioPCMConfig(int bitsPerSample, int channelCount, int sampleRate, SpeakerConfig channelMask = SpeakerConfig.DIRECTOUT)
        {
            _bitsPerSample = bitsPerSample;
            _channelCount = channelCount;
            _sampleRate = sampleRate;
            _channelMask = channelMask == 0 ? GetDefaultChannelMask(channelCount) : channelMask;
        }
    }
}
