#pragma once
#include "../Scheduler.h"
#include <boost/function.hpp>
#include <boost/thread/future.hpp>
#include <string>
#include <boost/shared_ptr.hpp>
#include <functional>

class Scheduler;

namespace MP
{
  class Task
  {
    friend class ::Scheduler;
  public:
    typedef unsigned long long time_point;
    typedef long long time_span;
    typedef id_type id_type;

    static time_point getCurrentTime()
    {
      FILETIME ft;
      ::GetSystemTimeAsFileTime(&ft);
      time_point time = static_cast<Task::time_point>(ft.dwHighDateTime) << 32;
      time |= ft.dwLowDateTime;
      return time;
    }
  private:
    const id_type m_id;
    const std::string m_name;
    const unsigned int m_priority;
    const time_point m_createTime;

    Task(const Task &);
    Task & operator=(const Task &);

  protected:
    Task() 
      : m_priority(0), m_id(0), m_createTime(0)
    {}
    Task(unsigned int priority, const std::string & name) 
      : m_priority(priority), m_name(name), m_id(s_currentId++), m_createTime(getCurrentTime())
    {}
  public:
    unsigned int getPriority() const { return m_priority; }
    const std::string & getName() const { return m_name; }
    unsigned int getId() const { return m_id; }
    time_point getCreateTime() const { return m_createTime; }

    virtual void execute() = 0;
    virtual ~Task() {}

    struct Comparer : public std::binary_function<const Task *, const Task *, bool>
    {
      bool operator()(const Task * l, const Task * r) const
      {
        if(l->getPriority() == r->getPriority())
          return l->getCreateTime() > r->getCreateTime();

        return l->getPriority() > r->getPriority();
      }
    };
  };

  typedef boost::shared_ptr<Task> TaskPtr;

  template<class T>
  struct NoOp { void operator()(const T &) {} };

  template<class ResultType, class Operation, class Callback = NoOp<ResultType> >
  class TaskImpl : public Task
  {
  public:
    typedef ResultType result_type;
    typedef boost::unique_future<result_type> task_result;
    typedef Operation operation;
    typedef Callback result_callback;
    boost::packaged_task<ResultType> m_task;
    task_result m_result;
    result_callback m_callback;
  public:
    TaskImpl(operation op)
      : m_task(op)
    {
      m_result = m_task.get_future();
    }
    TaskImpl(operation op, result_callback cb, unsigned int priority = 0, const std::string & name = std::string())
        : Task(priority, name), m_task(op), m_callback(cb)
    {
      m_result = m_task.get_future();
    }
    
    virtual void execute()
    {
      m_task();
      m_callback(m_result.get());
    }

    ResultType waitForCompletion()
    {
      m_result.wait();
      return m_result.get();
    }
  };

  template<class ResultType, class Callback>
  class CallbackTask : public Task
  {
  public:
    typedef Callback result_callback;
    result_callback m_callback;
    ResultType m_result;
  public:
    CallbackTask(result_callback op, const ResultType& result) 
      : m_callback(op), m_result(result)
    {
    }
    
    virtual void execute()
    {
      m_callback(m_result);
    }
  };
}