#pragma once

// hookman.h
// 8/23/2013 jichi
// Branch: ITH/HookManager.h, rev 133

#include "host/avl_p.h"
#include "host/textthread.h"
#include "winmutex/winmutex.h"

enum { MAX_REGISTER = 0xf };
enum { MAX_PREV_REPEAT_LENGTH = 0x20 };

struct ProcessRecord {
  DWORD pid_register;
  DWORD hookman_register;
  DWORD module_register;
  //DWORD engine_register; // jichi 10/19/2014: removed
  HANDLE process_handle;
  HANDLE hookman_mutex;
  HANDLE hookman_section;
  LPVOID hookman_map;
};

class ThreadTable : public MyVector<TextThread *, 0x40>
{
public:
  virtual void SetThread(DWORD number, TextThread *ptr);
  virtual TextThread *FindThread(DWORD number);
};

struct TCmp { char operator()(const ThreadParameter *t1,const ThreadParameter *t2); };
struct TCpy { void operator()(ThreadParameter *t1,const ThreadParameter *t2); };
struct TLen { int operator()(const ThreadParameter *t); };

typedef DWORD (*ProcessEventCallback)(DWORD pid);

class HookManager : public AVLTree<ThreadParameter, DWORD, TCmp, TCpy, TLen>
{
public:
  HookManager();
  ~HookManager();
  // jichi 12/26/2013: remove virtual modifiers
  TextThread *FindSingle(DWORD pid, DWORD hook, DWORD retn, DWORD split);
  TextThread *FindSingle(DWORD number);
  ProcessRecord *GetProcessRecord(DWORD pid);
  DWORD GetProcessIDByPath(LPCWSTR str);
  void RemoveSingleThread(DWORD number);
  //void LockHookman();
  //void UnlockHookman();
  void ResetRepeatStatus();
  void ClearCurrent();
  void AddLink(WORD from, WORD to);
  void UnLink(WORD from);
  void UnLinkAll(WORD from);
  void SelectCurrent(DWORD num);
  void DetachProcess(DWORD pid);
  void SetCurrent(TextThread *it);
  //void AddConsoleOutput(LPCWSTR text);

  // jichi 10/27/2013: Add const; add space.
  void DispatchText(DWORD pid, const BYTE *text, DWORD hook, DWORD retn, DWORD split, int len, bool space);

  void ClearText(DWORD pid, DWORD hook, DWORD retn, DWORD split);
  void RemoveProcessContext(DWORD pid);
  void RemoveSingleHook(DWORD pid, DWORD addr);
  void RegisterThread(TextThread*, DWORD);
  void RegisterPipe(HANDLE text, HANDLE cmd, HANDLE thread);
  void RegisterProcess(DWORD pid, DWORD hookman, DWORD module);
  void UnRegisterProcess(DWORD pid);
  //void SetName(DWORD);

  DWORD GetCurrentPID();
  HANDLE GetCmdHandleByPID(DWORD pid);

  //ConsoleCallback RegisterConsoleCallback(ConsoleCallback cf)
  //{ return (ConsoleCallback)_InterlockedExchange((long*)&console,(long)cf); }

  //ConsoleWCallback RegisterConsoleWCallback(ConsoleWCallback cf)
  //{ return (ConsoleWCallback)_InterlockedExchange((long*)&wconsole,(long)cf); }

  ThreadEventCallback RegisterThreadCreateCallback(ThreadEventCallback cf)
  { return (ThreadEventCallback)_InterlockedExchange((long*)&create,(long)cf); }

  ThreadEventCallback RegisterThreadRemoveCallback(ThreadEventCallback cf)
  { return (ThreadEventCallback)_InterlockedExchange((long*)&remove,(long)cf); }

  ThreadEventCallback RegisterThreadResetCallback(ThreadEventCallback cf)
  { return (ThreadEventCallback)_InterlockedExchange((long*)&reset,(long)cf); }

  ProcessEventCallback RegisterProcessAttachCallback(ProcessEventCallback cf)
  { return (ProcessEventCallback)_InterlockedExchange((long*)&attach,(long)cf); }

  ProcessEventCallback RegisterProcessDetachCallback(ProcessEventCallback cf)
  { return (ProcessEventCallback)_InterlockedExchange((long*)&detach,(long)cf); }

  ProcessEventCallback RegisterProcessNewHookCallback(ProcessEventCallback cf)
  { return (ProcessEventCallback)_InterlockedExchange((long*)&hook,(long)cf); }

  ProcessEventCallback ProcessNewHook() { return hook; }
  TextThread *GetCurrentThread() { return current; }
  ProcessRecord *Records() { return record; }
  ThreadTable *Table() { return thread_table; }

  //DWORD& SplitTime() { return split_time; }
  //DWORD& RepeatCount() { return repeat_count; }
  //DWORD& CyclicRemove() { return cyclic_remove; }
  //DWORD& GlobalFilter() { return global_filter; }
  //void ConsoleOutput(LPCSTR text) { if (console) console(text); } // not thread safe
  //void ConsoleOutputW(LPCWSTR text) { if (wconsole) wconsole(text); } // not thread safe

private:
  typedef win_mutex<CRITICAL_SECTION> mutex_type;
  mutex_type hmcs;

  TextThread *current;
  //ConsoleCallback console; // jichi 12/25/2013: add console output callback
  //ConsoleWCallback wconsole;
  ThreadEventCallback create,
                      remove,
                      reset;
  ProcessEventCallback attach,
                       detach,
                       hook;
  DWORD current_pid;
  ThreadTable *thread_table;
  HANDLE destroy_event;
  ProcessRecord record[MAX_REGISTER + 1];
  HANDLE text_pipes[MAX_REGISTER + 1],
         cmd_pipes[MAX_REGISTER + 1],
         recv_threads[MAX_REGISTER + 1];
  WORD register_count,
       new_thread_number;

  // jichi 1/16/2014: Stop adding new threads when full
  bool IsFull() const; // { return new_thread_number >= MAX_HOOK; }
  bool IsEmpty() const { return !new_thread_number; }
};

// EOF
