/*
 * vdr-podcatcher - A plugin for the Linux Video Disk Recorder
 * Copyright (c) 2006 Tobias Grimm <vdr@e-tobi.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id: mpaframeheader_test.cc 3211 2006-04-14 21:01:37Z svntobi $
 *
 */

#include <cxxtest/TestSuite.h>
#include "mpaframeheader.h"

#define MPEG_1 3
#define MPEG_2 2

#define MODE_STEREO       0
#define MODE_JOINT_STEREO 1
#define MODE_TWO_MONO     2
#define MODE_ONE_MONO     3

class MpaFrameHeaderTest: public CxxTest::TestSuite
{
    private:
        void SetHeader(MpaHeaderData header, int mpegVersion, int layer,
          int bitRateIndex, int samplingRateIndex, int padding, int channelMode)
        {
            header[0] = 0xFF;
            header[1] = 0xE0;

            header[1] |= mpegVersion << 3;

            switch (layer)
            {
                case 1:
                    header[1] |= 0x06;
                    break;
                case 2:
                    header[1] |= 0x04;
                    break;
                case 3:
                    header[1] |= 0x02;
                    break;
            }

            header[2] = bitRateIndex << 4 | samplingRateIndex << 2 | padding << 1;
            header[3] = channelMode << 6;
        }

        void TestBitRates(int mpegVersion, int layer, int expectedKbitRates[15])
        {
            MpaHeaderData header;

            for (int bitRateIndex = 0; bitRateIndex < 16; bitRateIndex++)
            {
                SetHeader(header, mpegVersion, layer, bitRateIndex, 0, 0, 0);
                TS_ASSERT_EQUALS(expectedKbitRates[bitRateIndex] * 1000,
                  MpaFrameHeader(header).GetBitRate());
            }
        }

        void TestSamplingRates(int mpegVersion, int expectedSamplingRates[4])
        {
            MpaHeaderData header;

            for (int samplingRateIndex = 0; samplingRateIndex < 4;
              samplingRateIndex++)
            {
                SetHeader(header, mpegVersion, 1, 0, samplingRateIndex, 0, 0);
                TS_ASSERT_EQUALS(expectedSamplingRates[samplingRateIndex],
                  MpaFrameHeader(header).GetSamplingRate());
            }
        }

    public:
        void TestSamplingRatesMpeg1()
        {
            int expectedSamplingRates[4] = {44100, 48000, 32000, 0};
            TestSamplingRates(MPEG_1, expectedSamplingRates);
        }

        void TestSamplingRatesMpeg2()
        {
            int expectedSamplingRates[4] = {22050, 24000, 16000, 0};
            TestSamplingRates(MPEG_2, expectedSamplingRates);
        }

        void TestSamplingRatesOfUnsupportedMpegVersions()
        {
            int expectedSamplingRates[4] = {0, 0, 0, 0};

            TestSamplingRates(0, expectedSamplingRates);
            TestSamplingRates(1, expectedSamplingRates);
        }

        void TestBitRatesMpeg1Layer1()
        {
            int expectedKbitRates[16] = {0, 32, 64, 96, 128, 160, 192, 224, 256, 288,
              320, 352, 384, 416, 448, 0};
            TestBitRates(MPEG_1, 1, expectedKbitRates);
        }

        void TestBitRatesMpeg1Layer2()
        {
            int expectedKbitRates[16] = {0, 32, 48, 56, 64, 80, 96, 112, 128, 160,
              192, 224, 256, 320, 384, 0};
            TestBitRates(MPEG_1, 2, expectedKbitRates);
        }

        void TestBitRatesMpeg1Layer3()
        {
            int expectedKbitRates[16] = {0, 32, 40, 48, 56, 64, 80, 96, 112, 128,
              160, 192, 224, 256, 320, 0};
            TestBitRates(MPEG_1, 3, expectedKbitRates);
        }

        void TestBitRatesMpeg2Layer1()
        {
            int expectedKbitRates[16] = {0, 32, 48, 56, 64, 80, 96, 112, 128, 144,
              160, 176, 192, 224, 256, 0};
            TestBitRates(MPEG_2, 1, expectedKbitRates);
        }

        void TestBitRatesMpeg2Layer2AndLayer3()
        {
            int expectedKbitRates[16] = {0, 8, 16, 24, 32, 40, 48, 56, 64, 80,
              96, 112, 128, 144, 160, 0};
            TestBitRates(MPEG_2, 2, expectedKbitRates);
            TestBitRates(MPEG_2, 3, expectedKbitRates);
        }

        void TestBitRatesOfUnsupportedMpegVersionsAndLayers()
        {
            int expectedKbitRates[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
              0, 0, 0, 0, 0, 0};
            TestBitRates(0, 3, expectedKbitRates);
            TestBitRates(1, 3, expectedKbitRates);
            TestBitRates(MPEG_1, 0, expectedKbitRates);
        }

