/********************************************************************
* CONFIDENTIAL - All source code is the propriety and confidential  *
* information of David Sorber.                                      *
*                                                                   *
* Copyright 2024 David Sorber                                       *
* Unpublished -- all rights reserved under the copyright laws       *
* of the United States.                                             *
********************************************************************/
#include <chrono>
#include <system_error>

#include <sys/stat.h>

#include "CopyManager.h"


DBS::CopyManager::CopyManager(CopyManagerOptions& options)
    : r_options(options),
      m_terminate(false),
      p_mainThread(nullptr)
{
}

DBS::CopyManager::~CopyManager()
{
    // Add processor thread terminators
    for (uint32_t idx = 0; idx < r_options.getNumProcThreads(); ++idx)
    {
        m_entryQueue.push_back(ENTRY_TERMINATOR);
    }

    // Join and delete thread terminators
    for (auto& thread : m_procThreads)
    {
        thread->join();
        delete thread;
    }
    m_procThreads.clear();

    // Cleanup the main thread
    if (p_mainThread != nullptr)
    {
        if (p_mainThread->joinable())
        {
            p_mainThread->join();
        }
        delete p_mainThread;
        p_mainThread = nullptr;
    }
}

int DBS::CopyManager::run()
{
    // Spawn processor threads
    for (uint32_t idx = 0; idx < r_options.getNumProcThreads(); ++idx)
    {
        m_procThreads.push_back(
            new std::thread(&CopyManager::processThreadBody, this));
    }

    // Spawn the main thread
    p_mainThread = new std::thread(&CopyManager::mainThreadBody, this);


    p_mainThread->join();

    return 0;
}

void DBS::CopyManager::mainThreadBody()
{
    const std::string& srcPath(r_options.getSrcPath());
    for (const auto& dirEntry : sfs::recursive_directory_iterator(srcPath))
    {
        // Flow control
        while (m_entryQueue.size() >= r_options.getFlowControlThreshold())
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(5));
        }

//        std::cout << "PATH: " << dirEntry << std::endl;
        m_entryQueue.push_back(dirEntry);
    }

    // Add processor thread terminators
    for (uint32_t idx = 0; idx < r_options.getNumProcThreads(); ++idx)
    {
        m_entryQueue.push_back(ENTRY_TERMINATOR);
    }
}


void DBS::CopyManager::processThreadBody()
{
    sfs::directory_entry entry;
    struct stat statBuf{};
    int rc = 0;
    std::error_code errorCode;

    while (true)
    {
        // Grab entry from queue
        entry = m_entryQueue.pop_front();
        if (entry == ENTRY_TERMINATOR)
        {
            break;
        }

        if (entry.is_directory())
        {
            // Directory entry
            const char* dirPath = entry.path().string().c_str();
            rc = stat(dirPath, &statBuf);

            if (! sfs::create_directories(entry.path(), errorCode))
            {
                // TODO: log it
            }
        }
        else if (entry.is_regular_file())
        {
            // Regular file entry
        }
        else
        {
            // Unsupported entry type
            // TODO: log it?
        }


        std::cout << "PATH: " << entry << std::endl;
    }
}