// $Id: JGLogFileManager.cpp,v 1.4 2003/03/16 14:51:00 fukasawa Exp $

//=============================================================================
/**
 *  @file    JGLogFileManager.h
 *
 *  @author  Fukasawa Mitsuo
 *
 *
 *    Copyright (C) 2001-2002 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 "jyugem.h"
#include "JGLogFileManager.h"
#include "JGEquipment.h"
#include "JGVariable.h"
#include "JGSubSystem.h"
#include "JGTrigger.h"
#include "sac/JGIODevice.h"
#include "JGLogTime.h"

//------------------------------------------------------------------------------
// Queue Log data .
//------------------------------------------------------------------------------
class JGLogTrigger : public JGTrigger
{
    friend class JGLogRequest;
public:
    JGLogTrigger(const string& category, const JGLogTime& logtime,
                 const string& logdata)
            : JGTrigger(category), m_logtime(logtime), m_logdata(logdata) {}
    virtual ~JGLogTrigger() {}

protected:
    JGLogTime m_logtime;
    string  m_logdata;
};

//------------------------------------------------------------------------------
class JGLogRequest : public JGTriggerRequest
{
public:
    JGLogRequest(JGLogTrigger * trig, JGTask * task)
            : JGTriggerRequest(trig, task) {}
    JGLogRequest(const string& category, const JGLogTime& logtime,
                 const string& logdata, JGTask * task = NULL)
            : JGTriggerRequest() {
            m_trigger = new JGLogTrigger(category, logtime, logdata);
            m_task = task;
        }
    virtual ~JGLogRequest() {}

    virtual int call()
    {
        // TRACE_FUNCTION(TRL_LOW, "JGLogRequest::call");

        JGLogFileManager * fm = (JGLogFileManager *)m_task;
        JGLogTrigger * logtrig = (JGLogTrigger *)m_trigger;
        int result = fm->write(logtrig->name(), logtrig->m_logtime,
                               logtrig->m_logdata);
        while (result == 1)
        {   // Retry, detect collesion
            result = fm->write(logtrig->name(), logtrig->m_logtime,
                     logtrig->m_logdata);
        }
        return 0;
    }
};



//------------------------------------------------------------------------------
// Switch log-file day by day.
//------------------------------------------------------------------------------
//// #define DEBUG_HOUR24
#ifndef DEBUG_HOUR24
static UINT _term24h = (24 * 60 * 60);     // seconds per a day
#else
static UINT _term24h = (10 * 60);          // seconds per 10 minutes
static int  _uniq = 1;
#endif

class DayTimerHandler : public TaskTimerHandler
{
public:
    DayTimerHandler(JGLogFileManager * mngr) : TaskTimerHandler(), m_fm(mngr) {}
    ~DayTimerHandler(void) { m_fm->update(); }

    // Call back hook.
    virtual void handle_time_out(const ACE_Time_Value &current_time,
                                 const void * arg)
    {
        TRACE_FUNCTION(TRL_LOW, "DayTimerHandler::handle_time_out");
        ACE_UNUSED_ARG(current_time);
        ACE_UNUSED_ARG(arg);

        ACE_Time_Value tv;           // The term which pass this method.

        int result = m_fm->update();
        if (result == 1)
        {   // collesion
            TRACE_DEBUG((_TX("Collesion to get the log file handler.\n")));
            tv.set(0, 500000);       // 500 msec
        }
        else
        {   // Set next term
            tv.set(_term24h);        // 24 hour
        }

        int id = ACE_Proactor::instance()->schedule_timer(*this, NULL, tv);
        if (id == -1)
        {
            TRACE_ERROR((_TX("Timer schedule failed")));
            return ;
        }
        return ;
    }

protected:
    JGLogFileManager * m_fm;
};


//-----------------------------------------------------------------------------
// Return own.
//-----------------------------------------------------------------------------
static JGLogFileManager * _manager = NULL;

JGLogFileManager * JGLogFileManager::instance()
{
    TRACE_FUNCTION(TRL_LOW, "JGLogFileManager::instance");
    if (_manager == NULL)
    {
        string dir = JGEquipment::instance()->rootDirectory();
        dir.append(DIR_SEPARATOR_STRING);
        dir.append(_TX("log"));
        _manager = new JGLogFileManager(dir);
    }
    return _manager;
}


//-----------------------------------------------------------------------------
// Create file
//-----------------------------------------------------------------------------
int JGLogFileManager::init(const BCHAR ** categories)
{
    TRACE_FUNCTION(TRL_LOW, "JGLogFileManager::init");

    time_t now;
    time(&now);
    string date;
    UINT   term = JGLogTime::lapse(now);
    JGLogTime::makeDateName(now, date);

    this->JGFileManager::init(categories, date);

#ifndef DEBUG_HOUR24
    ACE_Time_Value tv(_term24h - term);
#else
    ACE_Time_Value tv(_term24h - (term % (10 * 60)));
#endif
    m_handler = new DayTimerHandler(this);
    addTimer(tv, m_handler);

    return 0;
}

//-----------------------------------------------------------------------------
// Update file
//-----------------------------------------------------------------------------
int JGLogFileManager::update()
{
    TRACE_FUNCTION(TRL_LOW, "JGLogFileManager::update");
    // Lock
    ACE_GUARD_RETURN(ACE_Thread_Mutex, ace_mon, this->m_tmlock, 1);

    // Create new log file
    time_t now;
    time(&now);
    string date;
    JGLogTime::makeDateName(now, date);
#ifdef DEBUG_HOUR24
    BCHAR buf[32];
    _stprintf(buf, _TX("%03d"), _uniq++);
    date.append(buf);
#endif

    int result = this->JGFileManager::change(date);
    if (result < 0)
    {
        TRACE_ERROR((_TX("Can't create new log files.\n")));
        result = -1;
    }
    else
    {
        TRACE_DEBUG((_TX("Create new log files.\n")));
        result = 0;
    }
    return result;
}


//-----------------------------------------------------------------------------
// Logging data to file
//-----------------------------------------------------------------------------
int JGLogFileManager::log(const string& category, const JGLogTime& logtm,
                          const string& logstr, JGVariable * var)
{
    TRACE_FUNCTION(TRL_LOW, "JGLogFileManager::log");

    string logtext = logstr;
    if (var != NULL)
    {
        string systext = _TX("0");
        JGIODevice * iodev = var->sac();
        if (iodev != NULL)
        {
            JGSubSystem * subsys = iodev->subsystem();
            if (subsys != NULL)
            {
                systext = subsys->subsysid().toString();  // Sub-system ID
            }
        }
        logtext.append(_TX(" "));
        logtext.append(systext);
    }

    // Call to service
    JGLogRequest * logReq = new JGLogRequest(category, logtm, logtext, this);
    this->put(logReq);

    return 0;
}


//-----------------------------------------------------------------------------
// Write Log data to file
//-----------------------------------------------------------------------------
int JGLogFileManager::write(const string& category, const JGLogTime& logtm,
                            const string& logstr)
{
    TRACE_FUNCTION(TRL_LOW, "JGLogFileManager::write");
    // Lock
    ACE_GUARD_RETURN(ACE_Thread_Mutex, ace_mon, this->m_tmlock, 1);

    string logdata = const_cast<JGLogTime&>(logtm).toString();
    logdata += _TX("  ");
    logdata += logstr;
#ifdef WIN32
    logdata += _TX(" ;\r\n");
#else
    logdata += _TX(" ;\n");
#endif

    return this->JGFileManager::write(category, logdata);
}