        void TestGetSideInformationSizeMpeg1()
        {
            MpaHeaderData header;

            SetHeader(header, MPEG_1, 3, 0, 0, 0, MODE_STEREO);
            TS_ASSERT_EQUALS(32, MpaFrameHeader(header).GetSideInformationSize());

            SetHeader(header, MPEG_1, 3, 0, 0, 0, MODE_JOINT_STEREO);
            TS_ASSERT_EQUALS(32, MpaFrameHeader(header).GetSideInformationSize());

            SetHeader(header, MPEG_1, 3, 0, 0, 0, MODE_TWO_MONO);
            TS_ASSERT_EQUALS(32, MpaFrameHeader(header).GetSideInformationSize());

            SetHeader(header, MPEG_1, 3, 0, 0, 0, MODE_ONE_MONO);
            TS_ASSERT_EQUALS(17, MpaFrameHeader(header).GetSideInformationSize());
        }


        void TestGetSideInformationSizeMpeg2()
        {
            MpaHeaderData header;

            SetHeader(header, MPEG_2, 3, 0, 0, 0, MODE_STEREO);
            TS_ASSERT_EQUALS(17, MpaFrameHeader(header).GetSideInformationSize());

            SetHeader(header, MPEG_2, 3, 0, 0, 0, MODE_JOINT_STEREO);
            TS_ASSERT_EQUALS(17, MpaFrameHeader(header).GetSideInformationSize());

            SetHeader(header, MPEG_2, 3, 0, 0, 0, MODE_TWO_MONO);
            TS_ASSERT_EQUALS(17, MpaFrameHeader(header).GetSideInformationSize());

            SetHeader(header, MPEG_2, 3, 0, 0, 0, MODE_ONE_MONO);
            TS_ASSERT_EQUALS(9, MpaFrameHeader(header).GetSideInformationSize());
        }

        void TestGetFrameSizeMpeg1Layer3()
        {
            MpaHeaderData header;

            // size = 1152 * 96000 / 8 / 44100 + 0 * 1
            SetHeader(header, MPEG_1, 3, 7, 0, 0, MODE_STEREO);
            TS_ASSERT_EQUALS(313, MpaFrameHeader(header).GetFrameSize());

            // size = 1152 * 96000 / 8 / 44100 + 1 * 1
            SetHeader(header, MPEG_1, 3, 7, 0, 1, MODE_STEREO);
            TS_ASSERT_EQUALS(314, MpaFrameHeader(header).GetFrameSize());
        }

        void TestGetFrameSizeRoundingChallenge()
        {
            MpaHeaderData header;

            // Frame Size must be rounded to slots, not bytes
            // size_wrong = int(384 * 448000 / 8 / 44100 + 1 * 4)
            //            = int(491.619)
            //            = 491
            //
            // size_right = int(384 * 448000 / 8 / 44100 / 4 + 1) * 4
            //            = int(122.905) * 4
            //            = 122 * 4
            //            = 488

            SetHeader(header, MPEG_1, 1, 14, 0, 1, MODE_STEREO);
            TS_ASSERT_EQUALS(488, MpaFrameHeader(header).GetFrameSize());
        }

        void TestGetFrameSizeMpeg2Layer3()
        {
            MpaHeaderData header;

            // size = 576 * 32000 / 8 / 22050 + 0 * 4
            SetHeader(header, MPEG_2, 3, 4, 0, 0, MODE_STEREO);
            TS_ASSERT_EQUALS(104, MpaFrameHeader(header).GetFrameSize());
        }

        void TestGetFrameSizeMpeg2Layer1()
        {
            MpaHeaderData header;

            // size = 384 * 96000 / 8 / 22050 + 0 * 4
            SetHeader(header, MPEG_2, 1, 6, 0, 0, MODE_STEREO);
            TS_ASSERT_EQUALS(208, MpaFrameHeader(header).GetFrameSize());

            // size = 384 * 96000 / 8 / 22050 + 1 * 4
            SetHeader(header, MPEG_2, 1, 6, 0, 1, MODE_STEREO);
            TS_ASSERT_EQUALS(212, MpaFrameHeader(header).GetFrameSize());
        }

        void TestGetFrameSizeUnsupportedMpegVersions()
        {
            MpaHeaderData header;

            SetHeader(header, 0, 1, 6, 0, 0, MODE_STEREO);
            TS_ASSERT_EQUALS(4, MpaFrameHeader(header).GetFrameSize());

            SetHeader(header, 1, 1, 6, 0, 1, MODE_STEREO);
            TS_ASSERT_EQUALS(4, MpaFrameHeader(header).GetFrameSize());

            SetHeader(header, MPEG_1, 0, 6, 0, 1, MODE_STEREO);
            TS_ASSERT_EQUALS(4, MpaFrameHeader(header).GetFrameSize());
        }
};
