// ==========================================================================
//                 SeqAn - The Library for Sequence Analysis
// ==========================================================================
// Copyright (c) 2006-2018, Knut Reinert, FU Berlin
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Knut Reinert or the FU Berlin nor the names of
//       its contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
//
// ==========================================================================
// Author: David Weese <david.weese@fu-berlin.de>
// ==========================================================================

//SEQAN_NO_GENERATED_FORWARDS: no forwards are generated for this file

#ifndef SEQAN_HEADER_FILE_ASYNC_H
#define SEQAN_HEADER_FILE_ASYNC_H

/* IOREV
 *
 * _tested_
 * _nodoc_
 *
 * contains third way of file-io, low level platform specific, i.e. fd for
 * POSIX and handles for WINDOWS
 *
 * not clear in which places this is used exactly
 *
 * SEQAN_DIRECTIO used on UNIX, not sure exactly what it does
 * use of other macros unclear aswell
 *
 * not sure about all the AsyncRequest stuff, as most related calls are
 * deactivated in code, see also file/file_base.h
 *
 */


namespace seqan
{


template <typename TSpec /* = void */>
struct Async {};


#ifdef STDLIB_VS

template <typename TSpec>
class File<Async<TSpec> >
{
//IOREV _windows_
public:

    typedef LONGLONG    FilePtr;
    typedef ULONGLONG   SizeType;
    typedef DWORD       SizeType_;
    typedef HANDLE      Handle;

    Handle              handle, handleAsync;
    bool                noBuffering;

    File() :
        handle(INVALID_HANDLE_VALUE),
        handleAsync(INVALID_HANDLE_VALUE) {}

    File(void *) :    // to be compatible with the FILE*(NULL) constructor
        handle(INVALID_HANDLE_VALUE),
        handleAsync(INVALID_HANDLE_VALUE) {}

    virtual ~File()
    {
        close();
    }

    bool open(char const * fileName, int openMode = DefaultOpenMode<File>::VALUE)
    {
        SEQAN_PROADD(SEQAN_PROOPENFILES, 1);
        noBuffering = (getExtraFlags(openMode | OPEN_ASYNC) & (FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED)) != 0;
        handleAsync = CreateFileA(fileName,
                                  getFileAccess(openMode | OPEN_ASYNC),
                                  FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
                                  NULL,
                                  getCreationFlags(openMode | OPEN_ASYNC),
                                  getExtraFlags(openMode | OPEN_ASYNC),
                                  NULL);

        if (handleAsync == INVALID_HANDLE_VALUE)
        {
            if (!(openMode & OPEN_QUIET))
                std::cerr << "Open failed on file " << fileName << ". (ErrNo=" << GetLastError() << ")" << std::endl;
            return false;
        }
            #ifdef SEQAN_VERBOSE
        if (!(openMode & OPEN_QUIET))
            std::cerr << "file opened asynchronously " << fileName << " handle " << std::hex << handleAsync << std::dec << std::endl;
            #endif

        if (noBuffering)
        {
            handle = CreateFileA(fileName,                    // in this case io must be sector aligned
                                 getFileAccess(openMode),   // so we open a second file, for unaligned access
                                 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
                                 NULL,
                                 OPEN_EXISTING,
                                 getExtraFlags(openMode & ~OPEN_ASYNC),
                                 NULL);
            if (handle == INVALID_HANDLE_VALUE)
            {
                if (!(openMode & OPEN_QUIET))
                    std::cerr << "Open failed on secondary file " << fileName << ". (ErrNo=" << GetLastError() << ")" << std::endl;
                return false;
            }
                #ifdef SEQAN_VERBOSE
            if (!(openMode & OPEN_QUIET))
                std::cerr << "async file opened  " << fileName << " handle " << std::hex << handle << std::dec << std::endl;
                #endif
        }
        else
            handle = handleAsync;

        return true;
    }

    bool openTemp(int openMode = DefaultOpenTempMode<File>::VALUE)
    {
        char szTempName[MAX_PATH];
#ifdef SEQAN_DEFAULT_TMPDIR
        static const char szTempPath[MAX_PATH] = SEQAN_DEFAULT_TMPDIR;
#else
        char szTempPath[MAX_PATH];
        if (!GetTempPathA(MAX_PATH, szTempPath))
        {
            if (!(openMode & OPEN_QUIET))
                std::cerr << "Couldn't get a temporary path name. (ErrNo=" << GetLastError() << ")" << std::endl;
            return false;
        }
#endif
        if (!GetTempFileNameA(szTempPath, "GNDX", 0, szTempName))
        {
            if (!(openMode & OPEN_QUIET))
                std::cerr << "Couldn't get a temporary file name. (ErrNo=" << GetLastError() << ")" << std::endl;
            return false;
        }
        return open(szTempName, openMode | OPEN_TEMPORARY);
    }

    inline bool close()
    {
        BOOL result = TRUE;
            #ifdef SEQAN_VERBOSE
        std::cerr << "files closed handles " << std::hex << handleAsync << " and " << handle << std::dec << std::endl;
            #endif
        if (handle != handleAsync)
            result &= CloseHandle(handleAsync);
        result &= CloseHandle(handle);
        handleAsync = INVALID_HANDLE_VALUE;
        handle = INVALID_HANDLE_VALUE;
        SEQAN_PROSUB(SEQAN_PROOPENFILES, 1);
        return result != FALSE;
    }

