// $Id: JGProcProgStorage.cpp,v 1.5 2003/03/22 16:41:43 fukasawa Exp $

//=============================================================================
/**
 *  @file    JGProcProgStorage.h
 *
 *  @author  Fukasawa Mitsuo
 *
 *
 *    Copyright (C) 2001-2003 BEE Co.,Ltd. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
//=============================================================================

#define BEE_BUILD_DLL

#include "JGProcProgStorage.h"
#include "JGMemoryMappedFile.h"
#include "JGFile.h"
#include "JGBitVector.h"
#include "ace/Dirent.h"

static char *  _revision = {"JYUGEM-PROCPROG-1.0.0"};

//-----------------------------------------------------------------------------
// Set ppid
//-----------------------------------------------------------------------------
void ProcProgBlock::setID(JGid& ppid)
{
    BCHAR buf[256];
    _stprintf(buf, _TX("pp_%s"), ppid.toString().c_str());
    buf[NAME_LENGTH - 1] = '\0';
    _tcscpy(m_fname, buf);

    _stprintf(buf, _TX("%s"), ppid.toString().c_str());
    buf[PPID_LENGTH - 1] = '\0';
    _tcscpy(m_ppid, buf);
}

//
//
// JGProcProgStorage
//
//-----------------------------------------------------------------------------
// Constructor/Destoractor
//-----------------------------------------------------------------------------
JGProcProgStorage::JGProcProgStorage(const string& dir, const string& ppext)
        : JGMemoryMappedFile(dir, _TX("_procprog"), _TX("mmap"),
          sizeof(ProcProgHeader)), m_usage(NULL)
{
    m_ppext = ppext;
}

//-----------------------------------------------------------------------------
// Initial physical program area
//-----------------------------------------------------------------------------
int JGProcProgStorage::init(size_t maxsize)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgStorage::init");

    m_maxsize = maxsize;
    ProcProgHeader pphead;
    bool isExist;
    size_t length = PROCPROG_DATA_TOP(m_maxsize);
    length += m_maxsize * sizeof(ProcProgBlock);

    // TRACE_DEBUG((_TX("%s %d\n"), m_dir.c_str(), m_maxsize));

    //
    // Open map file
    //
    isExist = false;
    int result = this->readHeader(pphead);
    if (result >= 0)
    {
        if (pphead.m_cursize > 0)
        {
            isExist = true;
            m_maxsize = pphead.m_blockCount;
        }
    }

    if (isExist)
    {
        m_max = PROCPROG_DATA_TOP(m_maxsize) + (pphead.m_blockCount * pphead.m_blockSize);
        result = this->JGMemoryMappedFile::map(false);
        if (result < 0)
        {
            return BEE_ERROR;
        }
    }
    else
    {
        m_max = length;
        ACE_OS::strcpy(pphead.m_revision, _revision);
        pphead.m_blockCount = m_maxsize;
        pphead.m_blockSize = sizeof(ProcProgBlock);
        pphead.m_cursize = 0;
        result = this->JGMemoryMappedFile::map(true);
        if (result < 0)
        {
            return BEE_ERROR;
        }
        memmove(this->top(), &pphead, sizeof(ProcProgHeader));
        this->flush();       // flush header

        // TRACE_DEBUG((_TX("Create control file: %s %d\n"), m_dir.c_str(), m_maxsize));
    }

    // assigned data area
    m_top = (ProcProgBlock *)(this->top() + PROCPROG_DATA_TOP(m_maxsize));
    m_pph = (ProcProgHeader *)this->top();

    // make bitmap table
    BYTE * bitmap = this->top() + sizeof(ProcProgHeader);
    m_usage = new JGBitVector(m_maxsize, (UINT *)bitmap, false);

    //
    // Make reference table by database.
    //
    if (isExist)
    {
        ProcProgBlock * current = m_top;
        for (size_t i = 0; i < m_maxsize; i++)
        {   // Create process program map table
            if (m_usage->get(i))
            {
                TRACE_ERROR((_TX("Usage block(%s: %d) is %s \n"),
                             current->m_ppid, current->m_size,
                             (current->deleted()) ? _TX("deleted") : _TX("enable")));
            }
            if (m_usage->get(i) && (! current->deleted()))
            {   // Entry map table
                JGid ppid;
                current->getID(ppid);
                m_blockTable.insert(PPBlockPair(ppid, current));
            }
            current++;
        }
    }
    return BEE_SUCCESS;
}

//------------------------------------------------------------------------------
// Read header from mmap.
//------------------------------------------------------------------------------
int JGProcProgStorage::readHeader(ProcProgHeader& buf)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgStorage::readHeader");

    int result = this->JGMemoryMappedFile::map();
    if (result < 0)
    {
        return -1;
    }
    if (_tcscmp(_revision, (char *)this->top()) == 0)
    {
        memmove(&buf, this->top(), sizeof(ProcProgHeader));
        result = 0;    // Open process program control file
    }
    else
    {
        result = -1;   // New process program control file
    }
    m_mmap.close();
    return result;
}

//------------------------------------------------------------------------------
// Finish
//------------------------------------------------------------------------------
void JGProcProgStorage::fini()
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgStorage::fini");

    // BEE_GUARD_LOOP(ACE_Process_Mutex, ace_mon, this->m_pplock);

    m_blockTable.clear();
    if (m_usage != NULL)
    {
        delete m_usage;
        m_usage = NULL;
    }
    m_mmap.sync();
    m_mmap.close();
    return ;
}

//------------------------------------------------------------------------------
// Find process program by ppid
//------------------------------------------------------------------------------
ProcProgBlock * JGProcProgStorage::find(JGid& ppid)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgStorage::find");

    // BEE_GUARD_LOOP(ACE_Process_Mutex, ace_mon, this->m_pplock);

    PPBlockTable::iterator iter = m_blockTable.find(ppid);
    if (iter == m_blockTable.end())
    {
        return NULL;
    }
    ProcProgBlock * ppblock = (*iter).second;
    return ppblock;
}

//------------------------------------------------------------------------------
ProcProgBlock * JGProcProgStorage::find(const string& ppname)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgStorage::find");

    // BEE_GUARD_LOOP(ACE_Process_Mutex, ace_mon, this->m_pplock);

    JGid ppid;
    if (1)
    {   // ppid format is string
        ppid = ppname;
    }
    else
    {   // ppid format is integer
        ppid = (int)_tcstol(ppname.c_str(), 0, 0);
    }

    return this->find(ppid);
}

//------------------------------------------------------------------------------
// Get all process program ids
//------------------------------------------------------------------------------
int JGProcProgStorage::all(vector<JGid>& ids)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgStorage::ppids");

    // BEE_GUARD_LOOP(ACE_Process_Mutex, ace_mon, this->m_pplock);

    PPBlockTable::iterator iter = m_blockTable.begin();
    for ( ; iter != m_blockTable.end(); iter++)
    {
        ProcProgBlock * ppblock = (*iter).second;
        if (ppblock->deleted())
        {
            TRACE_ERROR((_TX("Invalid control block(%s)\n"), ppblock->m_ppid));
            continue;
        }
        JGid ppid;
        ppblock->getID(ppid);
        ids.push_back(ppid);
    }
    std::sort(ids.begin(), ids.end(), id_less());
    return BEE_SUCCESS;
}

//------------------------------------------------------------------------------
// Open process program by control block
//------------------------------------------------------------------------------
JGFile * JGProcProgStorage::openProgram(ProcProgBlock * ppblock,
                                        const string& caller)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgStorage::openProgram");

    // Open process program file
    string filename;
    ppblock->getFileName(filename);
    JGFile * ppfile = new JGFile(m_dir, filename, m_ppext);
    int result = ppfile->open();
    if (result < 0)
    {
        TRACE_ERROR((_TX("Can't %s process program(%s)\n"), caller.c_str(),
                                                            filename.c_str()));
        delete ppfile;
        return NULL;  // ignore, as delete control block
    }
    return ppfile;
}

//------------------------------------------------------------------------------
// Delete process program by ppid
//------------------------------------------------------------------------------
int JGProcProgStorage::del(JGid& ppid)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgStorage::del");

    // BEE_GUARD_LOOP(ACE_Process_Mutex, ace_mon, this->m_pplock);

    PPBlockTable::iterator iter = m_blockTable.find(ppid);
    if (iter == m_blockTable.end())
    {
        return BEE_ERROR;
    }
    ProcProgBlock * ppblock = (*iter).second;
    m_blockTable.erase(iter);

    // Delete process program file
    JGFile * ppfile = this->openProgram(ppblock, _TX("delete"));
    if (ppfile != NULL)
    {
        ppfile->remove();
        delete ppfile;
    }

    // Delete control block
    ppblock->erase();
    int idx = ppblock - this->m_top;
    m_usage->reset(idx);

    m_pph->m_cursize--;

    this->flush();

    return BEE_SUCCESS;
}

//------------------------------------------------------------------------------
// Write process program data
//------------------------------------------------------------------------------
int JGProcProgStorage::put(JGid& ppid, BYTE * datap, size_t size)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgStorage::put");

    // lock executor or application
    // BEE_GUARD_LOOP(ACE_Process_Mutex, ace_mon, this->m_pplock);

    if (m_pph->m_cursize >= m_pph->m_blockCount)
    {   // Full process program area
        TRACE_ERROR((_TX("Can't write process program(%s), as area full\n"),
                    ppid.toString().c_str()));
        return -1;
    }

    ProcProgBlock * pp = this->find(ppid);
    if (pp == NULL)
    {   // New process program
        pp = this->makeProcProgBlock(ppid, size);
    }

    JGFile * ppfile = this->openProgram(pp, _TX("write"));
    if (ppfile == NULL)
    {
        return -1;
    }
    ppfile->write(datap, size);
    ppfile->close();
    delete ppfile;

    return 0;
}

//------------------------------------------------------------------------------
// Get process program data
//------------------------------------------------------------------------------
int JGProcProgStorage::get(JGid& ppid, BYTE*& datap, size_t& size)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgStorage::get");

    // BEE_GUARD_LOOP(ACE_Process_Mutex, ace_mon, this->m_pplock);

    ProcProgBlock * ppblock = this->find(ppid);
    if (ppblock == NULL)
    {
        return BEE_ERROR;
    }

    // Read process program file
    JGFile * ppfile = this->openProgram(ppblock, _TX("read"));
    if (ppfile == NULL)
    {
        return BEE_ERROR;
    }
    BYTE * buf = (BYTE *)(new BYTE[ppblock->m_size + 32]);
    ppfile->read(buf, ppblock->m_size + 32);
    ppfile->close();
    delete ppfile;

    datap = buf;                     // Set return value
    size = ppblock->m_size;
    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// ppid 2 file name
//-----------------------------------------------------------------------------
void JGProcProgStorage::ppid2fname(JGid& ppid, string& fname)
{
    ProcProgBlock ppb;
    ppb.setID(ppid);
    ppb.getFileName(fname);
    fname += _TX(".");
    fname += m_ppext;
}

//------------------------------------------------------------------------------
// Create process program control block (Not create the program file)
//------------------------------------------------------------------------------
int JGProcProgStorage::create(JGid& ppid)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgStorage::create");

    // BEE_GUARD_LOOP(ACE_Process_Mutex, ace_mon, this->m_pplock);

    string ppname;
    ppid2fname(ppid, ppname);

    string dirName = m_dir;
    ACE_Dirent dir(dirName.c_str());
    dirent * directory;
    bool found = false;
    while ((directory = dir.read ()) != NULL)
    {
        string fname = directory->d_name;
        // if (fname == _TX(".") || fname == _TX(".."))
        // {
        //     continue;
        // }
        if (fname == ppname)
        {
            found = true;
            break;
        }
    }
    if (! found)
    {
        return BEE_ERROR;
    }

    ProcProgBlock * ppblock = this->find(ppid);
    if (ppblock == NULL)
    {
        ppblock = this->makeProcProgBlock(ppid);
        if (ppblock != NULL)
        {
            return BEE_ERROR;
        }
    }

    // Find process program file
    JGFile * ppfile = this->openProgram(ppblock, _TX("create"));
    if (ppfile == NULL)
    {
        return BEE_ERROR;
    }
    ppblock->m_size = ppfile->size();
    ppfile->close();
    delete ppfile;
    this->flush();
    return BEE_SUCCESS;
}

//------------------------------------------------------------------------------
// Purge spooled data.
//------------------------------------------------------------------------------
int JGProcProgStorage::purge()
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgStorage::purge");

    // BEE_GUARD_LOOP(ACE_Process_Mutex, ace_mon, this->m_pplock);

    ProcProgBlock * ppblock = this->m_top;
    for (size_t i = 0; i < m_maxsize; i++, ppblock++)
    {
        if (ppblock->deleted() || (! m_usage->get(i)))
        {
            continue;
        }

        // Delete process program file
        JGFile * ppfile = this->openProgram(ppblock, _TX("delete"));
        if (ppfile != NULL)
        {
            ppfile->remove();
            delete ppfile;
        }
        ppblock->erase();
    }
    m_pph->m_cursize = 0;
    m_usage->clear();
    this->flush();

    m_blockTable.clear();

    return 0;
}

//------------------------------------------------------------------------------
// Make process program control block.
//------------------------------------------------------------------------------
ProcProgBlock * JGProcProgStorage::makeProcProgBlock(JGid& ppid, size_t size)
{
    ProcProgBlock * ppblock;
    int empidx = m_usage->firstOff();
    if (empidx < 0)
    {
        return NULL;
    }
    ppblock = this->m_top + empidx;
    this->m_blockTable.insert(PPBlockPair(ppid, ppblock));
    m_usage->set(empidx);            // Assign process program area
    m_pph->m_cursize++;

    ppblock->setID(ppid);

    JGLogTime now;
    ppblock->m_time = now;
    ppblock->m_size = size;

    this->flush();
    return ppblock;
}

//------------------------------------------------------------------------------
// Print out process program control.
//------------------------------------------------------------------------------
void JGProcProgStorage::dump() const
{
    ACE_DEBUG((LM_DEBUG, ACE_TEXT("* PROCESS PROGRAM *\n")));

    ACE_DEBUG((LM_DEBUG, ACE_TEXT("%s : max = %d, current = %d\n"),
                         m_pph->m_revision, m_pph->m_blockCount,
                         m_pph->m_cursize));
    // dump bitmap
    for (size_t i = 0; i < m_maxsize; i++)
    {
        if (i != 0 && (i % 32) == 0)
        {
            ACE_DEBUG((LM_DEBUG, ACE_TEXT("\n")));
        }
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("%c"), (m_usage->get(i)) ? '1' : '0'));
    }
    ACE_DEBUG((LM_DEBUG, ACE_TEXT("\n")));

    ProcProgBlock * ppblock = this->m_top;
    for (size_t i = 0; i < m_maxsize; i++, ppblock++)
    {
        if (ppblock->deleted())
        {
            continue;
        }
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("[%s] %s %d\n"),
                             ppblock->m_time.toString().c_str(),
                             ppblock->m_ppid, ppblock->m_size));
    }
}
