#include <chrono>
#include <cstring>
#include <iostream>
#include <vector>

#include "include/System/IO/Directory.h"
#include "include/System/IO/DriveInfo.h"
#include "include/System/IO/File.h"
#include "include/System/IO/FileStream.h"

using namespace System::IO;

int main() {
  const std::wstring filesDir = L"../files";
  const std::wstring filePath = filesDir + L"/bigfile.dat";
  const std::wstring copyPath = filesDir + L"/bigfile_copy.dat";
  const std::wstring movedCopyPath = filesDir + L"/moved_bigfile_copy.dat";
  const std::wstring movedDirPath = L"../files_moved";
  const size_t blockSize = 1024 * 1024; // 1 MB
  const int32_t iterations = 100;       // 100 MB file

  if (!Directory::Exists(filesDir))
    Directory::CreateDirectory(filesDir);

  std::vector<char> buffer(blockSize, 'A');
  DWORD bytesWritten = 0;

  auto startWrite = std::chrono::high_resolution_clock::now();
  {
    FileStream outStream(filePath, GENERIC_WRITE, CREATE_ALWAYS);
    for (int32_t i = 0; i < iterations; i++)
      outStream.Write(buffer.data(), static_cast<DWORD>(buffer.size()),
                      bytesWritten);
  }
  auto endWrite = std::chrono::high_resolution_clock::now();
  std::wcout << L"C++ Write time: "
             << std::chrono::duration_cast<std::chrono::milliseconds>(
                    endWrite - startWrite)
                    .count()
             << L" ms" << std::endl;

  auto startCopy = std::chrono::high_resolution_clock::now();
  File::Copy(filePath, copyPath, true);
  auto endCopy = std::chrono::high_resolution_clock::now();
  std::wcout << L"C++ Copy time: "
             << std::chrono::duration_cast<std::chrono::milliseconds>(endCopy -
                                                                      startCopy)
                    .count()
             << L" ms" << std::endl;

  auto startRead = std::chrono::high_resolution_clock::now();
  {
    FileStream inStream(filePath, GENERIC_READ, OPEN_EXISTING);
    std::vector<char> readBuffer(blockSize);
    DWORD bytesRead = 0;
    while (inStream.Read(readBuffer.data(),
                         static_cast<DWORD>(readBuffer.size()), bytesRead) &&
           bytesRead > 0) {
    }
  }
  auto endRead = std::chrono::high_resolution_clock::now();
  std::wcout << L"C++ Read time: "
             << std::chrono::duration_cast<std::chrono::milliseconds>(endRead -
                                                                      startRead)
                    .count()
             << L" ms" << std::endl;

  auto startAttr = std::chrono::high_resolution_clock::now();
  DWORD attrs = File::GetAttributes(filePath);
  File::SetAttributes(filePath, attrs | FILE_ATTRIBUTE_HIDDEN);
  File::SetAttributes(filePath, attrs);
  auto endAttr = std::chrono::high_resolution_clock::now();
  std::wcout << L"File attribute toggle time: "
             << std::chrono::duration_cast<std::chrono::milliseconds>(endAttr -
                                                                      startAttr)
                    .count()
             << L" ms" << std::endl;

  auto startMoveFile = std::chrono::high_resolution_clock::now();
  File::Move(copyPath, movedCopyPath);
  auto endMoveFile = std::chrono::high_resolution_clock::now();
  std::wcout << L"Move file time: "
             << std::chrono::duration_cast<std::chrono::milliseconds>(
                    endMoveFile - startMoveFile)
                    .count()
             << L" ms" << std::endl;

  if (!Directory::Exists(movedDirPath))
    Directory::CreateDirectory(movedDirPath);

  auto startMoveDir = std::chrono::high_resolution_clock::now();
  Directory::Move(filesDir, movedDirPath + L"/files");
  auto endMoveDir = std::chrono::high_resolution_clock::now();
  std::wcout << L"Move directory time: "
             << std::chrono::duration_cast<std::chrono::milliseconds>(
                    endMoveDir - startMoveDir)
                    .count()
             << L" ms" << std::endl;

  std::wcout << L"Listing drives:\n";
  auto fixedDrives = DriveInfo::GetDrives();
  for (const auto &d : fixedDrives) {
    if (d.IsReady && d.DriveType == DriveType::Fixed &&
        d.DriveFormat != L"squashfs") {
      std::wcout << L"Drive: " << d.Name << L" | Type: "
                 << static_cast<UINT>(d.DriveType) << L" | Format: "
                 << d.DriveFormat << L" | Total: "
                 << d.TotalSize / (1024 * 1024) << L" MB" << L" | Free: "
                 << d.AvailableFreeSpace / (1024 * 1024) << L" MB\n";
    }
  }
}
