using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Threading;

/*
    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>
    /// Utilizes a BackgroundWorker to run the DVD demuxer in a seperate thread.
    /// </summary>
    sealed public class DemuxerBackgroundWorker
    {
        #region Fields

        private Dvd.IfoReader _dvd = null;
        private Dvd.Demuxer _dvdDemuxer = null;
        private DateTime _lastRaisedDateTime = DateTime.Now;
        private string _outputPath = string.Empty;
        private FicSprout.Bacicworx.IO.JobStatus _status = FicSprout.Bacicworx.IO.JobStatus.Inactive;
        private BackgroundWorker _worker = null;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the audio stream delays in milliseconds. The array contains eight items that are synced
        /// to the AudioIds property. Any AudioId with a value of zero indicates that that stream is not
        /// present and the delay value is invalid.
        /// </summary>
        public Int32[] AudioDelays
        {
            get
            {
                return _dvdDemuxer.AudioDelays;
            }
        }
        /// <summary>
        /// Gets the audio stream IDs. The array contains eight items. Any item with
        /// a value of zero indicates that that stream is not present.
        /// 
        /// </summary>
        public Int32[] AudioIds
        {
            get
            {
                return _dvdDemuxer.AudioIds;
            }
        }
        /// <summary>
        /// Gets the number of audio packets detected.
        /// </summary>
        public Int32 AudioPackets
        {
            get
            {
                return _dvdDemuxer.AudioPackets;
            }
        }
        /// <summary>
        /// Gets a list of programs that have damaged packets. If none were detected, an empty
        /// list is returned.
        /// </summary>
        public List<string> DamagedPrograms
        {
            get
            {
                return _dvdDemuxer.DamagedPrograms;
            }
        }
        /// <summary>
        /// Gets the number of navigation packets detected.
        /// </summary>
        public Int32 NavigationPackets
        {
            get
            {
                return _dvdDemuxer.NavigationPackets;
            }
        }
        /// <summary>
        /// Gets the output path.
        /// </summary>
        public string OutputPath
        {
            get
            {
                return _outputPath;
            }
        }
        /// <summary>
        /// Gets the number of padding packets detected.
        /// </summary>
        public Int32 PaddingPackets
        {
            get
            {
                return _dvdDemuxer.PaddingPackets;
            }
        }
        /// <summary>
        /// Gets a value indicating whether the demuxer is paused.
        /// </summary>
        public Boolean Paused
        {
            get
            {
                return _dvdDemuxer.Paused;
            }
        }
        /// <summary>
        /// Gets the id of the program chain currently being processed. If Inactive, zero is returned.
        /// </summary>
        public Int32 ProgramChain
        {
            get
            {
                return _dvdDemuxer.ProgramChain;
            }
        }
        /// <summary>
        /// Gets the length in bytes of the current program chain. If Inactive, zero is returned.
        /// </summary>
        public Int64 ProgramChainLength
        {
            get
            {
                return _dvdDemuxer.ProgramChainLength;
            }
        }
        /// <summary>
        /// Gets the number in bytes that have been processed for the current program chain. If
        /// Inactive, zero is returned.
        /// </summary>
        public Int64 ProgramChainProcessedLength
        {
            get
            {
                return _dvdDemuxer.ProgramChainProcessedLength;
            }
        }
        /// <summary>
        /// Gets the progress percentage for the current program chain. If
        /// Inactive, zero is returned.
        /// </summary>
        public Int32 ProgramChainProgress
        {
            get
            {
                return _dvdDemuxer.ProgramChainProgress;
            }
        }
        /// <summary>
        /// Gets the time remaining for completion of the current program chain. If
        /// Inactive, zero is returned.
        /// </summary>
        public FicSprout.Bacicworx.Media.Timecode ProgramChainTimeRemaining
        {
            get
            {
                return _dvdDemuxer.ProgramChainTimeRemaining;
            }
        }
        /// <summary>
        /// Gets the status of the worker.
        /// </summary>
        public FicSprout.Bacicworx.IO.JobStatus Status
        {
            get
            {
                return _status;
            }
        }
        /// <summary>
        /// Gets the subpicture stream delays in milliseconds. The array contains thirty-two items
        /// that are synced to the SubpictureIds property. Any SubpictureId with a value of zero
        /// indicates that that stream is not present and the delay value is invalid.
        /// </summary>
        public Int32[] SubpictureDelays
        {
            get
            {
                return _dvdDemuxer.SubpictureDelays;
            }
        }
        /// <summary>
        /// Gets the subpicture stream IDs. The array contains thirty-two items. Any item with
        /// a value of zero indicates that that stream is not present.
        /// 
        /// </summary>
        public Int32[] SubpictureIds
        {
            get
            {
                return _dvdDemuxer.SubpictureIds;
            }
        }
        /// <summary>
        /// Gets the number of subpicture packets detected.
        /// </summary>
        public Int32 SubpicturePackets
        {
            get
            {
                return _dvdDemuxer.SubpicturePackets;
            }
        }
        /// <summary>
        /// Gets the id of the title set currently being processed. If Inactive, zero is returned.
        /// </summary>
        public Int32 TitleSet
        {
            get
            {
                return _dvdDemuxer.TitleSet;
            }
        }
        /// <summary>
        /// Gets the total number of packets detected.
        /// </summary>
        public Int32 TotalPackets
        {
            get
            {
                return _dvdDemuxer.TotalPackets;
            }
        }
        /// <summary>
        /// Gets the percentage of the total operation completed. The value range is 0 to 100.
        /// </summary>
        public Int32 TotalProgress
        {
            get
            {
                return _dvdDemuxer.TotalProgress;
            }
        }
        /// <summary>
        /// Gets the number of unknown packets detected. The value should be zero.
        /// </summary>
        public Int32 UnknownPackets
        {
            get
            {
                return _dvdDemuxer.UnknownPackets;
            }
        }
        /// <summary>
        /// Gets the output filename of the video currently being processed. If Inactive,
        /// an empty string is returned.
        /// </summary>
        public string VideoFilename
        {
            get
            {
                return _dvdDemuxer.VideoFilename;
            }
        }
        /// <summary>
        /// Gets the number of video packets detected.
        /// </summary>
        public Int32 VideoPackets
        {
            get
            {
                return _dvdDemuxer.VideoPackets;
            }
        }
        /// <summary>
        /// Gets the filename of the vob currently being processed. If Inactive,
        /// an empty string is returned.
        /// </summary>
        public string VobFilename
        {
            get
            {
                return _dvdDemuxer.VobFilename;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Creates a new instance of the DemuxerBackgroundWorker class to demux vob streams.
        /// </summary>
        /// <param name="dvd">The dvd information.</param>
        /// <param name="path">The output path for the demuxed files.</param>
        /// <exception cref="System.ArgumentNullException">Thrown when dvd is null.</exception>
        /// <exception cref="System.ArgumentException">Thrown when path is null or empty.</exception>
        /// <exception cref="System.IO.DirectoryNotFoundException">Thrown when path is not found.</exception>
        public DemuxerBackgroundWorker(Dvd.IfoReader dvd, string path)
        {
            if (dvd == null)
            {
                throw new ArgumentNullException("dvd");
            }
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentException("path is null or empty.");
            }
            if (!System.IO.Directory.Exists(path))
            {
                throw new System.IO.DirectoryNotFoundException("'" + path + "' not found.");
            }
            
            _dvd = dvd;
            _outputPath = path;
        }

        /// <summary>
        /// Cancels the worker.
        /// </summary>
        public void Cancel()
        {
            if (_worker != null && _dvdDemuxer != null)
            {
                _dvdDemuxer.Cancel();
            }
        }

        /// <summary>
        /// Toggles the demuxer between pause and resume.
        /// </summary>
        public void Pause()
        {
            _dvdDemuxer.Pause();
        }

        /// <summary>
        /// Starts the worker.
        /// </summary>
        public void Start()
        {
            _worker = new BackgroundWorker();

            _worker.WorkerSupportsCancellation = true;
            _worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            _worker.RunWorkerAsync();
        }

        #endregion

        #region Published Events

        /// <summary>
        /// Raised when the worker has exited.
        /// </summary>
        public event EventHandler Completed;

        /// <summary>
        /// Raises the OnCompleted event.
        /// </summary>
        public void OnCompleted()
        {
            // Event assigned?
            if (Completed != null && _worker != null)
            {
                // Throttle event if it has been less than 50 milliseconds since we last raised an event.
                if (DateTime.Now.Subtract(_lastRaisedDateTime).Milliseconds < 50)
                {
                    Thread.Sleep(50);
                }

                _lastRaisedDateTime = DateTime.Now;
                Completed(this, EventArgs.Empty);
            }
        }

        #endregion

        #region Subscribed Events

        /// <summary>
        /// Starts the worker.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event arguments.</param>
        private void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            _status = FicSprout.Bacicworx.IO.JobStatus.Active;

            // Start the demuxer.
            _dvdDemuxer = new Demuxer(_dvd, _outputPath);
            _dvdDemuxer.Start();

            // Finished.
            _status = FicSprout.Bacicworx.IO.JobStatus.Completed;

            // Cancelled? Failure? Cleanup folder and exit.
            if ( _dvdDemuxer.Cancelled)
            {
                _status = FicSprout.Bacicworx.IO.JobStatus.Cancelled;
            }
            // Damaged packets found?
            if (_dvdDemuxer.DamagedPrograms.Count > 0)
            {
                _status = FicSprout.Bacicworx.IO.JobStatus.CompletedWarning;
            }

            OnCompleted();
        }

        #endregion
    }
}