    inline DWORD read(void * memPtr, SizeType_ count) const
    {
        SEQAN_PROADD(SEQAN_PROIO, (count + SEQAN_PROPAGESIZE - 1) / SEQAN_PROPAGESIZE);
        SEQAN_PROTIMESTART(tw);
        DWORD _transferedBytes;
        ReadFile(handle, memPtr, count, &_transferedBytes, NULL);
        SEQAN_PROADD(SEQAN_PROCWAIT, SEQAN_PROTIMEDIFF(tw));
        return _transferedBytes;
    }

    inline DWORD write(void const * memPtr, SizeType_ count) const
    {
        SEQAN_PROADD(SEQAN_PROIO, (count + SEQAN_PROPAGESIZE - 1) / SEQAN_PROPAGESIZE);
        SEQAN_PROTIMESTART(tw);
        DWORD _transferedBytes;
        WriteFile(handle, memPtr, count, &_transferedBytes, NULL);
        SEQAN_PROADD(SEQAN_PROCWAIT, SEQAN_PROTIMEDIFF(tw));
        return _transferedBytes;
    }

    inline FilePtr seek(FilePtr _pos, DWORD origin = FILE_BEGIN)
    {
//          LARGE_INTEGER li = _pos;
//            return SetFilePointer(handleAsync, li.LowPart, &li.HighPart, MoveMethod);
        LARGE_INTEGER new_pos, pos;
        pos.QuadPart = _pos;
        SetFilePointerEx(handle, pos, &new_pos, origin);
//            position = new_pos.QuadPart;
        return new_pos.QuadPart;
    }

    inline FilePtr tell()
    {
        return seek(0, FILE_CURRENT);
    }

    inline FilePtr size() const
    {
        LARGE_INTEGER result;
        DWORD dwError, high;
        result.LowPart = GetFileSize(handle, &high);
        result.HighPart = high;
        if (result.LowPart == INVALID_FILE_SIZE && (dwError = GetLastError()) != NO_ERROR)
        {
            std::cerr << "Couldn't get file size. (ErrNo=" << dwError << ")" << std::endl;
            return 0;
        }
        return result.QuadPart;
    }

    inline bool setEof() const
    {
        return SetEndOfFile(handle) != FALSE;
    }

    inline static DWORD error()
    {
        return GetLastError();
    }

    operator bool() const {
        return (handle != INVALID_HANDLE_VALUE) && (handleAsync != INVALID_HANDLE_VALUE);
    }

protected:

    DWORD getFileAccess(int openMode)
    {
        switch (openMode & OPEN_MASK)
        {
        case OPEN_RDONLY:
            return GENERIC_READ;

        case OPEN_WRONLY:
            return GENERIC_WRITE;

        case OPEN_RDWR:
            return GENERIC_READ | GENERIC_WRITE;

        default:
            return 0;
        }
    }

    DWORD getCreationFlags(int openMode)
    {
        if (openMode & OPEN_CREATE)
            if (openMode & OPEN_APPEND)
                return OPEN_ALWAYS;
            else
                return CREATE_ALWAYS;
        else
            return OPEN_EXISTING;
    }

    DWORD getExtraFlags(int openMode)
    {
        DWORD extra = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;    // | FILE_FLAG_WRITE_THROUGH;
        if (openMode & OPEN_ASYNC)
        {
            extra |= FILE_FLAG_OVERLAPPED;
                #ifdef SEQAN_DIRECTIO
            extra |= FILE_FLAG_NO_BUFFERING;
                #endif
        }
        if (openMode & OPEN_TEMPORARY)  extra |= FILE_FLAG_DELETE_ON_CLOSE;
        return extra;
    }

};


//////////////////////////////////////////////////////////////////////////////
// (SeqAn adaption)
//////////////////////////////////////////////////////////////////////////////

struct aiocb_win32
{
    OVERLAPPED  overlapped;
    Event       xmitDone;

    aiocb_win32()
    {}

    aiocb_win32(aiocb_win32 & other, Move) :
        overlapped(other.overlapped),
        xmitDone(other.xmitDone, Move())
    {}

