/* This file is part of AHD Subtitles Maker Professional
   A program can create and edit subtitles

   Copyright © Ala I Hadid 2009 - 2012

   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 3 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, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using AHD.SM.ASMP;
using AHD.Forms;

namespace AHD.MKV
{
    /// <summary>
    /// A class represents Matroska file
    /// </summary>
    public class MKVFile
    {
        /// <summary>
        /// A class represents Matroska file
        /// </summary>
        /// <param name="fileName">The complete mkv file path</param>
        public MKVFile(string fileName)
        {
            CheckFile(fileName);
        }
        private bool isValid = false;
        private FileStream stream;
        private byte[] vTable = { 3, 7, 15, 31, 63 };
        private double duration = 0;
        private List<MkvSubtitlesTrackInfo> subtitleTracks = new List<MkvSubtitlesTrackInfo>();

        //Methods
        private void CheckFile(string fileName)
        {
            stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
            uint id = GetID();
            if (id != (uint)MkvID.EbmlHeader)
            {
                isValid = false;
            }
            else
            {
                isValid = true;
                long vSize = GetElementSize();
                stream.Seek(vSize, SeekOrigin.Current);
                while (true)
                {
                    //1 get the element id
                    id = GetID();
                    if (id == (uint)MkvID.None)
                    {
                        break;
                    }
                    //2 calculate size
                    long dataSize = GetElementSize();
                    //3 read data
                    long ElementOffsetPosition = stream.Position;
                    long ElementEndPosition = ElementOffsetPosition + dataSize;

                    if (((MkvID)id) == MkvID.Track)
                    {
                        LoadTracks();
                        stream.Seek(ElementEndPosition, SeekOrigin.Begin);
                        break;
                    }
                    else if (((MkvID)id) != MkvID.Segment)
                        stream.Seek(dataSize, SeekOrigin.Current);
                    if (stream.Position >= stream.Length)
                        break;
                }
                if (subtitleTracks.Count == 0)
                { isValid = false; }
            }
            stream.Close();
            stream.Dispose();
        }
        private uint GetID()
        {
            byte b = (byte)stream.ReadByte();
            if (b == 0xEC || // Void
                b == 0xBF)   // CRC-32
                return b;

            byte[] bytes = new byte[4];
            bytes[0] = b;
            for (int i = 1; i < 4; i++)
                bytes[i] = (byte)stream.ReadByte();

            return BigEndianHelper.ToUINT32(bytes);
        }
        private uint GetTrackEntryID()
        {
            uint s = (uint)stream.ReadByte();

            if (s == 0xEC || // Void
                s == 0xBF || // CRC-32
                s == 0xD7 || // Track number
                s == 0x83 || // Track type
                s == 0xB9 || // Flag enabled
                s == 0x88 || // Flag default
                s == 0x9C || // Flag lacing
                s == 0x4F || // Track Time Code Scale
                s == 0xAA || // CodecDecodeAll
                s == 0xE0 || // Track Video
                s == 0xE1 || // Track Audio
                s == 0x86)   // Codec Id
            {
                return s;
            }

            s = (s << 8) | (byte)stream.ReadByte();
            if (s == 0x73C5 || // TrackUID
                s == 0x55AA || // FlagForced
                s == 0x6DE7 || // MinCache
                s == 0x6DF8 || // MaxCache
                s == 0x55EE || // MaxBlockAdditionID
                s == 0x63A2 || // CodecPrivate
                s == 0x7446 || // AttachmentLink
                s == 0x6D80 || // ContentEncodings
                s == 0x537F || // TrackOffset
                s == 0x6FAB || // TrackOverlay
                s == 0x536E || // Name
                s == 0x6624 || // TrackTranslate
                s == 0x66FC || // TrackTranslateEditionUID
                s == 0x66BF || // TrackTranslateCodec
                s == 0x66A5)   // TrackTranslateTrackID
            {
                return s;
            }

            s = (s << 8) | (byte)stream.ReadByte();
            if (s == 0x23E383 || // Default Duration
                s == 0x22B59C || // Language
                s == 0x258688 || // CodecName
                s == 0x23314F)   // TrackTimeCodeScale
                return s;

            return 0;
        }
        private uint GetClusterId()
        {
            uint s = (byte)stream.ReadByte();

            if (s == 0xE7 || // TimeCode
                s == 0xA7 || // Position
                s == 0xAB || // PrevSize
                s == 0xA0 || // BlockGroup
                s == 0xA1 || // Block
                s == 0xA2 || // BlockVirtual
                s == 0xA6 || // BlockMore
                s == 0xEE || // BlockAddID
                s == 0xA5 || // BlockAdditional
                s == 0x9B || // BlockDuration
                s == 0xFA || // ReferencePriority
                s == 0xFB || // ReferenceBlock
                s == 0xFD || // ReferenceVirtual
                s == 0xA4 || // CodecState
                s == 0x8E || // Slices
                s == 0x8E || // TimeSlice
                s == 0xCC || // LaceNumber
                s == 0xCD || // FrameNumber
                s == 0xCB || // BlockAdditionID
                s == 0xCE || // Delay
                s == 0xCF || // Duration
                s == 0xA3)   // SimpleBlock
            {
                return s;
            }

            s = (s << 8) | (byte)stream.ReadByte();

            if (s == 0x5854 || // SilentTracks
                s == 0x58D7 || // SilentTrackNumber
                s == 0x75A1) // BlockAdditions
                return s;

            return 0;
        }
        private long GetElementSize()
        {
            byte val = (byte)stream.ReadByte();
            byte vSize = GetVariableIntLength(val);
            return GetDataSize(vSize, val);
        }
        private long GetValue(byte dataSize)
        {
            byte val = (byte)stream.ReadByte();
            return GetDataSize(dataSize, val);
        }
        private long GetUINTValue(byte dataSize)
        {
            long val = 0;

            int sh = (dataSize - 1);

            for (int i = 0; i < dataSize; i++)
                val += (stream.ReadByte() << ((sh - i) * 8));

            return val;
        }
        private string GetString(long dataSize)
        {
            try
            {
                byte[] buffer = new byte[dataSize];
                stream.Read(buffer, 0, (int)dataSize);
                return System.Text.Encoding.UTF8.GetString(buffer);
            }
            catch
            {
                return string.Empty;
            }
        }
        private byte GetVariableIntLength(byte b)
        {
            byte size = 1;
            for (int i = 0; i < 8; i++)
            {
                if ((b & 0x80) == 0x80)//the bit is set
                {
                    break;
                }
                else
                {
                    size++;
                    b <<= 1;
                }
            }
            return size;
        }
        private long GetDataSize(byte vSize, byte b)
        {
            long val = 0;
            if (vSize == 1)
            {
                val = b & 0x7F;
            }
            else if (vSize == 8)
            {
                for (int i = 0; i < 7; i++)
                    val += (stream.ReadByte() << ((6 - i) * 8));
            }
            else
            {
                int sh = (vSize - 2);
                int max = (vSize - 1);

                if (vSize < 7)
                    val += (b & vTable[6 - vSize]) << (max * 8);
                else
                    val += b << 48;

                for (int i = 0; i < max; i++)
                    val += (stream.ReadByte() << ((sh - i) * 8));
            }
            return val;
        }

        private void GetMediaDurationFromSegmentInfo()
        {
            long timeCodeScale = 0;
            double duration8b = 0;

            while (true)
            {
                uint id = GetID();
                if (id == (uint)MkvID.None)
                    break;
                else
                {
                    long dataSize = GetElementSize();

                    if (id == (uint)MkvID.TimecodeScale)
                    {
                        timeCodeScale = GetValue((byte)dataSize);
                    }
                    else if (id == (uint)MkvID.Duration)
                    {
                        byte[] bytes = new byte[dataSize];
                        for (int i = 0; i < dataSize; i++)
                            bytes[i] = (byte)stream.ReadByte();
                        if (dataSize == 4)
                        {
                            duration8b = BigEndianHelper.ToFLOAT32(bytes);
                        }
                        else
                        {
                            duration8b = BigEndianHelper.ToFLOAT64(bytes);
                        }
                    }
                    else
                        stream.Seek(dataSize, SeekOrigin.Current);
                }
                if (stream.Position >= stream.Length)
                    break;
            }
            if (timeCodeScale > 0 && duration8b > 0)
                duration = duration8b / timeCodeScale * 1000000.0;
            else if (duration8b > 0)
                duration = duration8b;
        }
        private void LoadTracks()
        {
            while (true)
            {
                uint id = (uint)stream.ReadByte();
                if (id == (uint)MkvID.None)
                    break;
                else
                {
                    long dataSize = GetElementSize();
                    if (id == (uint)MkvID.TrackEntry)
                    {
                        long ElementEndPosition = stream.Position + dataSize;
                        AnalyzeTrackEntry();
                        stream.Seek(ElementEndPosition, SeekOrigin.Begin);
                    }
                    else
                        stream.Seek(dataSize, SeekOrigin.Current);
                }
                if (stream.Position >= stream.Length)
                    break;
            }

        }
        private void AnalyzeTrackEntry()
        {
            long defaultDuration = 0;
            byte trackNumber = 0;
            bool isVideoTrack = false;
            bool isSubtitleTrack = false;
            string name = "";
            string language = "";
            string codecID = "";
            string codecPrivate = "";
            string biCompression = "";
            int contentCompressionAlgorithm = -1;
            int contentEncodingType = -1;
            while (true)
            {
                uint id = GetTrackEntryID();
                if (id == (uint)MkvID.None)
                    break;
                else
                {
                    long dataSize = GetElementSize();
                    if (id == (uint)MkvTrackEntryID.DefaultDuration)
                    {
                        long afterPosition = stream.Position + dataSize;
                        defaultDuration = GetValue((byte)dataSize);
                        stream.Seek(afterPosition, SeekOrigin.Begin);
                    }
                    else if (id == (uint)MkvTrackEntryID.TrackVideo)
                    {
                        long afterPosition = stream.Position + dataSize;
                        GetVideoTrackInfo();
                        stream.Seek(afterPosition, SeekOrigin.Begin);
                        isVideoTrack = true;
                    }
                    else if (id == (uint)MkvTrackEntryID.TrackNumber)
                    {
                        long afterPosition = stream.Position + dataSize;
                        trackNumber = (byte)stream.ReadByte();
                        stream.Seek(afterPosition, SeekOrigin.Begin);
                    }
                    else if (id == (uint)MkvTrackEntryID.Name)
                    {
                        long afterPosition = stream.Position + dataSize;
                        name = GetString(dataSize);
                        stream.Seek(afterPosition, SeekOrigin.Begin);
                    }
                    else if (id == (uint)MkvTrackEntryID.Language)
                    {
                        long afterPosition = stream.Position + dataSize;
                        language = GetString(dataSize);
                        stream.Seek(afterPosition, SeekOrigin.Begin);
                    }
                    else if (id == (uint)MkvTrackEntryID.CodecId)
                    {
                        long afterPosition = stream.Position + dataSize;
                        codecID = GetString(dataSize);
                        stream.Seek(afterPosition, SeekOrigin.Begin);
                    }
                    else if (id == (uint)MkvTrackEntryID.TrackType)
                    {
                        long afterPosition = stream.Position + dataSize;
                        if (dataSize == 1)
                        {
                            byte trackType = (byte)stream.ReadByte();
                            if (trackType == 0x11) // subtitle
                            {
                                isSubtitleTrack = true;
                            }
                        }
                        stream.Seek(afterPosition, SeekOrigin.Begin);
                    }
                    else if (id == (uint)MkvTrackEntryID.CodecPrivate)
                    {
                        long afterPosition = stream.Position + dataSize;
                        codecPrivate = GetString(dataSize);
                        if (codecPrivate.Length > 20)
                            biCompression = codecPrivate.Substring(16, 4);
                        stream.Seek(afterPosition, SeekOrigin.Begin);
                    }
                    else if (id == (uint)MkvTrackEntryID.ContentEncodings)
                    {
                        long afterPosition = stream.Position + dataSize;

                        contentCompressionAlgorithm = 0;
                        contentEncodingType = 0;

                        int contentEncoding1 = stream.ReadByte();
                        int contentEncoding2 = stream.ReadByte();

                        if (contentEncoding1 == 0x62 && contentEncoding2 == 0x40)
                        {
                            GetContentEncoding(afterPosition, ref contentCompressionAlgorithm, ref contentEncodingType);
                        }
                        stream.Seek(afterPosition, SeekOrigin.Begin);
                    }
                    else
                        stream.Seek(dataSize, SeekOrigin.Current);
                }
                if (stream.Position >= stream.Length)
                    break;
            }
            if (isSubtitleTrack)
            {
                subtitleTracks.Add(new MkvSubtitlesTrackInfo(name, defaultDuration, trackNumber, language
                    , codecID, codecPrivate, biCompression, contentCompressionAlgorithm, contentEncodingType));
            }
        }
        private void GetVideoTrackInfo()
        {

        }
        private void GetContentEncoding(long endPosition, ref int contentCompressionAlgorithm, ref int contentEncodingType)
        {
            long datasize = GetElementSize();

            while (true)
            {
                int Id = (stream.ReadByte() << 8) | stream.ReadByte();

                if (Id == 0)
                    break;
                else
                {
                    if (Id == (int)MkvContentEncodingID.ContentEncodingOrder)
                    {
                        int contentEncodingOrder = (stream.ReadByte() << 8) | stream.ReadByte();
                    }
                    else if (Id == (int)MkvContentEncodingID.ContentEncodingScope)
                    {
                        int contentEncodingScope = (stream.ReadByte() << 8) | stream.ReadByte();
                    }
                    else if (Id == (int)MkvContentEncodingID.ContentEncodingType)
                    {
                        contentEncodingType = (stream.ReadByte() << 8) | stream.ReadByte();
                    }
                    else if (Id == (int)MkvContentEncodingID.ContentEncryption)
                    {
                        int contentEncryption = (stream.ReadByte() << 8) | stream.ReadByte();
                    }
                    else if (Id == (int)MkvContentEncodingID.ContentCompression)
                    {
                        datasize = GetElementSize();
                        long afterPosition = stream.Position + datasize;
                        while (stream.Position < afterPosition)
                        {
                            int contentCompressionId = (stream.ReadByte() << 8) | stream.ReadByte();
                            if (contentCompressionId == 0x4254)
                            {
                                contentCompressionAlgorithm = (stream.ReadByte() << 8) | stream.ReadByte();
                            }
                            else if (contentCompressionId == 0x4255)
                            {
                                int contentCompSettings = (stream.ReadByte() << 8) | stream.ReadByte();
                            }
                        }
                        stream.Seek(afterPosition, SeekOrigin.Begin);
                    }
                    if (stream.Position >= stream.Length)
                        break;
                }
            }
        }
        private void CopyStream(System.IO.Stream input, System.IO.Stream output)
        {
            byte[] buffer = new byte[2000];
            int len;
            while ((len = input.Read(buffer, 0, 2000)) > 0)
            {
                output.Write(buffer, 0, len);
            }
            output.Flush();
        }
        private void ReadCluster(List<MkvSubtitleBlock> blocks, int trackNumber)
        {
            #region ReadCluster
            long clusterTimeCode = 0;
            while (true)
            {
                uint cid = GetClusterId();
                if (cid == 0)
                    break;
                else
                {
                    long dataSize = GetElementSize();
                    if (cid == 0xE7) // Timecode
                    {
                        long afterPosition = stream.Position + dataSize;
                        clusterTimeCode = GetUINTValue((byte)dataSize);
                        stream.Seek(afterPosition, SeekOrigin.Begin);
                    }
                    else if (cid == 0xA0) // BlockGroup
                    {
                        long afterPosition = stream.Position + dataSize;
                        ReadBlockGroup(blocks, trackNumber, clusterTimeCode);
                        stream.Seek(afterPosition, SeekOrigin.Begin);
                    }
                    else if (cid == 0xA3) // SimpleBlock
                    {
                        long afterPosition = stream.Position + dataSize;
                        long before = stream.Position;
                        byte b = (byte)stream.ReadByte();
                        byte sizeOftrackNumber = GetVariableIntLength(b);
                        long trkNumber = GetDataSize(sizeOftrackNumber, b);

                        if (trkNumber == trackNumber)
                        {
                            int timeCode = (stream.ReadByte() << 8) | stream.ReadByte();
                            byte flags = (byte)stream.ReadByte();
                            byte numberOfFrames = 0;

                            switch ((flags & 6))  // 6 = 00000110 
                            {
                                case 0: // No lacing
                                    break;
                                case 2: // 2 = 00000010 = Xiph lacing
                                    numberOfFrames = (byte)stream.ReadByte();
                                    numberOfFrames++;
                                    break;
                                case 4: // 4 = 00000100 = Fixed-size lacing
                                    numberOfFrames = (byte)stream.ReadByte();
                                    numberOfFrames++;
                                    for (int i = 1; i <= numberOfFrames; i++)
                                        b = (byte)stream.ReadByte(); // frames
                                    break;
                                case 6:// 6 = 00000110 = EMBL
                                    numberOfFrames = (byte)stream.ReadByte();
                                    numberOfFrames++;
                                    break;
                            }

                            byte[] buffer = new byte[dataSize - (stream.Position - before)];
                            stream.Read(buffer, 0, buffer.Length);
                            blocks.Add(new MkvSubtitleBlock(timeCode + clusterTimeCode, (long)(timeCode + clusterTimeCode + duration), buffer));
                        }
                        stream.Seek(afterPosition, SeekOrigin.Begin);
                    }
                    else
                        stream.Seek(dataSize, SeekOrigin.Current);
                }
                if (stream.Position >= stream.Length)
                    break;
            }
            #endregion
        }
        private void ReadBlockGroup(List<MkvSubtitleBlock> blocks, int trackNumber, long clusterTimeCode)
        {
            long duration = 0;
            byte b = (byte)stream.ReadByte();
            if (b == 0xA1) // Block
            {
                b = (byte)stream.ReadByte();
                byte sizeOfSize = GetVariableIntLength(b);
                long   dataSize = GetDataSize(sizeOfSize, b);

                long afterPosition = stream.Position + dataSize;

                // track number
                b = (byte)stream.ReadByte();
                byte sizeOfTrackNo = GetVariableIntLength(b);
                long trackNo = GetDataSize(sizeOfTrackNo, b);

                // time code
                int timeCode = (stream.ReadByte() << 8) | stream.ReadByte();

                // lacing
                byte flags = (byte)stream.ReadByte();
                byte numberOfFrames = 0;
                switch ((flags & 6))  // 6 = 00000110 
                {
                    case 0:  // No lacing
                        break;
                    case 2: // 2 = 00000010 = Xiph lacing
                        numberOfFrames = (byte)stream.ReadByte();
                        numberOfFrames++;
                        break;
                    case 4:   // 4 = 00000100 = Fixed-size lacing
                        numberOfFrames = (byte)stream.ReadByte();
                        numberOfFrames++;
                        for (int i = 1; i <= numberOfFrames; i++)
                            b = (byte)stream.ReadByte(); // frames
                        break;
                    case 6:        // 6 = 00000110 = EMBL
                        numberOfFrames = (byte)stream.ReadByte();
                        numberOfFrames++;
                        break;
                }

                // save subtitle data
                if (trackNo == trackNumber)
                {
                    long sublength = afterPosition - stream.Position;
                    if (sublength > 0)
                    {
                        byte[] buffer = new byte[sublength];
                        stream.Read(buffer, 0, (int)sublength);
                        stream.Seek(afterPosition, SeekOrigin.Begin);
                        b = (byte)stream.ReadByte();
                        if (b == 0x9B) // BlockDuration
                        {
                            b = (byte)stream.ReadByte();
                            sizeOfSize = GetVariableIntLength(b);
                            dataSize = GetDataSize(sizeOfSize, b);
                            duration = GetUINTValue((byte)dataSize);
                        }

                        blocks.Add(new MkvSubtitleBlock(timeCode + clusterTimeCode, timeCode + clusterTimeCode + duration, buffer));
                    }
                }

            }
        }
        List<MkvSubtitleBlock> GetSubtitleBlocks(string fileName, int trackNumber)
        {
            List<MkvSubtitleBlock> blocks = new List<MkvSubtitleBlock>();
            stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
            uint id = GetID(); 
            Frm_ProgressBar pr = new Frm_ProgressBar();
            pr.Show();
            pr.progressBar1.Maximum = (int)stream.Length;
            if (id != (uint)MkvID.EbmlHeader)
            {
                isValid = false;
            }
            else
            {
                isValid = true;
                long vSize = GetElementSize();
                stream.Seek(vSize, SeekOrigin.Current);
                while (true)
                {
                    //1 get the element id
                    id = GetID();
                    if (id == (uint)MkvID.None)
                    {
                        break;
                    }
                    //2 calculate size
                    long dataSize = GetElementSize();
                    //3 read data
                    long ElementOffsetPosition = stream.Position;
                    long ElementEndPosition = ElementOffsetPosition + dataSize;

                    if (((MkvID)id) == MkvID.SegmentInfo)
                    {
                        GetMediaDurationFromSegmentInfo();
                        stream.Seek(ElementEndPosition, SeekOrigin.Begin);
                    }
                    else if (((MkvID)id) == MkvID.Track)
                    {
                        LoadTracks();
                        stream.Seek(ElementEndPosition, SeekOrigin.Begin);
                    }
                    else if (((MkvID)id) == MkvID.Cluster)
                    {
                        ReadCluster(blocks, trackNumber);
                        stream.Seek(ElementEndPosition, SeekOrigin.Begin);
                    }
                    else if (((MkvID)id) != MkvID.Segment)
                        stream.Seek(dataSize, SeekOrigin.Current);

                    if (stream.Position >= stream.Length)
                        break;
                    pr.progressBar1.Value = (int)stream.Position;
                }
                if (subtitleTracks.Count == 0)
                { isValid = false; }
            }
            stream.Close();
            stream.Dispose();
            pr.Close();
            return blocks;
        }
        public SubtitlesTrack GetSubtitlesTrack(string fileName, MkvSubtitlesTrackInfo info)
        {
            #region VOBSUB
            if (info.CodecID.ToUpper() == "S_VOBSUB")
            {
                if (info.ContentEncodingType == 1)
                {
                    return null;
                }
                List<MkvSubtitleBlock> blocks = new List<MkvSubtitleBlock>();
                try
                {
                     blocks = GetSubtitleBlocks(fileName, info.TrackNumber);
                }
                catch (Exception ex)
                {
                    MessageDialog.ShowErrorMessage(ex.Message + "\n" + ex.ToString(), "ERROR !");
                    return null;
                }
                Frm_ProgressBar pr = new Frm_ProgressBar();
                pr.Show();
                pr.progressBar1.Maximum = blocks.Count;
                try
                {
                    if (blocks.Count > 0)
                    {
                        List<VobSubMergedPack> mergedVobSubPacks = new List<VobSubMergedPack>();
                        IDX idx = new IDX(info.CodecPrivate.Split("\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries));
                        int i = 0;
                        foreach (MkvSubtitleBlock block in blocks)
                        {
                            if (info.ContentEncodingType == 0) // compressed with zlib
                            {
                                MemoryStream outStream = new MemoryStream();
                                ComponentAce.Compression.Libs.zlib.ZOutputStream outZStream = new ComponentAce.Compression.Libs.zlib.ZOutputStream(outStream);
                                MemoryStream inStream = new MemoryStream(block.Data);
                                byte[] buffer;
                                try
                                {
                                    CopyStream(inStream, outZStream);
                                    buffer = new byte[outZStream.TotalOut];
                                    outStream.Position = 0;
                                    outStream.Read(buffer, 0, buffer.Length);
                                }
                                finally
                                {
                                    outStream.Close();
                                    outZStream.Close();
                                    inStream.Close();
                                }
                                mergedVobSubPacks.Add(new VobSubMergedPack(buffer, TimeSpan.FromMilliseconds(block.StartTime), 32, IDXItem.Empty));
                            }
                            else
                            {
                                mergedVobSubPacks.Add(new VobSubMergedPack(block.Data, TimeSpan.FromMilliseconds(block.StartTime), 32, IDXItem.Empty));
                            }
                            if (mergedVobSubPacks.Count > 0)//just in case
                                mergedVobSubPacks[mergedVobSubPacks.Count - 1].EndTime = TimeSpan.FromMilliseconds(block.EndTime);
                            pr.progressBar1.Value = i;
                            i++;
                        } 
                        pr.Close();
                        //laucnh the subtitles window
                        Frm_VobSubtitlesToASMPSubtitles frm = new Frm_VobSubtitlesToASMPSubtitles(mergedVobSubPacks, idx.Palette);
                        if (frm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                        {
                            
                            return frm.SubtitlesTrackGenerated;
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageDialog.ShowErrorMessage(ex.Message + "\n" + ex.ToString(), "ERROR !");
                    pr.Close();
                    return null;
                }
            }
            #endregion
            else if (info.CodecID.ToUpper() == "S_HDMV/PGS")
            {
                return null;
            }
            else//subtitles format !!
            {
                List<MkvSubtitleBlock> blocks = GetSubtitleBlocks(fileName, info.TrackNumber);
                if (blocks.Count > 0)
                {

                }
            }
            return null;
        }
        //Properties
        /// <summary>
        /// Get if this file is a vaild Matroska file and can be read
        /// </summary>
        public bool IsVaild
        { get { return isValid; } }
        /// <summary>
        /// Get the media duration in Milliseconds
        /// </summary>
        public double Duration
        { get { return duration; } }
        /// <summary>
        /// Get the subtitle tracks
        /// </summary>
        public List<MkvSubtitlesTrackInfo> SubtitleTracks
        { get { return subtitleTracks; } }
    }
}
