/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright 2019 Danny Robson <danny@nerdcruft.net>
 */

#include "file.hpp"

#include "except.hpp"
#include "../cast.hpp"

using cruft::win32::file;


///////////////////////////////////////////////////////////////////////////////
file::file (std::filesystem::path const &src, DWORD access, DWORD share, DWORD create, DWORD flags)
    : m_handle (
        error::try_call (
            CreateFileW,
            src.c_str (),
            access,
            share,
            nullptr, // security
            create,
            flags,
            nullptr // template
        )
    )
{ ; }


//-----------------------------------------------------------------------------
file::file (file &&src) noexcept
    : m_handle (std::move (src.m_handle))
{ ; }


//-----------------------------------------------------------------------------
file::file (handle &&src) noexcept
    : m_handle (std::move (src))
{ ; }


//-----------------------------------------------------------------------------
file::file (posix::fd &&src)
    : file (handle (std::move (src)))
{ ; }



///////////////////////////////////////////////////////////////////////////////
DWORD
file::read(void *buf, size_t available)
{
    DWORD total_read;
    error::try_call (ReadFile, m_handle, buf, available, &total_read, nullptr);
    return total_read;
}


//-----------------------------------------------------------------------------
DWORD
file::write (void *buf, size_t available)
{
    DWORD total_written;
    error::try_call (WriteFile, m_handle, buf, available, &total_written, nullptr);
    return total_written;
}


///////////////////////////////////////////////////////////////////////////////
DWORD
file::read(void *buf, size_t available, OVERLAPPED &overlapped)
{
    DWORD total_read;
    auto const res = ReadFile (m_handle, buf, available, &total_read, &overlapped);

    // If we're performing async IO then a value of FALSE is always returned.
    if (!overlapped.hEvent && res == FALSE)
        error::throw_code();

    return total_read;
}


//-----------------------------------------------------------------------------
DWORD
file::write (void *buf, size_t available, OVERLAPPED &overlapped)
{
    DWORD total_written;
    auto const res = WriteFile (m_handle, buf, available, &total_written, &overlapped);

    // If we're performing async IO then a value of FALSE is always returned.
    if (!overlapped.hEvent && res == FALSE)
        error::throw_code();

    return total_written;
}


///////////////////////////////////////////////////////////////////////////////
u64
file::set_pointer(u64 request, DWORD method)
{
    LONG const lo = request & 0xffffffff;
    LONG       hi = request >> 32u;

    auto result = SetFilePointer (m_handle, lo, &hi, method);
    if (result == INVALID_SET_FILE_POINTER)
        error::throw_code();

    u64 offset = cruft::cast::lossless<u64> (result) | cruft::cast::lossless<u64> (hi) << 32;
    return offset;
}