#define win32_open_files_cap   32
char    win32_open_file_paths[win32_open_files_cap][MAX_PATH];
u64     win32_open_files_len = 1; // zero is invalid
HANDLE  win32_open_files[win32_open_files_cap];

void
win32_files_init()
{
  for (u32 i = 0; i < win32_open_files_cap; i++)
  {
    win32_open_files[i] = INVALID_HANDLE_VALUE;
  }
}

HANDLE
win32_get_open_file_handle(Platform_File_Handle file)
{
  return win32_open_files[file.value];
}

internal u64
win32_high_low_to_u64(u32 low_part, u32 high_part)
{
  ULARGE_INTEGER Time = {};
  Time.LowPart = low_part;
  Time.HighPart = high_part;
  u64 result = Time.QuadPart;
  return result;
}

internal u64
win32_file_time_to_u64(FILETIME file_time)
{
  return win32_high_low_to_u64(file_time.dwLowDateTime, file_time.dwHighDateTime);
}

Platform_File_Handle
platform_file_open(String path, Platform_File_Access_Flags flags_access,  Platform_File_Create_Flags flags_create)
{
  Platform_File_Handle result = {};
  
  DWORD flags_create_ = OPEN_EXISTING;
  
  HANDLE file_handle = CreateFileA(
                                   (char*)path.str, 
                                   (DWORD)flags_access,
                                   0, // share mode
                                   NULL, // security attributes
                                   (DWORD)flags_create,
                                   FILE_ATTRIBUTE_NORMAL,
                                   NULL
                                   );
  
  if (file_handle != INVALID_HANDLE_VALUE)
  {
    if (win32_open_files_len < win32_open_files_cap)
    {
      result.value = win32_open_files_len++;
    }
    else
    {
      // search for emtpy index
      for (u32 i = 1; i < win32_open_files_cap; i++)
      {
        if (win32_open_files[i] == INVALID_HANDLE_VALUE)
        {
          result.value = i;
        }
      }
    }
    
    assert(result.value != 0);
    win32_open_files[result.value] = file_handle;
    
    memory_copy(path.str, (u8*)win32_open_file_paths[result.value], path.len);
    win32_open_file_paths[result.value][path.len] = 0; // null term
  }
  else
  {
    win32_get_last_error();
  }
  
  return result;
}

void
platform_file_close(Platform_File_Handle file_handle)
{
  assert(file_handle.value < win32_open_files_len);
  CloseHandle(win32_get_open_file_handle(file_handle));
  win32_open_files[file_handle.value] = INVALID_HANDLE_VALUE;
}

u64
win32_get_file_size(HANDLE h)
{
  DWORD size_low, size_high;
  size_low = GetFileSize(h, &size_high);
  if (size_low == INVALID_FILE_SIZE)
  {
    win32_get_last_error();
    return 0;
  }
  LARGE_INTEGER win32_size;
  win32_size.LowPart = size_low;
  win32_size.HighPart = size_high;
  return (u64)win32_size.QuadPart;
}

Platform_File_Info
platform_file_get_info(Platform_File_Handle file_handle, Allocator* allocator)
{
  Platform_File_Info result = {};
  HANDLE h = win32_get_open_file_handle(file_handle);
  if (h == INVALID_HANDLE_VALUE) return result;
  
  // File Size
  u64 win32_size = win32_get_file_size(h);
  if (win32_size == 0 && win32_last_error != 0) return result;
  
  // File Times
  FILETIME time_created, time_last_write;
  if (!GetFileTime(h, &time_created, (LPFILETIME)0, &time_last_write))
  {
    win32_get_last_error();
    return result;
  }
  
  // File Path
  // call GetFullPathName with empty dest just to get the length needed
  DWORD file_name_len_needed = GetFullPathName(
                                               win32_open_file_paths[file_handle.value], 0, 0, 0
                                               );
  if (!file_name_len_needed)
  {
    win32_get_last_error();
    return result;
  }
  
  result.path = allocator_alloc_string(allocator, (u64)file_name_len_needed);
  result.path.len = (u64)GetFullPathName(
                                         win32_open_file_paths[file_handle.value],
                                         (DWORD)result.path.cap,
                                         (char*)result.path.str,
                                         0
                                         );
  result.path_abs = result.path;
  
  // File Attributes
  DWORD file_attrs = GetFileAttributesA((char*)result.path.str);
  if (!file_attrs)
  {
    win32_get_last_error();
    return result;
  }
  
  result.size = win32_size;
  result.time_created = win32_file_time_to_u64(time_created);
  result.time_last_write = win32_file_time_to_u64(time_last_write);
  
  if (has_flag(file_attrs, FILE_ATTRIBUTE_DIRECTORY)) 
  {
    add_flag(result.flags, FileFlag_IsDir);
  }
  
  return result;
}

Data
platform_file_read_all(Platform_File_Handle file_handle, Allocator* allocator)
{
  Data result = {};
  
  HANDLE h = win32_get_open_file_handle(file_handle);
  if (h == INVALID_HANDLE_VALUE) return result;
  
  u64 file_size = win32_get_file_size(h);
  if (file_size == 0 && win32_last_error != 0) return result;
  
  result.base = allocator_alloc(allocator, file_size + 1);
  result.size = file_size + 1;
  
  DWORD bytes_read = 0;
  if (ReadFile(h, (void*)result.base, (DWORD)result.size, &bytes_read, NULL))
  {
    result.base[result.size - 1] = 0;
  }
  else
  {
    win32_get_last_error();
  }
  
  return result;
}

bool
platform_file_write_all(Platform_File_Handle file_handle, Data file_data)
{
  bool result = false;
  
  HANDLE h = win32_get_open_file_handle(file_handle);
  if (h == INVALID_HANDLE_VALUE) return result;
  
  // Set file pointer to beginning
  SetFilePointer(h, 0, 0, FILE_BEGIN);
  
  DWORD bytes_written = 0;
  if (WriteFile(h, file_data.base, (DWORD)file_data.size, &bytes_written, NULL))
  {
    result = (bytes_written == file_data.size);
  }
  
  return result;
}

void
platform_dir_enum_(String path, Platform_Enum_Dir_Flags flags, Allocator* allocator, Platform_File_Info_List* list)
{
  WIN32_FIND_DATA ffd;
  HANDLE search_handle = FindFirstFile((char*)path.str, &ffd);
  if (search_handle != INVALID_HANDLE_VALUE)
  {
    do
    {
      Platform_File_Info_List_Ele* ele = allocator_alloc_struct(
                                                                allocator, Platform_File_Info_List_Ele
                                                                );
      
      sll_push(list->first, list->last, ele);
    } while (FindNextFile(search_handle, &ffd));
  }
}

Platform_File_Info_List
platform_dir_enum(String path, Platform_Enum_Dir_Flags flags, Allocator* allocator)
{
  Platform_File_Info_List result = {};
  platform_dir_enum_(path, flags, allocator, &result);
  return result;
}

String
platform_get_exe_path(Allocator* allocator)
{
  String result = allocator_alloc_string(allocator, (u64)MAX_PATH);
  result.len = (u64)GetModuleFileName(NULL, (char*)result.str, (DWORD)result.cap);
  if (!result.len) return result;
  
  return result;
}

bool
platform_pwd_set(String path)
{
  bool result = SetCurrentDirectory((char*)path.str);
  if (!result) win32_get_last_error();
  return result;
}