    aiocb_win32(aiocb_win32 && other) :
        overlapped(other.overlapped),
        xmitDone(other.xmitDone, Move())
    {}

private:
    aiocb_win32(aiocb_win32 const &)
    {}
};

template <>
struct HasMoveConstructor<aiocb_win32>:
    True {};

template <typename TSpec>
struct AsyncRequest<File<Async<TSpec> > >
{
    typedef aiocb_win32 Type;
};
/*
    template <typename TSpec>
    struct aEvent<File<Async<TSpec> > >
    {
        typedef Event Type;
    };


    template <typename TSpec>
    struct aQueue<File<Async<TSpec> > >
    {
        typedef IOQueue Type;
    };

    template <typename TSpec>
    struct aHint<File<Async<TSpec> > >
    {
        typedef typename aQueue<File<Async<TSpec> > >::Type::aHint Type;
    };

    template <typename TSpec>
    struct aCallback<File<Async<TSpec> > >
    {
        typedef typename aQueue<File<Async<TSpec> > >::Type::aCallback Type;
    };*/


template <typename TSpec>
inline typename Size<File<Async<TSpec> > >::Type length(File<Async<TSpec> > & me)
{
    return me.size();
}

template <typename TSpec>
inline bool setEof(File<Async<TSpec> > & me)
{
    return me.setEof();
}

template <typename TSpec>
inline unsigned sectorSize(File<Async<TSpec> > const &)
{
    DWORD SpC, nofC, tnoC, aligning;
    if (GetDiskFreeSpace(NULL, &SpC, &aligning, &nofC, &tnoC) == 0)
    {
        std::cerr << "Error " << GetLastError() << " while querying cluster size" << std::endl;
        return 4096;
    }
    return aligning;
}

template <typename TSpec, typename TValue, typename TSize, typename TPos>
inline bool asyncReadAt(File<Async<TSpec> > & me, TValue * memPtr, TSize const count, TPos const fileOfs,
                        aiocb_win32 & request)
{
    SEQAN_PROTIMESTART(tw);
    LARGE_INTEGER ofs;
    ofs.QuadPart = fileOfs;
    ofs.QuadPart *= sizeof(TValue);
    request.overlapped.Offset = ofs.LowPart;
    request.overlapped.OffsetHigh = ofs.HighPart;
    if (!request.xmitDone)
        open(request.xmitDone);
    request.overlapped.hEvent = request.xmitDone.hEvent;
    if (ReadFile(
            me.handleAsync,
            memPtr,
            count * sizeof(TValue),
            &ofs.LowPart,
            &request.overlapped) || (me.error() == ERROR_IO_PENDING))
    {
        SEQAN_PROADD(SEQAN_PROIO, (sizeof(TValue) * count + SEQAN_PROPAGESIZE - 1) / SEQAN_PROPAGESIZE);
        SEQAN_PROADD(SEQAN_PROIWAIT, SEQAN_PROTIMEDIFF(tw));
        return true;
    }
    if (me.error() == ERROR_NO_SYSTEM_RESOURCES)        // read synchronoulsy instead
    {
            #if SEQAN_ENABLE_DEBUG || SEQAN_ENABLE_TESTING
        std::cerr << "Warning: Falling back to sync. read. :( " << std::endl;
            #endif
        signal(request.xmitDone);
        return readAt(me, memPtr, count, fileOfs);
    }
    return false;
}

template <typename TSpec, typename TValue, typename TSize, typename TPos>
inline bool asyncWriteAt(File<Async<TSpec> > & me, TValue const * memPtr, TSize const count, TPos const fileOfs,
                         aiocb_win32 & request)
{
    SEQAN_PROTIMESTART(tw);
    LARGE_INTEGER ofs;
    ofs.QuadPart = fileOfs;
    ofs.QuadPart *= sizeof(TValue);
    request.overlapped.Offset = ofs.LowPart;
    request.overlapped.OffsetHigh = ofs.HighPart;
    if (!request.xmitDone)
        open(request.xmitDone);
    request.overlapped.hEvent = request.xmitDone.hEvent;
    if (WriteFile(
            me.handleAsync,
            memPtr,
            count * sizeof(TValue),
            &ofs.LowPart,
            &request.overlapped) || (me.error() == ERROR_IO_PENDING))
    {
        SEQAN_PROADD(SEQAN_PROIO, (sizeof(TValue) * count + SEQAN_PROPAGESIZE - 1) / SEQAN_PROPAGESIZE);
        SEQAN_PROADD(SEQAN_PROIWAIT, SEQAN_PROTIMEDIFF(tw));
        return true;
    }
    if (me.error() == ERROR_NO_SYSTEM_RESOURCES)        // write synchronoulsy instead
    {
            #if SEQAN_ENABLE_DEBUG || SEQAN_ENABLE_TESTING
        std::cerr << "Warning: Falling back to sync. write. :( " << std::endl;
            #endif
        signal(request.xmitDone);
        return writeAt(me, memPtr, count, fileOfs);
    }
    return false;
}

//////////////////////////////////////////////////////////////////////
// queue specific functions

inline bool waitFor(aiocb_win32 & request)
{
//IOREV _doc_
    SEQAN_PROTIMESTART(tw);
    bool inProgress;
    bool waitResult = waitFor(request.xmitDone, 60000, inProgress);
    if (inProgress)
        std::cerr << "waitFor timeout" << std::endl;
    SEQAN_PROADD(SEQAN_PROCWAIT, SEQAN_PROTIMEDIFF(tw));
    return waitResult;
}

template <typename TTime>
inline bool waitFor(aiocb_win32 & request, TTime timeoutMilliSec, bool & inProgress)
{
//IOREV _doc_
    SEQAN_PROTIMESTART(tw);
    bool waitResult = waitFor(request.xmitDone, timeoutMilliSec, inProgress);
    SEQAN_PROADD(SEQAN_PROCWAIT, SEQAN_PROTIMEDIFF(tw));
    return waitResult;
}

template <typename TSize>
inline int waitForAny(aiocb_win32 const * const contexts[], TSize count, DWORD timeoutMilliSec = Event::Infinite)
{
//IOREV _nodoc_
    Event::Handle * handles = new Event::Handle[count];
    for (TSize i = 0; i < count; ++i)
        handles[i] = contexts[i]->xmitDone.hEvent;

    SEQAN_PROTIMESTART(tw);
    DWORD result = WaitForMultipleObjects(count, handles, false, timeoutMilliSec);
    SEQAN_PROADD(SEQAN_PROCWAIT, SEQAN_PROTIMEDIFF(tw));
    delete[] handles;
    if (/*result >= WAIT_OBJECT_0 && */ result < WAIT_OBJECT_0 + count)
        return result - WAIT_OBJECT_0;

    return -1;
}

template <typename TSpec>
inline bool cancel(File<Async<TSpec> > & me, aiocb_win32 const & request)
{
//IOREV _doc_
    return CancelIo(me.handleAsync);
}

template <typename TSpec>
inline bool flush(File<Async<TSpec> > & me)
{
//IOREV _doc_
    if (me.handle != me.handleAsync)        // in case of equality no direct access was done -> no flush needed
        return FlushFileBuffers(me.handle) != 0;
    else
        return true;
}

template <typename TSpec, typename AsyncRequest>
inline void release(File<Async<TSpec> > & me, AsyncRequest & request)
{
//IOREV _nodoc_
}

/*
    //////////////////////////////////////////////////////////////////////
    // callback based read/write

    template < typename TSpec, typename TValue, typename TSize,
               typename aCallback, typename aHint >
    inline typename AsyncRequest<File<Async<TSpec> > >::Type
    asyncRead(File<Async<TSpec> > & me, TValue *memPtr, TSize const count,
        aCallback* cb, aHint* hint)
    {
        DWORD bsize = (DWORD)(count * sizeof(TValue));
        typename AsyncRequest<File<Async<TSpec> > >::Type request =
            me.queue->asyncReadAt(
                me.handleAsync,
                me.position,
                memPtr,
                bsize,
                cb,
                hint);
        me.position += bsize;
        return request;
    }

    template < typename TSpec, typename TValue, typename TSize,
               typename aCallback, typename aHint >
    inline typename AsyncRequest<File<Async<TSpec> > >::Type
    asyncWrite(File<Async<TSpec> > & me, TValue const *memPtr, TSize const count,
        aCallback* cb, aHint* hint)
    {
        DWORD bsize = (DWORD)(count * sizeof(TValue));
        typename AsyncRequest<File<Async<TSpec> > >::Type request =
            me.queue->asyncWriteAt(
                memPtr,
                me.handleAsync,
                me.position,
                bsize,
                cb,
                hint);
        me.position += bsize;
        return request;
    }

    template < typename TSpec, typename TValue, typename TSize, typename TPos,
               typename aCallback, typename aHint >
    inline typename AsyncRequest<File<Async<TSpec> > >::Type
    asyncReadAt(File<Async<TSpec> > & me, TValue *memPtr, TSize const count, TPos const fileOfs,
        aCallback* cb, aHint* hint)
    {
        DWORD bsize = (DWORD)(count * sizeof(TValue));
        return me.queue->asyncReadAt(
            me.handleAsync,
            fileOfs * sizeof(TValue),
            memPtr,
            bsize,
            cb,
            hint);
    }

    template < typename TSpec, typename TValue, typename TSize, typename TPos,
               typename aCallback, typename aHint >
    inline typename AsyncRequest<File<Async<TSpec> > >::Type
    asyncWriteAt(File<Async<TSpec> > & me, TValue *memPtr, TSize const count, TPos const fileOfs,
        aCallback* cb, aHint* hint)
    {
        DWORD bsize = (DWORD)(count * sizeof(TValue));
        return me.queue->asyncWriteAt(
            memPtr,
            me.handleAsync,
            fileOfs * sizeof(TValue),
            bsize,
            cb,
            hint);
    }


    //////////////////////////////////////////////////////////////////////
    // event based read/write

    template < typename TSpec, typename TValue, typename TSize,
               typename aEvent >
    inline typename AsyncRequest<File<Async<TSpec> > >::Type
    asyncRead(File<Async<TSpec> > & me, TValue *memPtr, TSize const count,
        aEvent &event)
    {
        DWORD bsize = (DWORD)(count * sizeof(TValue));
        typename AsyncRequest<File<Async<TSpec> > >::Type request =
            me.queue->asyncReadAt(
                me.handleAsync,
                me.position,
                memPtr,
                bsize,
                event);
        me.position += bsize;
        return request;
    }

    template < typename TSpec, typename TValue, typename TSize,
               typename aEvent >
    inline typename AsyncRequest<File<Async<TSpec> > >::Type
    asyncWrite(File<Async<TSpec> > & me, TValue *memPtr, TSize const count,
        aEvent &event)
    {
        DWORD bsize = (DWORD)(count * sizeof(TValue));
        typename AsyncRequest<File<Async<TSpec> > >::Type request =
            me.queue->asyncWriteAt(
                memPtr,
                me.handleAsync,
                me.position,
                bsize,
                event);
        me.position += bsize;
        return request;
    }

    template < typename TSpec, typename TValue, typename TSize, typename TPos,
               typename aEvent >
    inline typename AsyncRequest<File<Async<TSpec> > >::Type
    asyncReadAt(File<Async<TSpec> > & me, TValue *memPtr, TSize const count, TPos const fileOfs,
        aEvent &event)
    {
        DWORD bsize = (DWORD)(count * sizeof(TValue));
        return me.queue->asyncReadAt(
            me.handleAsync,
            fileOfs * sizeof(TValue),
            memPtr,
            bsize,
            event);
    }

    template < typename TSpec, TValue, typename TSize, typename TPos,
               typename aEvent >
    inline typename AsyncRequest<File<Async<TSpec> > >::Type
    asyncWriteAt(File<Async<TSpec> > & me, TValue *memPtr, TSize const count, TPos const fileOfs,
        aEvent &event)
    {
        DWORD bsize = (DWORD)(count * sizeof(TValue));
        return me.queue->asyncWriteAt(
            memPtr,
            me.handleAsync,
            fileOfs * sizeof(TValue),
            bsize,
            event);
    }


    //////////////////////////////////////////////////////////////////////
    // queue specific functions

    template <typename TSpec>
    inline void flush(File<Async<TSpec> > & me) {
        me.queue->flush();
    }

    template < typename TSpec, typename AsyncRequest >
    inline void release(File<Async<TSpec> > & me, AsyncRequest & request) {
        me.queue->release(request);
    }
*/

//////////////////////////////////////////////////////////////////////////////
// page aligned allocate for direct file io

struct TagAllocateAligned_;        //< allocate page aligned memory for direct i/o access
typedef Tag<TagAllocateAligned_> const TagAllocateAligned;

template <typename T, typename TValue, typename TSize>
inline void
allocate(T const &,
         TValue * & data,
         TSize count,
         TagAllocateAligned const)
{
//IOREV _doc_
    data = (TValue *) VirtualAlloc(NULL, count * sizeof(TValue), MEM_COMMIT, PAGE_READWRITE);
    if (data)
        SEQAN_PROADD(SEQAN_PROMEMORY, count * sizeof(TValue));
    else
        std::cerr << "AlignAllocator: Could not allocate memory of size " << std::hex << count * sizeof(TValue) << std::dec << ". (ErrNo=" << GetLastError() << ")" << std::endl;
}

//////////////////////////////////////////////////////////////////////////////
// page aligned deallocate for direct file io

template <typename T, typename TValue, typename TSize>
inline void
deallocate(T const &,
           TValue * data,
           TSize count,
           TagAllocateAligned const)
{
//IOREV _doc_
    if (data)
    {
        VirtualFree(data, 0, MEM_RELEASE);
        if (count)        // .. to use count if SEQAN_PROFILE is not defined
            SEQAN_PROSUB(SEQAN_PROMEMORY, count * sizeof(TValue));
    }
}

#else  //  #if STDLIB_VS


template <typename TSpec>
class File<Async<TSpec> >:
    public File<Sync<TSpec> >
{
//IOREV _nodoc_ members are not well documented
public:

    typedef File<Sync<TSpec> >  Base;

    typedef off_t           FilePtr;
    typedef off_t           SizeType;       // type of file size
    typedef size_t          SizeType_;      // type of transfer size (for read or write)
    typedef int             Handle;

    Handle handleAsync;
    using Base::handle;

    File(void * = NULL) :        // to be compatible with the FILE*(NULL) constructor
        handleAsync(-1) {}

    virtual ~File()
    {
        // this->close(); will already be called in the base class
    }

    bool open(char const * fileName, int openMode = DefaultOpenMode<File>::VALUE)
    {
        handle = ::open(fileName, Base::_getOFlag(openMode & ~OPEN_ASYNC), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
        if (handle == -1)
        {
            handleAsync = handle;
            if (!(openMode & OPEN_QUIET))
                std::cerr << "Open failed on file " << fileName << ": \"" << ::strerror(errno) << '"' << std::endl;
            return false;
        }

        if (Base::_getOFlag(openMode | OPEN_ASYNC) & O_DIRECT)
        {
            handleAsync = ::open(fileName, Base::_getOFlag(openMode | (OPEN_ASYNC & ~OPEN_CREATE)), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
            if (handleAsync == -1 || errno == EINVAL)          // fall back to cached access
            {
                    #if SEQAN_ENABLE_DEBUG || SEQAN_ENABLE_TESTING
                if (!(openMode & OPEN_QUIET))
                    std::cerr << "Warning: Direct access openening failed. \"" << ::strerror(errno) << '"' << std::endl;
                    #endif
                handleAsync = handle;
            }
                #if SEQAN_ENABLE_DEBUG || SEQAN_ENABLE_TESTING
            else if (!(openMode & OPEN_QUIET))
                std::cerr << "Direct access successfully initiated" << std::endl;
                #endif
        }
        else
            handleAsync = handle;

        if (sizeof(FilePtr) < 8 && !(openMode & OPEN_QUIET))
            // To remove this warning, you have to options:
            // 1. include the following line before including anything in your application
            //    #define _FILE_OFFSET_BITS 64
            // 2. include <seqan/platform.h> or <seqan/sequence.h> before any other include
            std::cerr << "WARNING: FilePtr is not 64bit wide" << std::endl;


        SEQAN_PROADD(SEQAN_PROOPENFILES, 1);
        return true;
    }

    bool close()
    {
        bool result = true;
        if (handleAsync != handle && handleAsync != -1)
            result &= (::close(handleAsync) == 0);
        result &= (::close(handle) == 0);
        handleAsync = -1;
        handle = -1;
        SEQAN_PROSUB(SEQAN_PROOPENFILES, 1);
        return result;
    }

};


//////////////////////////////////////////////////////////////////////////////
// (SeqAn adaption)
//////////////////////////////////////////////////////////////////////////////
/*
    template <typename TSpec>
    struct aQueue<File<Async<TSpec> > >
    {
        typedef void* Type;
    };
*/

struct AiocbWrapper :
    public aiocb
{
    AiocbWrapper() = default;
    AiocbWrapper(AiocbWrapper &&) = default;

    AiocbWrapper(AiocbWrapper & other, Move) :
        aiocb(other)
    {}

    AiocbWrapper & operator=(AiocbWrapper &&) = default;
private:
    AiocbWrapper(AiocbWrapper const &) = delete;
    AiocbWrapper & operator=(AiocbWrapper const &) = delete;
};

template <>
struct HasMoveConstructor<AiocbWrapper>:
    True {};


template <typename TSpec>
struct AsyncRequest<File<Async<TSpec> > >
{
//IOREV _doc_
    typedef AiocbWrapper Type;
};
/*
    template <typename TSpec>
    struct aEvent<File<Async<TSpec> > >
    {
        typedef aiocb Type;
    };
*/

//////////////////////////////////////////////////////////////////////
// event based read/write

//    enum { AsyncIOSignal_ = SIGIO };

inline void printRequest(AiocbWrapper & request, const char * _hint)
{
//IOREV _nodoc_ _notinlined_
    std::cerr << std::hex;
    if (_hint)
        std::cerr << _hint << std::endl;
    std::cerr << "fildes:  " << request.aio_fildes << std::endl;
    std::cerr << "buffer:  " << (unsigned long)request.aio_buf << std::endl;
    std::cerr << "offset:  " << request.aio_offset << std::endl;
    std::cerr << "nbytes:  " << request.aio_nbytes << std::endl;
    std::cerr << "event:   " << request.aio_sigevent.sigev_notify << std::endl;
    std::cerr << "Raddr:   " << &request << std::endl;
    std::cerr << std::dec;
}

inline void printRequest(AiocbWrapper & request)
{
    printRequest(request, NULL);
}

template <typename TSpec, typename TValue, typename TSize, typename TPos>
bool asyncReadAt(File<Async<TSpec> > & me, TValue * memPtr, TSize const count, TPos const fileOfs,
                 AiocbWrapper & request)
{
//IOREV _doc_ _notinlined_
    SEQAN_PROTIMESTART(tw);
    memset(&request, 0, sizeof(aiocb));
    request.aio_fildes = me.handleAsync;
    request.aio_buf = memPtr;
    request.aio_offset = fileOfs;
    request.aio_offset *= sizeof(TValue);
    request.aio_nbytes = count * sizeof(TValue);
    request.aio_sigevent.sigev_notify = SIGEV_NONE;
/*      request.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
        request.aio_sigevent.sigev_signo = AsyncIOSignal_;
        request.aio_sigevent.sigev_value.sival_ptr = &request;
        #ifdef SEQAN_VVERBOSE
            printRequest(request, "aio_read():");
        #endif
*/  if (request.aio_nbytes == 0)
        return true;

    SEQAN_PROADD(SEQAN_PROIO, (request.aio_nbytes + SEQAN_PROPAGESIZE - 1) / SEQAN_PROPAGESIZE);
    int result = aio_read(&request);
    SEQAN_PROADD(SEQAN_PROIWAIT, SEQAN_PROTIMEDIFF(tw));
    if (result != 0)
    {
        request.aio_nbytes = 0;
        if (errno == EAGAIN)        // read synchronoulsy instead
        {
#if SEQAN_ENABLE_DEBUG || SEQAN_ENABLE_TESTING
            std::cerr << "Warning: Falling back to sync. read. :( " << std::endl;
#endif
            bool success = readAt(me, memPtr, count, fileOfs);
            if (!success)
                SEQAN_FAIL(
                    "readAt(%d, %d, %d, %d) failed: \"%s\"",
                    me.handle, (size_t)memPtr, count, fileOfs, strerror(errno));
            return success;
        }
#if SEQAN_ENABLE_DEBUG
        else
            std::cerr << "aio_read failed (asyncReadAt). \"" << ::strerror(errno) << '"' << std::endl;
#endif
    }
    return result == 0;
}

template <typename TSpec, typename TValue, typename TSize, typename TPos>
bool asyncWriteAt(File<Async<TSpec> > & me, const TValue * memPtr, TSize const count, TPos const fileOfs,
                  AiocbWrapper & request)
{
//IOREV _doc_ _notinlined_
    SEQAN_PROTIMESTART(tw);
    memset(&request, 0, sizeof(aiocb));
    request.aio_fildes = me.handleAsync;
    request.aio_buf = const_cast<TValue *>(memPtr);
    request.aio_offset = fileOfs;
    request.aio_offset *= sizeof(TValue);
    request.aio_nbytes = count * sizeof(TValue);
    request.aio_sigevent.sigev_notify = SIGEV_NONE;
/*      request.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
        request.aio_sigevent.sigev_signo = AsyncIOSignal_;
        request.aio_sigevent.sigev_value.sival_ptr = &request;
        #ifdef SEQAN_VVERBOSE
            printRequest(request, "aio_write():");
        #endif
*/  if (request.aio_nbytes == 0)
        return true;

    SEQAN_PROADD(SEQAN_PROIO, (request.aio_nbytes + SEQAN_PROPAGESIZE - 1) / SEQAN_PROPAGESIZE);
    int result = aio_write(&request);
    SEQAN_PROADD(SEQAN_PROIWAIT, SEQAN_PROTIMEDIFF(tw));
    if (result != 0)
    {
        request.aio_nbytes = 0;
        int errorNo = errno;
        if (errorNo == EAGAIN)      // write synchronoulsy instead
        {
#if SEQAN_ENABLE_DEBUG || SEQAN_ENABLE_TESTING
            std::cerr << "Warning: Falling back to sync. write. :( " << std::endl;
#endif
            bool success = writeAt(me, memPtr, count, fileOfs);
            if (!success)
                SEQAN_FAIL(
                    "writeAt(%d, %d, %d, %d) failed: \"%s\"",
                    me.handle, (size_t)memPtr, count, fileOfs, strerror(errno));
            return success;
        }
#if SEQAN_ENABLE_DEBUG
        else
        {
            std::cerr << "aio_write failed (asyncWriteAt): \"" << ::strerror(errno) << '"' << std::endl;
        }
#endif
    }
    return result == 0;
}

template <typename TSpec>
inline bool flush(File<Async<TSpec> > & me)
{
//IOREV _doc_
        #if _POSIX_SYNCHRONIZED_IO > 0
    return me.handle == me.handleAsync || fdatasync(me.handle) == 0;

        #else
    return me.handle == me.handleAsync || fsync(me.handle) == 0;

        #endif
}

//////////////////////////////////////////////////////////////////////
// queue specific functions

inline bool waitFor(AiocbWrapper & request)
{
//IOREV _doc_
/*        #ifdef SEQAN_VVERBOSE
            printRequest(request, "aio_suspend():");
        #endif
*/
    if (request.aio_nbytes == 0)
        return true;

    aiocb * cblist = &request;
    SEQAN_PROTIMESTART(tw);
    int result = aio_suspend(&cblist, 1, NULL);
    ssize_t nbytes = aio_return(&request);
    SEQAN_PROADD(SEQAN_PROCWAIT, SEQAN_PROTIMEDIFF(tw));

//#if SEQAN_ENABLE_DEBUG
    if (result != 0 || nbytes != (ssize_t)request.aio_nbytes)
    {
        int errorNo = aio_error(&request);
        if (errorNo != EINPROGRESS)
        {
            if (errorNo != ECANCELED)
                errorNo = errno;
            std::cerr << "Asynchronous I/O operation failed (waitFor): \"" << ::strerror(errorNo) << '"' << std::endl;
            printRequest(request);
        }
    }
//#endif

    return (result == 0) && (nbytes == (ssize_t)request.aio_nbytes);
}

inline bool waitFor(AiocbWrapper & request, long timeoutMilliSec, bool & inProgress)
{
//IOREV _doc_
/*        #ifdef SEQAN_VVERBOSE
            printRequest(request, "aio_suspend_timeout():");
        #endif
*/
    if (request.aio_nbytes == 0)
    {
        inProgress = false;
        return true;
    }

    int result;
    if (timeoutMilliSec != 0)
    {
        aiocb * cblist = &request;
        timespec ts;
        ts.tv_sec = timeoutMilliSec / 1000;
        ts.tv_nsec = (timeoutMilliSec % 1000) * 1000;
        SEQAN_PROTIMESTART(tw);
        result = aio_suspend(&cblist, 1, &ts);
        (void)result;      // never used
        SEQAN_PROADD(SEQAN_PROCWAIT, SEQAN_PROTIMEDIFF(tw));
    }

    result = aio_error(&request);
    inProgress = (result == EINPROGRESS);
    ssize_t nbytes;

    if (inProgress)
        result = 0;
    else
    {
        nbytes = aio_return(&request);
        result = (nbytes != (ssize_t)request.aio_nbytes);
    }

        #if SEQAN_ENABLE_DEBUG
    if (result != 0)
    {
        int errorNo = aio_error(&request);
        if (errorNo != EINPROGRESS)
        {
            if (errorNo != ECANCELED)
                errorNo = errno;
            std::cerr << "Asynchronous I/O operation failed (waitFor with timeOut=" << timeoutMilliSec << "ms): \"" << ::strerror(errorNo) << '"' << std::endl;
            printRequest(request);
        }
    }
        #endif
    return result == 0;
}

template <typename TSize>
inline TSize waitForAny(AiocbWrapper const * const contexts[], TSize count)
{
//IOREV _nodoc_
    SEQAN_PROTIMESTART(tw);
    bool result = aio_suspend(contexts, count, NULL);
    SEQAN_PROADD(SEQAN_PROCWAIT, SEQAN_PROTIMEDIFF(tw));
    return result == 0;
}

template <typename TSize>
inline TSize waitForAny(AiocbWrapper const * const contexts[], TSize count, long timeoutMilliSec)
{
//IOREV _nodoc_
    timespec ts;
    ts.tv_sec = timeoutMilliSec / 1000;
    ts.tv_nsec = (timeoutMilliSec % 1000) * 1000;
    SEQAN_PROTIMESTART(tw);
    bool result = aio_suspend(contexts, count, &ts);
    SEQAN_PROADD(SEQAN_PROCWAIT, SEQAN_PROTIMEDIFF(tw));
    return result == 0;
}

template <typename TSpec>
inline bool cancel(File<Async<TSpec> > & me, AiocbWrapper & request)
{
//IOREV _doc_
/*        #ifdef SEQAN_VVERBOSE
            printRequest(request, "aio_cancel():");
        #endif
*/  return aio_cancel(me.handleAsync, &request) == 0;
}

inline int error(AiocbWrapper const & request)
{
//IOREV _nodoc_
    return aio_error(&request);
}

inline int _returnValue(AiocbWrapper & request)
{
//IOREV _nodoc_
    return aio_return(&request);
}

template <typename TSpec>
inline void release(File<Async<TSpec> > & /*me*/, AiocbWrapper const & /*request*/)
{
//IOREV _nodoc_
}

/*
    typedef void (*sighandler_t)(int);
    static unsigned AsyncIOHandlerRefCount_ = 0;
    static struct sigaction AsyncIOOldSig_;

    inline void AsyncIOHandler_(int sigNo, siginfo_t *info, void *hint) {
        SEQAN_ASSERT_EQ(sigNo, AsyncIOSignal_);
        // TODO: signal respective event
        // currently we don't need async IO handlers because
        // we only wait for single events
    }

    static sighandler_t _addAsyncIOHandler() {
        struct sigaction newSig, oldSig;
        newSig.sa_sigaction = AsyncIOHandler_;
        sigemptyset(&newSig.sa_mask);
        newSig.sa_flags = SA_RESTART + SA_SIGINFO;
        if (sigaction(AsyncIOSignal_, &newSig, &oldSig) < 0)
            return SIG_ERR;
        return oldSig.sa_handler;
    }
*/

//////////////////////////////////////////////////////////////////////////////
// page aligned allocate for direct file io

struct TagAllocateAligned_;        //< allocate page aligned memory for direct i/o access
typedef Tag<TagAllocateAligned_> const TagAllocateAligned;

template <typename T, typename TValue, typename TSize>
inline void
allocate(T const & /*me*/,
         TValue * & data,
         TSize count,
         TagAllocateAligned const)
{
//IOREV _doc_
    data = (TValue *)::valloc(count * sizeof(TValue));
#ifdef SEQAN_PROFILE
    if (data)
        SEQAN_PROADD(SEQAN_PROMEMORY, count * sizeof(TValue));
    else
        std::cerr << "AlignAllocator: Could not allocate memory of size " << std::hex <<
            count * sizeof(TValue) << " with page alignment. (ErrNo=" << std::dec <<
            errno << ")" << std::endl;
#endif
}

//////////////////////////////////////////////////////////////////////////////
// page aligned deallocate for direct file io

template <typename T, typename TValue, typename TSize>
inline void
deallocate(T const & /*me*/,
           TValue * data,
           TSize
#ifdef SEQAN_PROFILE
           count
#endif
          ,
           TagAllocateAligned const)
{
//IOREV _doc_
#ifdef SEQAN_PROFILE
    if (data && count)        // .. to use count if SEQAN_PROFILE is not defined
        SEQAN_PROSUB(SEQAN_PROMEMORY, count * sizeof(TValue));
#endif
    ::free(data);
}

template <typename TSpec, typename TSize>
inline void resize(File<Async<TSpec> > & me, TSize new_length)
{
//IOREV _doc_
    if (!me.resize(new_length))
        SEQAN_FAIL(
            "resize(%d, %d) failed: \"%s\"",
            me.handle, new_length, strerror(errno));
}

#endif  // #ifdef STDLIB_VS

//////////////////////////////////////////////////////////////////////////////
// global functions

template <typename TSpec>
struct Size<File<Async<TSpec> > >
{
//IOREV
    typedef typename File<Async<TSpec> >::SizeType Type;
};

template <typename TSpec>
struct Position<File<Async<TSpec> > >
{
//IOREV
    typedef typename File<Async<TSpec> >::FilePtr Type;
};

template <typename TSpec>
struct Difference<File<Async<TSpec> > >
{
//IOREV
    typedef typename File<Async<TSpec> >::FilePtr Type;
};



template <typename TSpec, typename TValue, typename TSize>
inline void
allocate(File<Async<TSpec> > const & me,
         TValue * & data,
         TSize count)
{
//IOREV _doc_
    allocate(me, data, count, TagAllocateAligned());
}

template <typename TSpec, typename TValue, typename TSize>
inline void
deallocate(File<Async<TSpec> > const & me,
           TValue * data,
           TSize count)
{
//IOREV _doc_
    deallocate(me, data, count, TagAllocateAligned());
}

}

#endif  // SEQAN_HEADER_FILE_ASYNC_H
