#pragma comment(lib, "user32")
#pragma comment(lib, "shell32")
#include <shellapi.h>

f64 GetUnixTimestamp ();
s64 GetUnixTimestampNanoseconds ();

enum class Wait_For_Result: s32 {
  SUCCESS = 0,
  TIMEOUT = 1,
  ERROR = 2 // can't use ERROR because of Windows.h *sigh*
};

internal void semaphore_init (Semaphore* sem, s32 initial_value = 0);
internal void semaphore_destroy (Semaphore* sem);
internal void signal (Semaphore* sem);
internal Wait_For_Result wait_for (Semaphore* sem, s32 milliseconds = -1);

internal void condition_variable_init (Condition_Variable* cv);
internal void condition_variable_destroy (Condition_Variable* cv);
internal void wait (Condition_Variable* cv, Mutex* mutex, s32 wait_time_ms = -1);
internal void wake (Condition_Variable* cv);
internal void wake_all (Condition_Variable* cv);

struct Thread; // #hack forward declares to get this to compile.
typedef s64 (*Thread_Proc)(Thread* thread); 

internal bool thread_init (Thread* thread, Thread_Proc proc, string thread_name="");
internal void thread_deinit (Thread* thread, bool zero_thread=false);
internal void thread_start (Thread* thread, void* thread_data = nullptr);
internal bool thread_is_done (Thread* thread, s32 milliseconds=0);

typedef u32 OS_Error_Code;
internal string get_error_string (OS_Error_Code error_code);

internal bool file_is_valid (File file);
internal File file_open (string file_path, bool for_writing=false, bool keep_existing_content=false, bool log_errors=false);
internal void file_close (File* file);
internal bool file_read (File file, void* data, s64 bytes_to_read_count, s64* bytes_read_count=nullptr);
internal bool file_length (File file, s64* length);
internal bool file_length (string file_path, s64* length);
internal s64 file_current_position (File file);
internal bool file_set_position (File file, s64 position);
internal ArrayView<u8> read_entire_file (File file, bool add_null_terminator);
internal ArrayView<u8> read_entire_file (string file_path, bool add_null_terminator=false, bool log_errors=false);

// use to_byte_view to convert ArrayView<non-u8> to ArrayView<u8>
internal bool file_write (File* file, void* data, s64 length);
internal bool write_entire_file (string file_path, void* file_data, s64 count);
internal bool write_entire_file (string file_path, ArrayView<u8> file_data);

internal bool file_exists (string file_path);

// file_write
// write_entire_file...

// #TODO #fs File System Operations
// file_move, file_delete

// #window_creation
typedef HWND Window_Type;
typedef HICON Window_Icon;

struct Window_Dimensions {
  s32 window_x;
  s32 window_y;
  s32 window_width;
  s32 window_height;
};

struct Window_Info {
  Window_Type window;
  Window_Dimensions initial_dimensions; // for resetting.
  Window_Icon icon;
  Window_Icon icon_minimized;
  
  bool is_main_window;
  bool tray_icon_added;
  bool minimized_to_tray;
  // Platform-Specific (Win32)
  HMENU tray_icon_menu;
  HDC hdc;
  // Likely will only be used for main window:
  NOTIFYICONDATAW nid;
};

// #move: Monitor - platform-independent hardware monitor information: 
struct Monitor {
  s64 left;
  s64 top;
  s64 right;
  s64 bottom;
  bool primary;
  bool present;
#if OS_WINDOWS
  MONITORINFO monitor_info;
#endif
};

enum class Win32_Drive_Type: s32 {
  Unknown     = 0,
  No_Root_Dir = 1,
  Removable   = 2,
  Fixed       = 3,
  Remote      = 4,
  cdrom       = 5,
  ramdisk     = 6
};

enum class File_System: s32 {
  Unknown = 0,
  MusaFS  = 1,
  exFAT   = 2,
  Ext2    = 65,
  Ext3    = 66,
  Ext4    = 67,
  Btrfs   = 79,
  XFS     = 86,
  ZFS     = 91,
  NTFS    = 128, // Windows
  ReFS    = 130, // Windows
  AFS     = 256, // Apple File System
  F2FS    = 1024,
  // Legacy systems:
  FAT32   = -1,
  // FAT16  :: -2;
  // FAT12  :: -3;
};

string to_string (Win32_Drive_Type type) {
  switch (type) {
    case Win32_Drive_Type::Unknown:     { return "Unknown";     } break;
    case Win32_Drive_Type::No_Root_Dir: { return "No_Root_Dir"; } break;
    case Win32_Drive_Type::Removable:   { return "Removable";   } break;
    case Win32_Drive_Type::Fixed:       { return "Fixed";       } break;
    case Win32_Drive_Type::Remote:      { return "Remote";      } break;
    case Win32_Drive_Type::cdrom:       { return "cdrom";       } break;
    case Win32_Drive_Type::ramdisk:     { return "ramdisk";     } break;
  }
  
  Assert(false);
  return "Unknown";
}

string to_string (File_System fs) {
  switch (fs) {
    case File_System::Unknown: { return "Unknown"; } break;
    case File_System::MusaFS:  { return "MusaFS";  } break;
    case File_System::exFAT:   { return "exFAT";   } break;
    case File_System::Ext2:    { return "Ext2";    } break;
    case File_System::Ext3:    { return "Ext3";    } break;
    case File_System::Ext4:    { return "Ext4";    } break;
    case File_System::Btrfs:   { return "Btrfs";   } break;
    case File_System::XFS:     { return "XFS";     } break;
    case File_System::ZFS:     { return "ZFS";     } break;
    case File_System::NTFS:    { return "NTFS";    } break;
    case File_System::ReFS:    { return "ReFS";    } break;
    case File_System::AFS:     { return "AFS";     } break;
    case File_System::F2FS:    { return "F2FS";    } break;
    case File_System::FAT32:   { return "FAT32";   } break;
  }
  
  Assert(false);
  return "Unknown";
}

File_System Win32_filesystem_from_string (string s) {
  string s_copy = to_lower_copy(s);

  if (s_copy == "ntfs")  { return File_System::NTFS; }
  if (s_copy == "refs")  { return File_System::ReFS; }
  if (s_copy == "fat")   { return File_System::FAT32; }
  if (s_copy == "exfat") { return File_System::exFAT; }
  
  Assert(false); 
  return File_System::Unknown;
}
struct Dense_FS; // #hack forward declare!

struct NTFS_USN_Journal {
  bool no_permission;
  HANDLE hVol;
  // ArrayView<USN_Journal_Change> changes;
};

struct Win32_Drive {
  string label;
  string volume_name;
  Win32_Drive_Type type;
  File_System file_system;
  s64 full_size;
  s64 free_space;
  u32 serial_number;
  u32 max_component_length;
  u32 file_system_flags;
  bool is_present;
    
  s64 bytes_accessed;
  s64 file_count;
  f32 time_to_enumerate;
  Dense_FS* data;
  
  NTFS_USN_Journal jrnl;
};

typedef Win32_Drive OS_Drive;

bool os_window_is_minimized (Window_Type window);

bool os_main_window_is_minimized ();

bool os_create_window (string new_window_name, Window_Type parent=nullptr, bool center_window=true, bool open_on_largest_monitor=true, bool display_window=true, void* wnd_proc_override=nullptr);

Window_Info get_main_window ();
Window_Info* get_main_window_pointer ();

string os_get_machine_name ();


