/**
 *   \file               sharedmem.cpp
 *   \brief              Implementation of named shared memory class
 *   \version            \$Revision$
 *   \par Last commit:
 *                       \$Date$ by \n
 *                       \$Author$
 *   \par Copyright:
 *                       MC \n
 *   \par Notes:
 *
 */

#include <system/sharedmem.h>

#if defined(_WIN32) || defined(_WIN32_WINCE)
    #include <stdexcept>
    #include <stdio.h>
    #include <string.h>
#endif

#if defined(_LINUX)
    #include <sys/mman.h>
    #include <fcntl.h>
    #include <stdexcept>
#endif

namespace System
{

#if defined(_WIN32) || defined(_WIN32_WINCE)

SharedMemory::SharedMemory()
    : m_size(0), m_mapObject(0), m_ptr(0)
{
}

SharedMemory::SharedMemory(SharedMemory const&)
    : m_size(0), m_mapObject(0), m_ptr(0)
{
    throw std::logic_error("The copy constructor for shared memory blocks should not be used");
}

SharedMemory::~SharedMemory()
{
    if (m_ptr)
        UnmapViewOfFile(m_ptr);
    if (m_mapObject)
        CloseHandle(m_mapObject);
}

void SharedMemory::create(unsigned size, const char* name, bool writeAccess)
{
    m_size = size;

#if defined(_WIN32_WINCE) || defined(UNICODE)
    wchar_t uName[256];
    mbstowcs(uName, name, sizeof(uName));
    m_mapObject = CreateFileMapping(
        INVALID_HANDLE_VALUE,   // use paging file | physical memory (Win CE | Unicode)
        NULL,                   // no security attr.
        ((writeAccess)
            ? PAGE_READWRITE
            : PAGE_READONLY),   // read/write access
        0,                      // size: high 32-bits
        m_size,                 // size: low 32-bits
        uName                   // name of map object
    );
#else
    OSVERSIONINFO ver;
    ver.dwOSVersionInfoSize = sizeof(ver);
    GetVersionEx(&ver);
    bool NTPlatform = ver.dwPlatformId == VER_PLATFORM_WIN32_NT;
    int rwFlag = NTPlatform ? PAGE_READWRITE : PAGE_WRITECOPY;

    m_mapObject = CreateFileMapping(
        INVALID_HANDLE_VALUE,   // use paging file | physical memory (Win CE)
        NULL,                   // no security attr.
        ((writeAccess)
            ? rwFlag
            : PAGE_READONLY),   // read/write access
        0,                      // size: high 32-bits
        m_size,                 // size: low 32-bits
        name                    // name of map object
    );
#endif
    if (!m_mapObject)
    {
        char msg[256];
        sprintf("%s", (GetLastError() == ERROR_INVALID_HANDLE) ? "Object name is in use" : "Cannot create shared memory object");
        throw std::runtime_error(msg);
    }

    // use 0 offset and entire size
    m_ptr = (unsigned char*)MapViewOfFile(m_mapObject, (writeAccess) ? FILE_MAP_READ|FILE_MAP_WRITE : FILE_MAP_READ, 0, 0, 0);
    if (!m_ptr)
    {
        CloseHandle(m_mapObject);
        throw std::runtime_error("Cannot create shared memory");
    }
}

#else

SharedMemory::SharedMemory()
    : m_size(0), m_fId(0), m_ptr(0)
{
}

SharedMemory::SharedMemory(SharedMemory const&)
    : m_size(0), m_fId(0), m_ptr(0)
{
    throw std::logic_error("The copy constructor for shared memory blocks should not be used");
}

SharedMemory::~SharedMemory()
{
    if (m_ptr)
        munmap(m_ptr, m_size);
    if (m_fId)
        close(m_fId);
}

void SharedMemory::create(uint32_t size, const char* name, bool writeAccess)
{
    bool createFile;
    m_size = size;
    m_fId = open(name, (writeAccess) ? O_RDWR : O_RDONLY);
    createFile = (m_fId == -1) || (lseek(m_fId, 0, SEEK_END) != (off_t)m_size);
    if (createFile)
    {
        if (m_fId)
            close(m_fId);
        m_fId = creat(name, 0660);
        unsigned char initVal = 0;
        lseek(m_fId, m_size-1, SEEK_SET);
        write(m_fId, &initVal, 1);
        close(m_fId);
        m_fId = open(name, (writeAccess) ? O_RDWR : O_RDONLY);
    }
    if (m_fId == -1)
    {
        throw std::runtime_error("Cannot create memory οbject");
    }

    m_ptr = (unsigned char*)mmap(0, m_size, (writeAccess) ? PROT_READ|PROT_WRITE : PROT_READ, MAP_SHARED, m_fId, 0);
    if ((int32_t)m_ptr == -1)
    {
        close(m_fId);
        throw std::runtime_error("Cannot create shared memory");
    }
}

#endif

SharedMemory& SharedMemory::operator = (SharedMemory const&)
{
    throw std::logic_error("The assignement operator for shared memory blocks should not be used");
}

unsigned char* SharedMemory::getPointer()
{
    return m_ptr;
}

} // namespace System
