using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

/*
    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
{
    /// <summary>
    /// Demuxes the contents of an unprotected DVD to a local folder.
    /// </summary>
    public partial class DemuxDvdDialog : Form
    {
        #region Fields

        private Dvd.IfoReader _dvd = null;
        private string _dvdOutputPath = string.Empty;
        private string _dvdSourcePath = string.Empty;
        private Int32 _selectedProgramChainIndex = -1;
        private Int32 _selectedTitleSetIndex = -1;
        private DemuxPgcSelection[] _selections = null;

        #endregion

        #region Properties

        /// <summary>
        /// Gets and sets the DVD output folder where the demuxed video and audio streams are stored.
        /// </summary>
        public string DvdOutputPath
        {
            get
            {
                return _dvdOutputPath;
            }
            set
            {
                _dvdOutputPath = value;
                textBoxDvdOutputPath.Text = _dvdOutputPath;
            }
        }
        /// <summary>
        /// Gets the selections to demux.
        /// </summary>
        public DemuxPgcSelection[] Selections
        {
            get
            {
                return _selections;
            }
        }
        /// <summary>
        /// Gets the total length in bytes of the selections.
        /// </summary>
        public Int64 SelectionsLength
        {
            get
            {
                return _dvd.CheckedProgramsLength;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Creates a new instance of the ImportDvdDialog class.
        /// </summary>
        public DemuxDvdDialog()
        {
            InitializeComponent();

            // Button Image property does not support transparent images. Load the images
            // from Resources into an ImageList - which does support transparency. 
            imageList1.Images.Add(Properties.Resources.Dvd);
            imageList1.Images.Add(Properties.Resources.DvdDown);
            imageList1.Images.Add(Properties.Resources.Folder);
            imageList1.Images.Add(Properties.Resources.FolderDown);
            imageList1.TransparentColor = System.Drawing.Color.Fuchsia;
            
            imageList2.Images.Add(Properties.Resources.Edit);
            imageList2.Images.Add(Properties.Resources.Video);
            imageList2.Images.Add(Properties.Resources.Audio);
            imageList2.Images.Add(Properties.Resources.Subtitle);
            imageList2.Images.Add(Properties.Resources.Chapters);
            imageList2.Images.Add(Properties.Resources.ChaptersDown);
            imageList2.TransparentColor = System.Drawing.Color.Fuchsia;
            
            buttonBrowseDvdSourcePath.Image = imageList1.Images[0];
            buttonBrowseDvdOutputPath.Image = imageList1.Images[2];
            pictureBoxChapters.Image = imageList2.Images[4];

            UpdateMenus();
        }

        /// <summary>
        /// Updates all child tree nodes recursively.
        /// </summary>
        /// <param name="treeNode">The node to update child nodes for.</param>
        /// <param name="nodeChecked">Whether the node is checked.</param>
        private void CheckAllTitleSetChildNodes(TreeNode treeNode, bool nodeChecked)
        {
            foreach (TreeNode node in treeNode.Nodes)
            {
                node.Checked = nodeChecked;

                // Update internal title set Checked property.
                Int32[] ids = (Int32[])node.Tag;
                if (ids != null && ids.Length == 3)
                {
                    _dvd.TitleSets[ids[1]].ProgramChains[ids[2]].VideoChecked = node.Checked;
                    treeViewTitleSets.Nodes[ids[1]].Nodes[ids[2]].Nodes[0].Checked = node.Checked;
 
                    for (int i = 0; i < _dvd.TitleSets[ids[1]].ProgramChains[ids[2]].AudioStreamRefs.Length; i++)
                    {
                        _dvd.TitleSets[ids[1]].ProgramChains[ids[2]].AudioStreamRefs[i].Checked = node.Checked;
                        treeViewTitleSets.Nodes[ids[1]].Nodes[ids[2]].Nodes[i + 1].Checked = node.Checked;
                    }

                    for (int i = 0; i < _dvd.TitleSets[ids[1]].ProgramChains[ids[2]].Subtitles.Length; i++)
                    {
                        _dvd.TitleSets[ids[1]].ProgramChains[ids[2]].Subtitles[i].Checked = node.Checked;
                        treeViewTitleSets.Nodes[ids[1]].Nodes[ids[2]].Nodes[i +
                          _dvd.TitleSets[ids[1]].ProgramChains[ids[2]].AudioStreamRefs.Length + 1].Checked = node.Checked;
                    }
                }

                if (node.Nodes.Count > 0)
                {
                    // If the current node has child nodes, call the CheckAllChildsNodes method recursively.
                    this.CheckAllTitleSetChildNodes(node, nodeChecked);
                }
            }
        }

        /// <summary>
        /// Updates all tree nodes.
        /// </summary>
        /// <param name="nodeChecked">Whether the node is checked.</param>
        private void CheckAllTitleSetNodes(bool nodeChecked)
        {
            foreach (TreeNode node in treeViewTitleSets.Nodes)
            {
                node.Checked = nodeChecked;

                // Update internal title set Checked property.
                Int32[] ids = (Int32[])node.Tag;
                if (ids != null && ids.Length == 3)
                {
                    _dvd.TitleSets[ids[1]].ProgramChains[ids[2]].VideoChecked = node.Checked;
                    treeViewTitleSets.Nodes[ids[1]].Nodes[ids[2]].Nodes[0].Checked = node.Checked;
 
                    for (int i = 0; i < _dvd.TitleSets[ids[1]].ProgramChains[ids[2]].AudioStreamRefs.Length; i++)
                    {
                        _dvd.TitleSets[ids[1]].ProgramChains[ids[2]].AudioStreamRefs[i].Checked = node.Checked;
                        treeViewTitleSets.Nodes[ids[1]].Nodes[ids[2]].Nodes[i + 1].Checked = node.Checked;
                    }

                    for (int i = 0; i < _dvd.TitleSets[ids[1]].ProgramChains[ids[2]].Subtitles.Length; i++)
                    {
                        _dvd.TitleSets[ids[1]].ProgramChains[ids[2]].Subtitles[i].Checked = node.Checked;
                        treeViewTitleSets.Nodes[ids[1]].Nodes[ids[2]].Nodes[i +
                            _dvd.TitleSets[ids[1]].ProgramChains[ids[2]].AudioStreamRefs.Length + 1].Checked = node.Checked;
                    }
                }

                CheckAllTitleSetChildNodes(node, nodeChecked);
            }
        }

        /// <summary>
        /// Restores the user node selection.
        /// </summary>
        private void RestoreSourceTreeSelection()
        {
            treeViewTitleSets.SelectedNode = treeViewTitleSets.Nodes[_selectedTitleSetIndex].Nodes[_selectedProgramChainIndex];
            treeViewTitleSets.SelectedNode.ExpandAll();
            treeViewTitleSets.SelectedNode.EnsureVisible();
            treeViewTitleSets.Focus();

            UpdateMenus();
            UpdateSourceDetails();
        }

        /// <summary>
        /// Unchecks all subtitles.
        /// </summary>
        private void UncheckSubtitles()
        {
            for (int i = 0; i < _dvd.TitleSets.Length; i++)
            {
                for (int j = 0; j < _dvd.TitleSets[i].ProgramChains.Length; j++)
                {
                    for (int k = 0; k < _dvd.TitleSets[i].ProgramChains[j].Subtitles.Length; k++)
                    {
                        _dvd.TitleSets[i].ProgramChains[j].Subtitles[k].Checked = false;
                        treeViewTitleSets.Nodes[i].Nodes[j].Nodes[k +
                            _dvd.TitleSets[i].ProgramChains[j].AudioStreamRefs.Length + 1].Checked = false;
                    }
                }
            }
        }

        /// <summary>
        /// Updates the UI.
        /// </summary>
        private void UpdateMenus()
        {
            buttonDemux.Enabled = false;
            pictureBoxChapters.Visible = false;

            if (!string.IsNullOrEmpty(_dvdSourcePath) && !string.IsNullOrEmpty(_dvdOutputPath))
            {
                buttonDemux.Enabled = true;
            }

            if (_selectedTitleSetIndex != -1 && _selectedProgramChainIndex != -1)
            {
                pictureBoxChapters.Visible = true;
            }
        }

        /// <summary>
        /// Updates the source TreeView with the DVD info.
        /// </summary>
        private void UpdateSourceTree()
        {
            treeViewTitleSets.Nodes.Clear();
            TreeNode parentNode = null;
            for (int i = 0; i < _dvd.TitleSets.Length; i++)
            {
                parentNode = treeViewTitleSets.Nodes.Add("Title " + _dvd.TitleSets[i].Id.ToString("0#"));
                parentNode.Checked = _dvd.TitleSets[i].Checked;
                
                for (int j = 0; j < _dvd.TitleSets[i].ProgramChains.Length; j++)
                {
                    TreeNode node = parentNode.Nodes.Add("Program " + _dvd.TitleSets[i].ProgramChains[j].Id.ToString("0#") +
                        "  [" + _dvd.TitleSets[i].ProgramChains[j].Duration.ToString() + "]");
                    node.Tag = new Int32[3] { (Int32)DvdMediaNode.NonMedia, i, j};
                    node.Checked = _dvd.TitleSets[i].ProgramChains[j].Checked;
                    node.ImageIndex = -1;
                    node.SelectedImageIndex = -1;
                    node.StateImageIndex = -1;

                    //
                    // Show the video stream.
                    //
                    StringBuilder sb = new StringBuilder();
                    sb.Append(_dvd.TitleSets[i].VideoStandard);
                    if (_dvd.TitleSets[i].VideoAspectRatio == FicSprout.Bacicworx.Media.VideoAspectRatio.Aspect_1_33)
                    {
                        sb.Append(",  4:3");
                    }
                    if (_dvd.TitleSets[i].VideoAspectRatio == FicSprout.Bacicworx.Media.VideoAspectRatio.Aspect_1_78)
                    {
                        sb.Append(",  16:9"); 
                    }
                    if ((_dvd.TitleSets[i].VideoAutoLetterbox || _dvd.TitleSets[i].VideoIsLetterBoxed)
                       && !_dvd.TitleSets[i].VideoAutoPanScan)
                    {
                        sb.Append(" (Letterboxed)");
                    }
                    if (!(_dvd.TitleSets[i].VideoAutoLetterbox || _dvd.TitleSets[i].VideoIsLetterBoxed)
                       && _dvd.TitleSets[i].VideoAutoPanScan)
                    {
                        sb.Append(" (PanScan)");
                    }
                    if ((_dvd.TitleSets[i].VideoAutoLetterbox || _dvd.TitleSets[i].VideoIsLetterBoxed)
                       && _dvd.TitleSets[i].VideoAutoPanScan)
                    {
                        sb.Append(" (Letterboxed/PanScan)");
                    }
                    sb.Append(", " + _dvd.TitleSets[i].VideoResolution);
                    sb.Append(",  " + _dvd.TitleSets[i].VideoCodingMode);
                    sb.Append(",  " + _dvd.TitleSets[i].VideoFramerate);
                    if (_dvd.TitleSets[i].ProgramChains[j].AnglesCount > 1)
                    {
                        sb.Append(",  " + _dvd.TitleSets[i].ProgramChains[j].AnglesCount.ToString() + " Angles");
                    }

                    TreeNode videoNode = node.Nodes.Add(sb.ToString());
                    videoNode.Checked = _dvd.TitleSets[i].ProgramChains[j].Checked;
                    videoNode.ImageIndex = 1;
                    videoNode.SelectedImageIndex = 1;
                    videoNode.StateImageIndex = 1;
                    videoNode.Tag = new Int32[3] { (Int32)DvdMediaNode.Video, i, j};
                    sb.Length = 0;

                    //
                    // Show the audio streams.
                    //

                    // Some DVDs have duplicate audio tracks.
                    List<Int32> audioIDs = new List<Int32>();
                    for (int k = 0; k < _dvd.TitleSets[i].ProgramChains[j].AudioStreamRefs.Length; k++)
                    {
                        Dvd.AudioStreamRef audioRef = _dvd.TitleSets[i].ProgramChains[j].AudioStreamRefs[k];

                        if (audioRef.IsPresent && !audioIDs.Contains(audioRef.TrackId))
                        {
                            audioIDs.Add(audioRef.TrackId);
                            Dvd.AudioStream audio = _dvd.TitleSets[i].AudioStreams[audioRef.Index];

                            sb.Append(audio.CodingMode + ",  ");
                            sb.Append(audio.ChannelCount.ToString() + "ch");
                            if (audio.Language.Length > 0 && audio.Language != Dvd.Utility.UnspecifiedString)
                            {
                                sb.Append(",  " + audio.Language);
                            }
                            if (audio.CodeExtension.Length > 0 && audio.CodeExtension != Dvd.Utility.UnspecifiedString)
                            {
                                sb.Append(",  " + audio.CodeExtension);
                            }
                            sb.Append("  [" + audioRef.TrackId.ToString("X2") + "]");
                       
                            TreeNode audioNode = node.Nodes.Add(sb.ToString());
                            audioNode.Checked = audioRef.Checked;
                            audioNode.ImageIndex = 2;
                            audioNode.SelectedImageIndex = 2;
                            audioNode.StateImageIndex = 2;
                            audioNode.Tag = new Int32[4] { (Int32)DvdMediaNode.Audio, i, j, audioRef.Index};
                            sb.Length = 0;
                        }
                    }

                    //
                    // Show the subpicture streams (subtitles).
                    //
                    for (int k = 0; k < _dvd.TitleSets[i].ProgramChains[j].Subtitles.Length; k++)
                    {
                        Dvd.Subtitle subtitle = _dvd.TitleSets[i].ProgramChains[j].Subtitles[k];

                        if (subtitle.Language.Length > 0 && subtitle.Language != Dvd.Utility.UnspecifiedString)
                        {
                            sb.Append(subtitle.Language + ",  ");
                        }
                        if (subtitle.Description.Length > 0 && subtitle.Description != Dvd.Utility.UnspecifiedString && subtitle.Description != Dvd.Utility.UnknownString)
                        {
                            sb.Append(subtitle.Description + ",  ");
                        }
                        sb.Append(Dvd.Utility.GetSubtitleAspectName(subtitle.Aspect) + ",  ");
                        sb.Append("[" + (subtitle.SubpictureIndex + 1).ToString() +"/" + subtitle.TrackId.ToString("X2") +"]");
                     
                        TreeNode subpictureNode = node.Nodes.Add(sb.ToString());
                        subpictureNode.Checked = subtitle.Checked;
                        subpictureNode.ImageIndex = 3;
                        subpictureNode.SelectedImageIndex = 3;
                        subpictureNode.StateImageIndex = 3;
                        subpictureNode.Tag = new Int32[4] { (Int32)DvdMediaNode.Subtitle, i, j, k };
                        sb.Length = 0;
                    }
                }
            }
        }

        /// <summary>
        /// Updates details about the source DVD.
        /// </summary>
        private void UpdateSourceDetails()
        {
            if (_selectedTitleSetIndex == -1 || _selectedProgramChainIndex == -1)
            {
                return;
            }

            StringBuilder sb = new StringBuilder();
            if (_dvd.TitleSets[_selectedTitleSetIndex].ProgramChains[_selectedProgramChainIndex].AnglesCount > 1)
            {
                sb.Append(_dvd.TitleSets[_selectedTitleSetIndex].ProgramChains[_selectedProgramChainIndex].AnglesCount.ToString() + " Angles,  ");
            }
            sb.Append(_dvd.TitleSets[_selectedTitleSetIndex].ProgramChains[_selectedProgramChainIndex].Programs.Length.ToString() + " " +
            FicSprout.Bacicworx.Text.Utility.Pluralize("Chapter", "Chapters",
            _dvd.TitleSets[_selectedTitleSetIndex].ProgramChains[_selectedProgramChainIndex].Programs.Length) + ",  ");
            sb.Append(_dvd.TitleSets[_selectedTitleSetIndex].ProgramChains[_selectedProgramChainIndex].Cells.Length.ToString() + " " +
                FicSprout.Bacicworx.Text.Utility.Pluralize("Cell", "Cells",
                _dvd.TitleSets[_selectedTitleSetIndex].ProgramChains[_selectedProgramChainIndex].Cells.Length) + ",  ");
            sb.Append((_dvd.TitleSets[_selectedTitleSetIndex].ProgramChains[_selectedProgramChainIndex].Length / 0x400).ToString("#,#") + " KB");

            labelTitleSetDetails.Text = sb.ToString();
            sb.Length = 0;
        }

        #endregion

        #region Events

        private void buttonBrowseDvdOutputPath_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();
            dialog.Description = "Select a destination folder.";
            dialog.SelectedPath = _dvdOutputPath;

            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                _dvdOutputPath = dialog.SelectedPath;
                textBoxDvdOutputPath.Text = _dvdOutputPath;
                UpdateMenus();
            }

            dialog.Dispose();
            dialog = null;
        }

        private void buttonBrowseDvdOutputPath_MouseDown(object sender, MouseEventArgs e)
        {
            buttonBrowseDvdOutputPath.Image = imageList1.Images[3];
        }

        private void buttonBrowseDvdOutputPath_MouseUp(object sender, MouseEventArgs e)
        {
            buttonBrowseDvdOutputPath.Image = imageList1.Images[2];
        }

        private void buttonBrowseDvdSourcePath_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();
            dialog.Description = "Select the DVD drive or 'VIDEO_TS' folder.";
            dialog.SelectedPath = _dvdSourcePath;
            dialog.ShowNewFolderButton = false;

            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.Cancel)
            {
                dialog.Dispose();
                dialog = null;
                return;
            }

            // DVD found?
            if (!System.IO.File.Exists(dialog.SelectedPath + "\\VIDEO_TS.IFO") &&
                    !System.IO.File.Exists(dialog.SelectedPath + "\\VIDEO_TS\\VIDEO_TS.IFO"))
            {
                MessageBox.Show("The drive or folder does not contain DVD content.", "DVD Not Found",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                dialog.Dispose();
                dialog = null;
                return;
            }

            // Adjust path.
            _dvdSourcePath = dialog.SelectedPath + "\\";
            if (System.IO.File.Exists(dialog.SelectedPath + "\\VIDEO_TS\\VIDEO_TS.IFO"))
            {
                _dvdSourcePath = _dvdSourcePath + "VIDEO_TS\\";
            }
            _dvdSourcePath = _dvdSourcePath.Replace("\\\\", "\\");

            // Opening another DVD? Close the current instance.
            if (_dvd != null)
            {
                _dvd.Close();
                _dvd = null;
            }

            // Open the new DVD.
            _dvd = new Dvd.IfoReader(_dvdSourcePath);

            // Update UI.
            labelDvdRegions.Text = "Region: " + _dvd.RegionString;
            textBoxDvdSourcePath.Text = _dvdSourcePath;
            UpdateSourceTree();

            // Select the largest PGC.
            _selectedTitleSetIndex = _dvd.MainTitleSetId - 1;
            _selectedProgramChainIndex = _dvd.MainProgramChainId - 1;
            treeViewTitleSets.SelectedNode = treeViewTitleSets.Nodes[_selectedTitleSetIndex].Nodes[_selectedProgramChainIndex];
            treeViewTitleSets.SelectedNode.EnsureVisible();
            treeViewTitleSets.Focus();

            UpdateMenus();
            UpdateSourceDetails();

            dialog.Dispose();
            dialog = null;
        }

        private void buttonBrowseDvdSourcePath_MouseDown(object sender, MouseEventArgs e)
        {
            buttonBrowseDvdSourcePath.Image = imageList1.Images[1];
        }

        private void buttonBrowseDvdSourcePath_MouseUp(object sender, MouseEventArgs e)
        {
            buttonBrowseDvdSourcePath.Image = imageList1.Images[0];
        }

        private void buttonDemux_Click(object sender, EventArgs e)
        {
            // Enough free space to import the selected content? Double estimate because of demuxing, encoding,
            // and other space requirements.
            Int64 value = Convert.ToInt64(System.Math.Ceiling((_dvd.CheckedProgramsLength * 2.5) -
                (new System.IO.DriveInfo(_dvdOutputPath).AvailableFreeSpace)));
            if (value > 0)
            {
                MessageBox.Show("Cannot demux the DVD because there is not enough free\n" +
                                "disk space.\n\n" +
                                "Unselect DVD items or delete unnecessary files from the\n" +
                                "disk to increase free space.\n\n" +
                                "Requires at least " + (value / 0x100000).ToString() + " MB additional free space.",
                                "Low Disk Space", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            // Get selections.
            _selections = new DemuxPgcSelection[_dvd.CheckedProgramsCount];
            Int32 index = 0;

            for (int i = 0; i < _dvd.TitleSets.Length; i++)
            {
                for (int j = 0; j < _dvd.TitleSets[i].ProgramChains.Length; j++)
                {
                    if (_dvd.TitleSets[i].ProgramChains[j].VideoChecked ||
                        _dvd.TitleSets[i].ProgramChains[j].CheckedAudioStreamsCount > 0 ||
                        _dvd.TitleSets[i].ProgramChains[j].CheckedSubtitlesCount > 0)
                    {
                        DemuxPgcSelection selection = new DemuxPgcSelection();

                        // Vob in multiple pieces?
                        selection.IfoFilename = _dvd.TitleSets[i].IfoFilename;
                        selection.VobFilenames = Dvd.Utility.GetVideoTitleSetVobFilenames(
                            _dvd.TitleSets[i].VobFilename, _dvd.TitleSets[i].Parts);
                        selection.VideoLength = _dvd.TitleSets[i].ProgramChains[j].Length;
                        selection.VideoOutputFilename = _dvd.TitleSets[i].ProgramChains[j].VideoOutputFilename;
                        selection.Angle = 1;
                        selection.Pgc = _dvd.TitleSets[i].ProgramChains[j].Id;
                        selection.AudioIDs = _dvd.TitleSets[i].ProgramChains[j].GetCheckedAudioTrackIds();
                        selection.AudioOutputFilenames = _dvd.TitleSets[i].ProgramChains[j].GetCheckedAudioOutputFilenames();
                        selection.SubtitleIDs = _dvd.TitleSets[i].ProgramChains[j].GetCheckedSubtitleTrackIds();
                        selection.SubtitleOutputFilenames = _dvd.TitleSets[i].ProgramChains[j].GetCheckedSubtitleOutputFilenames();
                        selection.Titleset = _dvd.TitleSets[i].Id;

                        if (_dvd.TitleSets[i].ProgramChains[j].VideoChecked)
                        {
                            selection.VideoID = _dvd.TitleSets[i].ProgramChains[j].VideoIDString;
                        }

                        _selections[index] = selection;
                        index++;

                        // Write chapters in Scenarist format.
                        System.IO.File.WriteAllLines(_dvdOutputPath + "\\VTS_" + _dvd.TitleSets[i].Id.ToString("00") +
                            "_PGC_" + _dvd.TitleSets[i].ProgramChains[j].Id.ToString("00") +
                            " - Chapter Information - Scenarist.txt",
                        _dvd.TitleSets[i].ProgramChains[j].GetChapterTimecodes());

                        // Write chapters in IfoEdit format.
                        System.IO.File.WriteAllLines(_dvdOutputPath + "\\VTS_" + _dvd.TitleSets[i].Id.ToString("00") +
                            "_PGC_" + _dvd.TitleSets[i].ProgramChains[j].Id.ToString("00") +
                            " - Chapter Information - IfoEdit.txt",
                        _dvd.TitleSets[i].ProgramChains[j].GetChapterEndFrames());

                        // Write chapters in CCE format.
                        string[] values = _dvd.TitleSets[i].ProgramChains[j].GetChapterEndFrames();
                        for (int k = values.Length - 1; k > 0; k--)
                        {
                            values[k] = "chapter=" + values[k - 1] + ":";
                        }
                        values[0] = "chapter=0:";

                        System.IO.File.WriteAllLines(_dvdOutputPath + "\\VTS_" + _dvd.TitleSets[i].Id.ToString("00") +
                            "_PGC_" + _dvd.TitleSets[i].ProgramChains[j].Id.ToString("00") +
                            " - Chapter Information - CCE.txt", values);
                    }
                }
            }

            // Selections to demux?
            if (_selections.Length > 0)
            {
                DemuxDvdProgressDialog dialogProgress = new DemuxDvdProgressDialog();
                dialogProgress.DvdOutputPath = _dvdOutputPath;
                dialogProgress.Selections = _selections;
                dialogProgress.SelectionsLength = _selections.Length;

                dialogProgress.ShowDialog();

                dialogProgress.Close();
                dialogProgress.Dispose();
                dialogProgress = null;
            }

            _selections = null;
        }

        private void contextMenuItemTitleSetsCollapseAll_Click(object sender, EventArgs e)
        {
            treeViewTitleSets.CollapseAll();
        }

        private void contextMenuItemTitleSetsExpandAll_Click(object sender, EventArgs e)
        {
            treeViewTitleSets.ExpandAll();
        }

        private void contextMenuItemTitleSetsSelectAll_Click(object sender, EventArgs e)
        {
            CheckAllTitleSetNodes(true);
            RestoreSourceTreeSelection();
        }

        private void contextMenuItemTitleSetsSelectNone_Click(object sender, EventArgs e)
        {
            CheckAllTitleSetNodes(false);
            RestoreSourceTreeSelection();
        }

        private void contextMenuItemTitleSetsUncheckSubtitles_Click(object sender, EventArgs e)
        {
            UncheckSubtitles();
            RestoreSourceTreeSelection();
        }

        private void pictureBoxChapters_Click(object sender, EventArgs e)
        {
            DvdPgcChaptersDialog dialog = new DvdPgcChaptersDialog();
            dialog.Chapters = _dvd.TitleSets[_selectedTitleSetIndex].ProgramChains[_selectedProgramChainIndex].GetChapters();

            dialog.ShowDialog();

            dialog.Close();
            dialog.Dispose();
            dialog = null;
        }

        private void pictureBoxChapters_MouseDown(object sender, MouseEventArgs e)
        {
            pictureBoxChapters.Image = imageList2.Images[5];
        }

        private void pictureBoxChapters_MouseUp(object sender, MouseEventArgs e)
        {
            pictureBoxChapters.Image = imageList2.Images[4];
        }

        private void treeViewTitleSets_AfterCheck(object sender, TreeViewEventArgs e)
        {
            // The code only executes if the user caused the checked state to change.
            if (e.Action != TreeViewAction.Unknown)
            {
                if (e.Node.Nodes.Count > 0)
                {
                    // Check all children
                    this.CheckAllTitleSetChildNodes(e.Node, e.Node.Checked);
                }
                else
                {
                    // Update internal Checked properties.
                    Int32[] ids = (Int32[])e.Node.Tag;

                    if (ids.Length == 3 || ids.Length == 4)
                    {
                        // Video checked changed?
                        if ((DvdMediaNode)ids[0] == DvdMediaNode.Video)
                        {
                            _dvd.TitleSets[ids[1]].ProgramChains[ids[2]].VideoChecked = e.Node.Checked;
                        }

                        // Audio checked changed? Node Tag property contains 3 values instead of two.
                        if ((DvdMediaNode)ids[0] == DvdMediaNode.Audio)
                        {
                            _dvd.TitleSets[ids[1]].ProgramChains[ids[2]].AudioStreamRefs[ids[3]].Checked = e.Node.Checked;
                        }

                        // Subtitle checked changed? Node Tag property contains 3 values instead of two.
                        if ((DvdMediaNode)ids[0] == DvdMediaNode.Subtitle)
                        {
                            _dvd.TitleSets[ids[1]].ProgramChains[ids[2]].Subtitles[ids[3]].Checked = e.Node.Checked;
                        }
                    }
                }
            }
        }

        private void treeViewTitleSets_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            Int32[] ids = (Int32[])e.Node.Tag;

            if (ids != null && (ids.Length == 3 || ids.Length == 4))
            {
                _selectedProgramChainIndex = ids[2];
                _selectedTitleSetIndex = ids[1];

                UpdateSourceDetails();
                UpdateMenus();
            }
        }

        #endregion
    }
}
