using System;
using System.Text;

/*
    Copyright (c) 2010 flixmonk. (http://DVDSwarm.codeplex.com)
    All Rights Reserved.
        
    Redistribution and use in source and binary forms, with or without modification,
    are permitted provided that the following conditions are met:
        
    * Redistributions of source code must retain the above copyright notice, this list
      of conditions and the following disclaimer.
        
    * Redistributions in binary form must reproduce the above copyright notice, this
    list of conditions and the following disclaimer in the documentation and/or other
    materials provided with the distribution. 
        
    * Neither the name of The Flixmonk nor the names of its contributors may be used to
    endorse or promote products derived from this software without specific prior written
    permission.
        
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
    SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
    OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace DVDSwarm.Dvd
{
    /// <summary>
    /// Contains information about an audio stream. Member of the TitleSet class.
    /// </summary>
    sealed public class AudioStream
    {
        #region Fields

        private string _applicationInfo = string.Empty;
        private string _applicationMode = string.Empty;
        private Int32 _channelCount = 0;
        private string _codeExtension = string.Empty;
        private string _codingMode = string.Empty;
        private Int32 _codingModeValue = 0;
        private string _fileExtension = string.Empty;
        private string _language = Dvd.Utility.UnspecifiedString;
        private bool _multiChannelExtension = false;
        private string _quantization = string.Empty;
        private string _sampleRate = string.Empty;
        
        #endregion

        #region Properties

        /// <summary>
        /// Gets additional application info for the audio when ApplicationMode is
        /// 'Karaoke' or 'Surround', otherwise this property is empty.
        /// </summary>
        public string ApplicationInfo
        {
            get
            {
                return _applicationInfo;
            }
        }
        /// <summary>
        /// Gets the application mode for the audio.
        /// </summary>
        public string ApplicationMode
        {
            get
            {
                return _applicationMode;
            }
        }
        /// <summary>
        /// Gets the number of channels for the audio.
        /// </summary>
        public Int32 ChannelCount
        {
            get
            {
                return _channelCount;
            }
        }
        /// <summary>
        /// Gets the code extension for the audio.
        /// </summary>
        public string CodeExtension
        {
            get
            {
                return _codeExtension;
            }
        }
        /// <summary>
        /// Gets the coding mode for the audio.
        /// </summary>
        public string CodingMode
        {
            get
            {
                return _codingMode;
            }
        }
        /// <summary>
        /// Gets the coding mode value for the audio.
        /// </summary>
        public Int32 CodingModeValue
        {
            get
            {
                return _codingModeValue;
            }
        }
        /// <summary>
        /// Gets the associated file extension for the audio including the starting period.
        /// </summary>
        public string FileExtension
        {
            get
            {
                return _fileExtension;
            }
        }
        /// <summary>
        /// Gets the language for the audio.
        /// </summary>
        public string Language
        {
            get
            {
                return _language;
            }
        }
        /// <summary>
        /// Gets whether the multichannel extension is present for the audio.
        /// </summary>
        public bool MultiChannelExtension
        {
            get
            {
                return _multiChannelExtension;
            }
        }
        /// <summary>
        /// Gets the quantization/DRC for the audio.
        /// </summary>
        public string Quantization
        {
            get
            {
                return _quantization;
            }
        }
        /// <summary>
        /// Gets the sample rate for the audio.
        /// </summary>
        public string SampleRate
        {
            get
            {
                return _sampleRate;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Creats a new instance of the AudioStream class using the specified data.
        /// </summary>
        /// <param name="bytes">A byte array containing bytes to parse.</param>
        /// <param name="extendedBytes">A byte array containing multi-channel extension bytes to parse.</param>
        /// <exception cref="System.ArgumentNullException">Thrown when bytes is null.</exception>
        /// <exception cref="System.ArgumentException">Thrown when bytes length is not eight bytes.</exception>
        /// <exception cref="System.ArgumentNullException">Thrown when extendedBytes is null.</exception>
        /// <exception cref="System.ArgumentException">Thrown when extendedBytes length is not 24 bytes.</exception>
        public AudioStream(byte[] bytes, byte[] extendedBytes)
        {
            if (bytes == null)
            {
                throw new ArgumentNullException("bytes");
            }
            if (bytes.Length != 8)
            {
                throw new ArgumentException("bytes length must be eight bytes.");
            }
            if (extendedBytes == null)
            {
                throw new ArgumentNullException("extendedBytes");
            }
            if (extendedBytes.Length != 24)
            {
                throw new ArgumentException("extendedBytes length must be 24 bytes.");
            }
        
            //
            // Audio Attributes
            //
            
            // Note: Bytes[6] is reserved but is not used.

            _applicationMode = Dvd.Utility.AudioStreamApplicationModeNames[(bytes[0] & 3)];
            _channelCount = ((bytes[1] & 7) + 1);
            _codeExtension = Dvd.Utility.AudioStreamCodeExtensionNames[(bytes[5] & 7)];
            _codingModeValue = ((bytes[0] >> 5) & 7);
            _codingMode = Dvd.Utility.AudioStreamCodingModeNames[_codingModeValue];
            _fileExtension = Dvd.Utility.AudioStreamCodingModeFileExtensions[_codingModeValue];

            // If zero, _language="Unspecified". If one, determine lanaguage.
            // Note: Byte 4 is reserved for the language code extension, but is not used.
            _language = Dvd.Utility.UnspecifiedString;
            if ((((byte)(bytes[0] >> 2)) & 3) == 1)
            {
                _language = Dvd.Utility.GetLanguageName(Dvd.Utility.GetLanguageCode(bytes));
            }

            if (((bytes[0] >> 4) & 1) == 1)
            {
                _multiChannelExtension = true;
            }

            _quantization = Dvd.Utility.AudioStreamQuantizationNames[(((bytes[1] >> 6)) & 3)];
            _sampleRate = Dvd.Utility.AudioStreamSampleRateNames[((bytes[1] >> 3) & 3)];

            // Get Karaoke info.
            StringBuilder sb = new StringBuilder();
            if (_applicationMode == "Karaoke")
            {
                // Solo or Duet?
                if ((bytes[7] & 1) == 0)
                {
                    sb.Append("Solo");
                }
                else
                {
                    sb.Append("Duet");
                }

                // Channels?
                sb.Append(", Channels " + Dvd.Utility.AudioStreamKaraokeChannelNames[((bytes[7] >> 4) & 7)]);

                //
                // Multi-channel Extension present
                //
                if (((bytes[7] >> 1)& 1) == 1)
                {
                    // Channel 0
                    if ((extendedBytes[0] & 1) > 0)
                    {
                        sb.Append(", Channel 0 [M]");
                    }
                    // Channel 1
                    if ((extendedBytes[1] & 1) > 0)
                    {
                        sb.Append(", Channel 1 [M]");
                    }

                    // Channels 2-4
                    for (int i = 2; i <= 4; i++)
                    {
                        if ((extendedBytes[i] & 0xf) > 0)
                        {
                            sb.Append(", Channel " + i.ToString() + " [");

                            if (((extendedBytes[i] >> 1) & 1) > 0)
                            {
                                sb.Append("M1,");
                            }
                            if ((extendedBytes[i] & 1) > 0)
                            {
                                sb.Append("M2,");
                            }
                            if (((extendedBytes[i] >> 3) & 1) > 0)
                            {
                                sb.Append("V1,");
                            }
                            if (((extendedBytes[i] >> 2) & 1) > 0)
                            {
                                sb.Append("V2,");
                            }
                            // Remove final comma.
                            sb.Remove(sb.Length - 1, 1);

                            sb.Append("]");
                        }
                    }
                }

                // Karaoke version?
                if (((bytes[7] >> 2) & 1) != 0)
                {
                    sb.Append(", Version " + ((bytes[7] >> 2) & 1).ToString());
                }

                _applicationInfo = sb.ToString();
                sb.Length = 0;
                sb = null;
            }

            // Get Surround info.
            if (_applicationMode == "Surround")
            {
                _applicationInfo = "Surround decoding unsupported";
                if ((bytes[7] >> 3) == 1)
                {
                    _applicationInfo = "Surround decoding supported";
                }
            }
        }

        #endregion
    }
}
