#ifndef RTORRENT_CORE_MANAGER_H
#define RTORRENT_CORE_MANAGER_H

#include <iosfwd>
#include <memory>
#include <vector>
#include <torrent/utils/log_buffer.h>
#include <torrent/object.h>

#include "download_list.h"
#include "range_map.h"

namespace torrent {
  class Bencode;
}

namespace utils {
class FileStatusCache;
}

namespace core {

class HttpQueue;

using ThrottlePair = std::pair<torrent::Throttle*, torrent::Throttle*>;
using ThrottleMap  =  std::map<std::string, ThrottlePair>;

class View;

class Manager {
public:
  typedef DownloadList::iterator                    DListItr;
  typedef utils::FileStatusCache                    FileStatusCache;

  Manager();
  ~Manager();

  bool                is_download_shutdown_completed();

  DownloadList*       download_list()                   { return m_download_list.get(); }
  FileStatusCache*    file_status_cache()               { return m_file_status_cache.get(); }

  HttpQueue*          http_queue()                      { return m_http_queue.get(); }

  View*               hashing_view()                    { return m_hashingView; }
  void                set_hashing_view(View* v);

  auto*               log_important()                   { return m_log_important.get(); }
  auto*               log_complete()                    { return m_log_complete.get(); }

  ThrottleMap&        throttles()                       { return m_throttles; }
  ThrottlePair        get_throttle(const std::string& name);

  int64_t             retrieve_throttle_value(const torrent::Object::string_type& name, bool rate, bool up);

  void                cleanup();

  const std::string&  magnet_path();
  void                set_magnet_path(const std::string& path);

  void                shutdown(bool force);

  void                push_log(const char* msg);
  void                push_log_std(const std::string& msg) { m_log_important->lock_and_push_log(msg.c_str(), msg.size(), 0); m_log_complete->lock_and_push_log(msg.c_str(), msg.size(), 0); }
  void                push_log_complete(const std::string& msg) { m_log_complete->lock_and_push_log(msg.c_str(), msg.size(), 0); }

  void                handshake_log(const sockaddr* sa, int msg, int err, const torrent::HashString* hash);

  static const int create_start    = 0x1;
  static const int create_tied     = 0x2;
  static const int create_quiet    = 0x4;
  static const int create_raw_data = 0x8;

  typedef std::vector<std::string> command_list_type;

  // Temporary, find a better place for this.
  void                try_create_download(const std::string& uri, int flags, const command_list_type& commands);
  void                try_create_download_expand(const std::string& uri, int flags, command_list_type commands = command_list_type());
  void                try_create_download_from_meta_download(torrent::Object* bencode, const std::string& metafile);

private:
  void                create_http(const std::string& uri);
  void                create_final(std::istream* s);

  void                initialize_bencode(Download* d);

  void                receive_http_failed(std::string msg);
  void                receive_hashing_changed();

  std::unique_ptr<DownloadList>    m_download_list;
  std::unique_ptr<FileStatusCache> m_file_status_cache;
  std::unique_ptr<HttpQueue>       m_http_queue;

  View*               m_hashingView{};

  ThrottleMap         m_throttles;

  torrent::log_buffer_ptr m_log_important;
  torrent::log_buffer_ptr m_log_complete;

  std::string         m_magnet_path;
};

// Meh, cleanup.
extern void receive_tracker_dump(const std::string& url, const char* data, size_t size);

inline const std::string& Manager::magnet_path() { return m_magnet_path; }

}

#endif
