#region Copyright (C) 2005-2010 Team MediaPortal

// Copyright (C) 2005-2010 Team MediaPortal
// http://www.team-mediaportal.com
// 
// MediaPortal 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 2 of the License, or
// (at your option) any later version.
// 
// MediaPortal 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 MediaPortal. If not, see <http://www.gnu.org/licenses/>.

#endregion

using System;
using System.Collections;
using System.Diagnostics;
using System.Threading;

namespace MediaPortal.Dispatcher
{
  public sealed class JobDispatcher
  {
    #region Methods

    private void Clear()
    {
      _jobs.Clear();
    }

    private void Dispatch()
    {
      _isRunning = true;

      try
      {
        Job job = null;

        for (;;)
        {
          lock (_dispatcher)
          {
            if (_isPaused || _jobs.Count == 0)
            {
              Monitor.Wait(_dispatcher);
            }

            if (_isRunning == false)
            {
              break;
            }

            job = (Job)_jobs.Peek();

            if (job.IsReady == false)
            {
              Monitor.Wait(_dispatcher, job.Next.Subtract(DateTime.Now));
            }

            if (_isRunning == false)
            {
              break;
            }

            if (job.IsReady == false)
            {
              continue;
            }

            _jobs.Dequeue();
          }

          job.Run();
          job = null;
        }
      }
      catch (ThreadInterruptedException)
      {
        Trace.WriteLine("JobDispatcher.Dispatch: ThreadInterruptException");
      }
      catch (Exception e)
      {
        Trace.WriteLine("JobDispatcher.Dispatch: {0}", e.Message);
      }

      _isRunning = false;
    }

    internal static void Dispatch(Job job)
    {
      lock (_dispatcher)
      {
        _jobs.Enqueue(job);

        Monitor.Pulse(_dispatcher);
      }
    }

    public static void Init()
    {
      lock (_dispatcher)
      {
        if (_isRunning)
        {
          throw new InvalidOperationException("Dispatcher is already initialized.");
        }

        _dispatcherThread = new Thread(new ThreadStart(_dispatcher.Dispatch));
        _dispatcherThread.IsBackground = true;
        _dispatcherThread.Priority = ThreadPriority.AboveNormal;
        _dispatcherThread.Name = "Dispatcher";
        _dispatcherThread.Start();

        // wait for the dispatcher thread to get going
        while (_isRunning == false)
        {
          Thread.Sleep(100);
        }
      }
    }

    public static void Pause()
    {
      lock (_dispatcher)
      {
        if (_isRunning == false)
        {
          throw new InvalidOperationException("Dispatcher is not running.");
        }

        if (_isPaused)
        {
          throw new InvalidOperationException("Dispatcher is already paused.");
        }

        _isPaused = true;
      }
    }

    public static void Resume()
    {
      lock (_dispatcher)
      {
        if (_isRunning == false)
        {
          throw new InvalidOperationException("Dispatcher is not running.");
        }

        if (_isPaused == false)
        {
          throw new InvalidOperationException("Dispatcher is already running.");
        }

        _isPaused = false;

        Monitor.Pulse(_dispatcher);
      }
    }

    public static void Term()
    {
      lock (_dispatcher)
      {
        //if(_isRunning == false)
        //	throw new InvalidOperationException("Dispatcher is already shutdown.");

        _isRunning = false;
        _dispatcher.Clear();

        Monitor.Pulse(_dispatcher);

        _dispatcherThread = null;
      }
    }

    #endregion Methods

    #region Fields

    private static JobDispatcher _dispatcher = new JobDispatcher();
    private static Thread _dispatcherThread;
    private static volatile bool _isRunning;
    private static volatile bool _isPaused;
    private static PriorityQueue _jobs = new PriorityQueue(new JobComparer());

    #endregion Fields
  }
}