using System;
using System.Globalization;

/*
    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>
    /// Utility fileds and methods for parsing.
    /// </summary>
    sealed public class Utility
    {
        #region Fields

        /// <summary>
        /// Audio stream application mode names.
        /// </summary>
        public static string[] AudioStreamApplicationModeNames = new string[3]
        {
            UnspecifiedString, "Karaoke", "Surround"
        };

        /// <summary>
        /// Audio stream code extension names.
        /// </summary>
        public static string[] AudioStreamCodeExtensionNames = new string[5]
        {
            UnspecifiedString, "Normal Captions", "Audio For Visually Impaired", "Director's Comments",
            "Alternate Director's Comments"
        };

        /// <summary>
        /// Audio stream coding mode file extensions.
        /// </summary>
        public static string[] AudioStreamCodingModeFileExtensions = new string[8]
        {
            ".AC3", ".RAW", ".MP2", ".MP2", ".RAW", ".RAW", ".DTS", ".RAW"
        };

        /// <summary>
        /// Audio stream coding mode names.
        /// </summary>
        public static string[] AudioStreamCodingModeNames = new string[8]
        {
            // Values for SDDS and AC-2 not known.
            "AC-3", Dvd.Utility.UnknownString, "MPEG-1", "MPEG-2 Ext", "LPCM", Dvd.Utility.UnknownString,
            "DTS", Dvd.Utility.UnknownString
        };

        /// <summary>
        /// Audio stream coding mode prefixes.
        /// </summary>
        public static Int32[] AudioStreamCodingModePrefixes = new Int32[8]
        {
            // SDDS is 0x90, AC-2 is unknown. Coding modes for values 1, 5, 7 unknown.
            0x80, 0, 0xc0, 0xd0, 0xa0, 0, 0x88, 0
        };

        /// <summary>
        /// Audio stream karaoke channel names.
        /// </summary>
        public static string[] AudioStreamKaraokeChannelNames = new string[8]
        {
           "1+1 [Invalid]", "1/0 [Invalid]", "2/0 [L,R]", "3/0 [L,M,R]", "2/1 [L,R,V1]",
           "3/1 [L,M,R,V1]", "2/2 [L,R,V1,V2]", "3/2 [L,M,R,V1,V2]"
        };

        /// <summary>
        /// Audio stream quantization names.
        /// </summary>
        public static string[] AudioStreamQuantizationNames = new string[4]
        {
           "16bps", "20bps", "24bps", "DRC"
        };

        /// <summary>
        /// Audio stream sample rate names.
        /// </summary>
        public static string[] AudioStreamSampleRateNames = new string[2]
        {
            "48Kbps", "96Kbps"
        };

        /// <summary>
        /// The DVD secotr size in bytes.
        /// </summary>
        public static Int32 DvdSectorSize = 2048; // 2048 bytes = 2 KB

        /// <summary>
        /// ISO 639-2 two letter language codes.
        /// </summary>
        public static string[] LanguageCodes = new string[141]
        { 
            "aa", "ab", "af", "am", "ar", "as", "ay", "az", "ba", "be", 
            "bg", "bh", "bi", "bn", "bo", "br", "ca", "co", "cs", "cy", 
            "da", "de", "dz", "el", "en", "eo", "es", "et", "eu", "fa", 
            "fi", "fj", "fo", "fr", "fy", "ga", "gd", "gl", "gn", "gu", 
            "ha", "he", "hi", "hr", "hu", "ia", "id", "ie", "ik", "in", 
            "is", "it", "iu", "iw", "ja", "ji", "jw", "ka", "kk", "kl", 
            "km", "kn", "ko", "ks", "ku", "ky", "la", "ln", "lo", "lt", 
            "lv", "mg", "mi", "mk", "ml", "mn", "mo", "mr", "ms", "mt", 
            "my", "na", "ne", "nl", "no", "oc", "om", "or", "pa", "pl", 
            "ps", "pt", "qu", "rm", "rn", "ro", "ru", "rw", "sa", "sd", 
            "sg", "sh", "si", "sk", "sl", "sm", "sn", "so", "sq", "sr", 
            "ss", "st", "su", "sv", "sw", "ta", "te", "tg", "th", "ti", 
            "tk", "tl", "tn", "to", "tr", "ts", "tt", "tw", "ug", "uk", 
            "ur", "uz", "ui", "vo", "wo", "xh", "yi", "yo", "za", "zh", 
            "zu"
        };

        /// <summary>
        /// Names for the ISO 639-2 two letter language codes.
        /// </summary>
        public static string[] LanguageNames = new string[141]
        { 
            "Afar", "Abkhazian", "Afrikaans", "Ameharic", "Arabic", "Assamese", "Aymara", "Azerbaijani", "Bashkir", "Byelorussian", 
            "Bulgarian", "Bihari", "Bislama", "Bengali", "Tibetan", "Breton", "Catalan", "Corsican", "Czech", "Welsh", 
            "Danish", "German", "Bhutani", "Greek", "English", "Esperanto", "Spanish", "Estonian", "Basque", "Persian", 
            "Finnish", "Fiji", "Faroese", "French", "Frisian", "Irish", "Scots Gaelic", "Galician", "Guarani", "Gujarati", 
            "Hausa", "Hebrew", "Hindi", "Croatian", "Magyar", "Armenian", "Indonesian", "Interlingue", "Inupiak", "Indonesian", 
            "Icelandic", "Italian", "Inuktitut", "Hebrew", "Japanese", "Yiddish", "Javanese", "Georgian", "Kazakh", "Greenlandic", 
            "Cambodian", "Kannada", "Korean", "Kashmiri", "Kurdish", "Kirghiz", "Latin", "Lingala", "Laothian", "Lithuanian", 
            "Latvian", "Malagasy", "Maori", "Macedonian", "Malayalam", "Mongolian", "Moldavian", "Marathi", "Malay", "Maltese", 
            "Myanmar", "Nauru", "Nepali", "Nederlands", "Norsk", "Occitan", "Oromo", "Oriya", "Punjabi", "Polish", 
            "Pashto", "Portuguese", "Quechua", "Rhaeto-Romance", "Kirundi", "Romanian", "Russian", "Kinyarwanda", "Sanskrit", "Sindhi",
            "Sangho", "Serbo-Croatian", "Sinhalese", "Slovak", "Slovenian", "Samoan", "Shona", "Somali", "Albanian", "Serbian", 
            "Siswati", "Sesotho", "Sundanese", "Svenska", "Swahili", "Tamil", "Telugu", "Tajik", "Thai", "Tigrinya", 
            "Turkmen", "Tagalog", "Setswana", "Tonga", "Turkish", "Tsonga", "Tatar", "Twi", "Uighur", "Ukrainian", 
            "Urdu", "Uzbek", "Vietnamese", "Volapuk", "Wolof", "Xhosa", "Yiddish", "Yoruba", "Zhuang", "Chinese", 
            "Zulu"
        };

        /// <summary>
        /// Program chain framerate names.
        /// </summary>
        public static string[] ProgramChainFramerateNames = new string[4]
        {
           "(Illegal FPS)", "25fps", "(Illegal FPS)", "30fps"
        };

        /// <summary>
        /// Subpicture coding extension names.
        /// </summary>
        public static string[] SubpictureCodeExtensionNames = new string[16]
        {
            UnspecifiedString, "Normal Captions", "Large Captions", "Captions For Children", Dvd.Utility.UnknownString,
            "Normal Closed Captions", "Large Closed Captions", "Closed Captions For Children", Dvd.Utility.UnknownString,
            "Forced Caption", Dvd.Utility.UnknownString, Dvd.Utility.UnknownString, Dvd.Utility.UnknownString,
            "Director's Comments", "Large Director's Comments", "Director's Comments For Children"
        };

        /// <summary>
        /// Subpicture coding mode names.
        /// </summary>
        public static string[] SubpictureCodingModeNames = new string[2]
        {
            "2-bit RLE", "Extended"
        };

        /// <summary>
        /// Subtitle aspect names.
        /// </summary>
        public static string[] SubtitleAspectNames = new string[6]
        {
            "Fullscreen", "Fullscreen-PanScan", "Letterbox", "PanScan", "Widescreen", "Widescreen-Letterbox"
        };

        /// <summary>
        /// An unknown element.
        /// </summary>
        public const string UnknownString = "???";

        /// <summary>
        /// An unspecified element.
        /// </summary>
        public const string UnspecifiedString = "Unspecified";

        /// <summary>
        /// Title set video bitrate names.
        /// </summary>
        public static string[] VideoBitrateNames = new string[2]
        {
            "VBR", "CBR"
        };

        /// <summary>
        /// Title set video coding mode names.
        /// </summary>
        public static string[] VideoCodingModeNames = new string[2]
        {
            "MPEG-1", "MPEG-2"
        };

        /// <summary>
        /// Title set video film names.
        /// </summary>
        public static string[] VideoFilmNames = new string[2]
        {
            "Camera", "Film"
        };

        /// <summary>
        /// Title set NTSC video resolution names.
        /// </summary>
        public static string[] VideoResolutionNtscNames = new string[4]
        {
            "720x480", "704x480", "352x480", "352x240"
        };

        /// <summary>
        /// Title set PAL video resolution names.
        /// </summary>
        public static string[] VideoResolutionPalNames = new string[4]
        {
            "720x576", "704x576", "352x576", "352x288" 
        };

        /// <summary>
        /// Title set video standard names.
        /// </summary>
        public static string[] VideoStandardNames = new string[2]
        {
            "NTSC", "PAL"
        };

        #endregion

        #region Methods

        /// <summary>
        /// Converts language code bytes to an ISO 639-2 two-letter language code. The bytes are
        /// located at zero-based indexes 2 and 3.
        /// </summary>
        /// <param name="value">A byte array containing the bytes to convert.</param>
        /// <returns>A string containing the ISO 639-2 two-letter language code.</returns>
        /// <exception cref="System.ArgumentNullException">Thrown when value is null.</exception>
        /// <exception cref="System.ArgumentException">Thrown when value length is less than four bytes.</exception>
        public static string ConvertBytesToLanguageCode(byte[] value)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }
            if (value.Length < 4)
            {
                throw new ArgumentException("value length is less than four bytes.");
            }

            return System.Text.Encoding.ASCII.GetString(value, 2, 1) + System.Text.Encoding.ASCII.GetString(value, 3, 1);
        }

        /// <summary>
        /// Converts an ISO 639-2 two-letter language code to a name.
        /// </summary>
        /// <param name="value">A a string containing the ISO 639-2 two-letter language code to convert.</param>
        /// <returns>A string containing the name for the language code if found, otherwise an empty string.</returns>
        /// <exception cref="System.ArgumentException">Thrown when value is null or empty.</exception>
        public static string ConvertLanguageCodeToName(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                throw new ArgumentException("value is null or empty.");
            }

            string result = string.Empty;
            for (int i = 0; i < LanguageNames.Length; i++)
            {
                if (LanguageCodes[i] == value)
                {
                    result = LanguageNames[i];
                    break;
                }
            }

            return result;
        }

        /// <summary>
        /// Converts a SubtitleAspect enumeration to a SubtitleAspectName string.
        /// </summary>
        /// <param name="value">The SubtitleAspect enumeration to convert.</param>
        /// <returns>An SubtitleAspectName string.</returns>
        public static string ConvertSubtitleAspectToName(SubtitleAspect value)
        {
            return SubtitleAspectNames[(Int32)value];
        }

        /// <summary>
        /// Returns an array containing the filenames of the vobs in the title set.
        /// </summary>
        /// <param name="filename">The filename of the first file of the title set.</param>
        /// <param name="parts">The number of files in the title set.</param>
        /// <exception cref="System.ArgumentException">Thrown when filename is null or empty.</exception>
        /// <exception cref="System.ArgumentException">Thrown when parts is less than one.</exception>
        public static string[] GetVideoTitleSetVobFilenames(string filename, Int32 parts)
        {
            if (string.IsNullOrEmpty(filename))
            {
                throw new ArgumentException("filename is null or empty.");
            }
            if (parts < 1)
            {
                throw new ArgumentException("parts less than one.");
            }

            string[] result = new string[parts];
            result[0] = filename;

            if (parts > 1)
            {
                for (int i = 1; i < parts; i++)
                {
                    result[i] = filename.Substring(0, filename.Length - 5) + i.ToString() +
                        filename.Substring(filename.Length - 4);
                }
            }

            return result;
        }

        /// <summary>
        /// Returns the video title set id parsed from the specfied filename.
        /// </summary>
        /// <param name="filename">The filename to parse.</param>
        /// <exception cref="System.ArgumentException">Thrown when filename is null or empty.</exception>
        public static Int32 GetVideoTitleSetID(string filename)
        {
            if (string.IsNullOrEmpty(filename))
            {
                throw new ArgumentException("filename is null or empty.");
            }

            Int32 result = 0;
            Int32.TryParse(filename.Substring(filename.Length - 8, 2), out result);

            return result;
        }

        /// <summary>
        /// Returns the video title set part number parsed from the specfied filename.
        /// </summary>
        /// <param name="filename">The filename to parse.</param>
        /// <exception cref="System.ArgumentException">Thrown when filename is null or empty.</exception>
        public static Int32 GetVideoTitleSetPartNumber(string filename)
        {
            if (string.IsNullOrEmpty(filename))
            {
                throw new ArgumentException("filename is null or empty.");
            }

            Int32 result = 0;
            Int32.TryParse(filename.Substring(filename.Length - 5, 1), out result);

            return result;
        }

        #endregion
    }
}
