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

//=============================================================================
/**
 *  @file    JGEventManager.cpp
 *
 *  @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 "JGInfoManager.h"
#include "JGEventManager.h"
#include "JGEquipment.h"
#include "JGLimit.h"
#include "JGCommDevice.h"
#include "JGTaskTrigger.h"
#include "JGLogFileManager.h"
#include "JGLogTime.h"
#include "BS2ACKMessage.h"
#include "BS2ErrorMessage.h"
#include "BS2ListItem.h"
#include "BS2DeclAtoms.h"

static JGEventManager * _manager = NULL;

//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
JGEventManager::JGEventManager() : JGManager(CATEGORY_EVENT)
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGEventManager::JGEventManager");
}

//-----------------------------------------------------------------------------
JGEventManager::~JGEventManager()
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGEventManager::~JGEventManager");

}

//-----------------------------------------------------------------------------
// Return own.
//-----------------------------------------------------------------------------
JGEventManager * JGEventManager::instance()
{
    TRACE_FUNCTION(TRL_LOW, "JGEventManager::instance");
    if (_manager == NULL)
    {
        _manager = new JGEventManager;
    }
    return _manager;
}

//-----------------------------------------------------------------------------
// Initialize
//-----------------------------------------------------------------------------
int JGEventManager::init(void * parm)
{
    TRACE_FUNCTION(TRL_MIDDLE, "JGEventManager::init");
    ACE_UNUSED_ARG(parm);

    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Find Event by CEID
//-----------------------------------------------------------------------------
JGEvent * JGEventManager::find(JGid& ceid)
{
    TRACE_FUNCTION(TRL_LOW, "JGEventManager::find");

    JGEventTable::iterator iter = m_evtable.find(ceid);
    if (iter == m_evtable.end())
    {
        return NULL;
    }
    JGEvent * event = &((*iter).second);
    return event;
}

//-----------------------------------------------------------------------------
// Find Report by RPTID
//-----------------------------------------------------------------------------
JGReport * JGEventManager::findReport(JGid& rptid)
{
    TRACE_FUNCTION(TRL_LOW, "JGEventManager::findReport");

    JGReportTable::iterator iter = m_rptable.find(rptid);
    if (iter == m_rptable.end())
    {
        return NULL;
    }
    JGReport * report = &((*iter).second);
    return report;
}


//-----------------------------------------------------------------------------
// Create Event Report
//-----------------------------------------------------------------------------
BS2Message * JGEventManager::createEventReport(JGid& ceid, JGVariable * var)
{
    TRACE_FUNCTION(TRL_LOW, "JGEventManager::createEventReport");

    JGEventTable::iterator iter = m_evtable.find(ceid);
    if (iter == m_evtable.end())
    {
        TRACE_ERROR((_TX("ceid(%s) is nothing.\n"), ceid.toString().c_str()));
        return NULL;
    }
    JGEvent * event = &((*iter).second);

    BS2Message * msg = NULL;
    if (event->enabled())
    {
        BS2ListItem * rootlist = new BS2ListItem();
        BS2Item * item = this->getDataID();
        rootlist->add(item);

        if (ceid.toString() == CEID_LIMIT)
        {
            JGLimit * limit = var->limit();
            if (limit != NULL)
            {
                item = limit->ceidItem(m_equipment->getIdType());
            }
            else
            {
                item = BS2Item::factory(_TX("CEID"), ceid);
            }
        }
        else
        {
            item = BS2Item::factory(_TX("CEID"), ceid);
        }

        rootlist->add(item);

        item = event->createReports(var);
        rootlist->add(item);

        msg = BS2Message::factory(SFCODE(6,11));
        BS2Assert(msg != NULL);
        msg->add(rootlist);
    }
    return msg;
}

//-----------------------------------------------------------------------------
// Apply defined report to table.
//-----------------------------------------------------------------------------
int JGEventManager::applyReport(JGReport& defined)
{
    TRACE_FUNCTION(TRL_LOW, "JGEventManager::applyReport");

    JGid rptid = defined.rptid();
    JGReportTable::iterator rpt_it = m_rptable.find(rptid);
    if (rpt_it == m_rptable.end())
    {   // current report is new
        if (defined.vids().size() > 0)
        {
            m_rptable.insert(JGReportPair(rptid, defined));
        }
    }
    else
    {
        if (defined.vids().size() > 0)
        {   // update report
            JGReport * report = &((*rpt_it).second);
            report->vids().clear();
            report->vids() = defined.vids();
        }
        else
        {   // delete the report
            this->releaseReport(rptid);
        }
    }
    return 0;
}

//-----------------------------------------------------------------------------
// Request to send Event Message (S6F11).
//-----------------------------------------------------------------------------
int JGEventManager::request(JGManager * mngr, const string& event,
                            JGVariable * arg)
{
    TRACE_FUNCTION(TRL_LOW, "JGEventManager::request");

    int result;
    if (event == EVT_EC_CHANGED)
    {
        result = JGEventManager::instance()->reqSend(mngr, event, arg);
    }
    else
    {
        result = JGEventManager::instance()->reqSend(mngr, event, arg);
    }

    return result;
}

//-----------------------------------------------------------------------------
// Rquest to send Event Message (S6F11).
//-----------------------------------------------------------------------------
int JGEventManager::reqSend(JGManager * mngr, const string& event,
                            JGVariable * arg)
{
    TRACE_FUNCTION(TRL_LOW, "JGEventManager::reqSend");
    ACE_UNUSED_ARG(mngr);

    JGActionTable::iterator iter = m_actable.find(event);
    if (iter == m_actable.end())
    {
        TRACE_ERROR((_TX("%s manager does not find the act(%s).\n"),
                    event.c_str()));
        return BEE_ERROR;
    }


    JGid ceid = (*iter).second;    // Get CEID

    {   // *** LOGGING ***
        JGLogTime now;
        string  logstr = ceid.toString();
        JGLogFileManager::instance()->log(this->name(), now, logstr);
    }

    BS2Message * msg = this->createEventReport(ceid, arg);
    if (msg != NULL)
    {
        int tid = m_device->send(msg, this);
        if (tid == 0)
        {
            TRACE_ERROR((_TX("send message error.\n")));
            return BEE_ERROR;
        }
    }
    else
    {
        TRACE_DEBUG((_TX("The event(%s) is disable.\n"),
                     ceid.toString().c_str()));
    }
    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Release the report.
//-----------------------------------------------------------------------------
void JGEventManager::releaseReport(JGid& rptid)
{
    TRACE_FUNCTION(TRL_LOW, "JGEventManager::releaseReport");

    JGReportTable::iterator rpt_it = m_rptable.find(rptid);
    if (rpt_it != m_rptable.end())
    {   // delete the report
        m_rptable.erase(rpt_it);
    }
    JGEventTable::iterator evt_it = m_evtable.begin();
    for ( ; evt_it != m_evtable.end(); ++evt_it)
    {
        JGEvent * evt = &((*evt_it).second);
        JGReports * rptset = evt->reportSet();
        JGReports::iterator iter = rptset->begin();
        while (iter != rptset->end())
        {   // append report
            JGReport * rpt = (*iter);
            if (rpt->rptid() == rptid)
            {
                iter = rptset->erase(iter);
                evt->enabled(false);
            }
            else
            {
                iter++;
            }
        }
    }
}


//-----------------------------------------------------------------------------
// Enabled/Disable Event Request (S2F33 -> S2F34)
//-----------------------------------------------------------------------------
class _ParseReport : public BS2Traverser
{
    friend class JGEventManager;
public:
    _ParseReport() {}
    virtual ~_ParseReport() {}

    JGvalue      m_dataid;
    JGReportArray m_reports;

    virtual int parseItem(BS2Item * item) {
        TRACE_FUNCTION(TRL_LOW, "JGEventManager::_ParseReport::parseItem");
        BS2Atom * atom = item->atom();
        if (item->name() == _TX("RPTID"))
        {
            string rptname;
            JGid   rptid;
            atom->get(rptid);
            rptid.get(rptname);
            JGReport report(rptid, rptname);
            m_reports.push_back(report);
        }
        else if (item->name() == _TX("VID"))
        {
            JGid vid;
            atom->get(vid);
            JGVariable * var = JGEquipment::instance()->variable(vid);
            if (var == NULL)
            {
                return -1;
            }
            m_reports.back().insert(var);
        }
        else if (item->name() == _TX("DATAID"))
        {
            atom->get(m_dataid);
        }
        return 0;
    }
};

//-----------------------------------------------------------------------------
//
BS2Message * JGEventManager::defineReport(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGEventManager::defineReport");

    BS2Message * replymsg;
    BYTE ack;
    _ParseReport rptinfo;

    int result = msg->traverse(&rptinfo);    // parse S2F33
    if (result < 0)
    {
        ack = ACK_NG;
        replymsg = BS2Message::response(msg, ack, _TX("DRACK"));
        return replymsg;
    }

    ack = ACK_OK;
    if (rptinfo.m_reports.size() == 0)
    {   // unlink all report.
        m_rptable.clear();
        JGEventTable::iterator iter = m_evtable.begin();
        for ( ; iter != m_evtable.end(); ++iter)
        {
            JGEvent * event = &((*iter).second);
            event->clearReports();
            event->enabled(false);
        }
    }
    else
    {
        for (size_t i = 0; i < rptinfo.m_reports.size(); i++)
        {
            applyReport(rptinfo.m_reports[i]);
            resetCeed(rptinfo.m_reports[i]);
        }
    }

    // Create reply message.
    replymsg = BS2Message::response(msg, ack, _TX("DRACK"));
    return replymsg;
}

//-----------------------------------------------------------------------------
// Link Event Request (S2F35 -> S2F36)
//-----------------------------------------------------------------------------
class _ParseEvent : public BS2Traverser
{
    friend class JGEventManager;
public:
    _ParseEvent() {}
    virtual ~_ParseEvent() {}

    JGvalue        m_dataid;
    vector<JGid>   m_ceids;
    vector<JGEvent> m_events;

    virtual int parseItem(BS2Item * item) {
        TRACE_FUNCTION(TRL_LOW, "JGEventManager::_ParseEvent::parseItem");
        BS2Atom * atom = item->atom();
        if (item->name() == _TX("CEID"))
        {
            JGid ceid;
            atom->get(ceid);
            JGEvent * event = JGEventManager::instance()->find(ceid);
            if (event == NULL)
            {
                return -4;             // ceid is not exist
            }
            if (! event->isDeleted())
            {
                return -3;             // ceid is defined
            }
            for (size_t i = 0; i < m_ceids.size(); i++)
            {
                if (m_ceids[i] == ceid)
                {
                    return -3;         // duplicate ceid
                }
            }
            m_ceids.push_back(ceid);
            JGEvent newEvent(*event);
            newEvent.clearReports();
            newEvent.enabled(false);
            m_events.push_back(newEvent);
        }
        else if (item->name() == _TX("RPTID"))
        {
            JGid rptid;
            atom->get(rptid);
            JGReport * report = JGEventManager::instance()->findReport(rptid);
            if (report == NULL)
            {
                return -5;            // report is not found
            }
            m_events.back().insert(report);
        }
        else if (item->name() == _TX("DATAID"))
        {
            atom->get(m_dataid);
        }
        return 0;
    }
};

//-----------------------------------------------------------------------------
//
BS2Message * JGEventManager::linkEventReport(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGEventManager::linkEventReport");

    BS2Message * replymsg;
    BYTE ack;
    _ParseEvent evtinfo;

    int result = msg->traverse(&evtinfo);    // parse S2F35
    if (result < 0)
    {
        ack = -result;
        replymsg = BS2Message::response(msg, ack, _TX("LRACK"));
        return replymsg;
    }

    if (evtinfo.m_events.size() == 0)
    {
        ack = 2;            // Illegal format
        replymsg = BS2Message::response(msg, ack, _TX("LRACK"));
        return replymsg;
    }

    // Update link reports
    ack = ACK_OK;
    for (size_t i = 0; i < evtinfo.m_events.size(); i++)
    {
        JGEvent * event = this->find(evtinfo.m_events[i].ceid());
        BS2Assert(event != NULL);

        event->clearReports();
        event->enabled(false);
        *event = evtinfo.m_events[i];
    }

    // Create reply message.
    replymsg = BS2Message::response(msg, ack, _TX("LRACK"));
    return replymsg;
}

//-----------------------------------------------------------------------------
// Enabled/Disable Event Request (S2F37 -> S2F38)
//-----------------------------------------------------------------------------
class _ParseCeed : public BS2Traverser
{
    friend class JGEventManager;
public:
    _ParseCeed() {}
    virtual ~_ParseCeed() {}

    JGvalue           m_ceed;
    vector<JGEvent *> m_events;

    virtual int parseItem(BS2Item * item) {
        TRACE_FUNCTION(TRL_LOW, "JGEventManager::_ParseCeed::parseItem");
        BS2Atom * atom = item->atom();
        if (item->name() == _TX("CEED"))
        {
            atom->get(m_ceed);
        }
        else if (item->name() == _TX("CEID"))
        {
            JGid ceid;
            atom->get(ceid);
            JGEvent * event = JGEventManager::instance()->find(ceid);
            if (event == NULL)
            {
                TRACE_ERROR((_TX("does not find the event(%s).\n"),
                             ceid.toString().c_str()));
                return -1;
            }
            m_events.push_back(event);
        }
        return 0;
    }
};

//-----------------------------------------------------------------------------
BS2Message * JGEventManager::setEnabled(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGEventManager::setEnabled");

    BS2Message * replymsg;
    BYTE ack;
    _ParseCeed ceedinfo;
    int result = msg->traverse(&ceedinfo);    // parse S2F37
    if (result < 0)
    {
        ack = ACK_NG;
        replymsg = BS2Message::response(msg, ack, _TX("ERACK"));
        return replymsg;
    }

    // Change Enable/Disable
    JGEvent * event;
    bool ceed = (ceedinfo.m_ceed.getInt() == 0) ? false : true;
    if (ceedinfo.m_events.size() == 0)
    {   // change enable/disable bit of all events.
        JGEventTable::iterator iter = m_evtable.begin();
        for ( ; iter != m_evtable.end(); ++iter)
        {
            event = &((*iter).second);
            event->enabled(ceed);
        }
    }
    else
    {   // change enable/disable bit of each event
        for (size_t i = 0; i < ceedinfo.m_events.size(); i++)
        {
            event = ceedinfo.m_events[i];
            event->enabled(ceed);
        }
    }
    // Create reply message.
    ack = ACK_OK;
    replymsg = BS2Message::response(msg, ack, _TX("ERACK"));
    return replymsg;
}

//-----------------------------------------------------------------------------
// Event Report Request (S6F15 --> S6F16)
//-----------------------------------------------------------------------------
BS2Message * JGEventManager::eventReportList(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGEventManager::eventReportList");
    BS2Item * item;
    BS2Message * replymsg = BS2Message::response(msg);   // Create S6F16
    BS2ListItem * rootlist = new BS2ListItem();
    replymsg->add(rootlist);

    BS2Atom * atom = msg->getAtom(_TX("CEID"));
    if (atom == NULL)
    {
        TRACE_ERROR((_TX("CEID is nothing.\n")));
        item = this->getDataID();
        rootlist->add(item);
        item = BS2Item::factory(_TX("CEID"), new BS2Ascii());
        rootlist->add(item);
        return replymsg;
    }

    JGid ceid;
    atom->get(ceid);
    item = this->getDataID();     // Set "DATAID"
    rootlist->add(item);
    item = BS2Item::factory(_TX("CEID"), ceid);
    rootlist->add(item);

    JGEventTable::iterator iter = m_evtable.find(ceid);
    if (iter == m_evtable.end())
    {
        TRACE_ERROR((_TX("%s manager: event(%s) is not found.\n"),
                     ceid.toString().c_str()));
        return replymsg;
    }
    JGEvent * event = &((*iter).second);

    // * Ignore enable bit !?

    // Making report data
    item = event->createReports();
    rootlist->add(item);

    return replymsg;
}

//-----------------------------------------------------------------------------
// Individual Report Request (S6F19 --> S6F20)
//-----------------------------------------------------------------------------
BS2Message * JGEventManager::individualReport(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGEventManager::individualReport");
    BS2Message * replymsg = BS2Message::response(msg);   // Create S6F20

    BS2Atom * atom = msg->getAtom(_TX("RPTID"));
    if (atom == NULL)
    {
        TRACE_ERROR((_TX("RPTID is nothing.\n")));
        replymsg->add(new BS2ListItem());    // Set null list
        return replymsg;
    }

    JGid rptid;
    atom->get(rptid);

    JGReportTable::iterator iter = m_rptable.find(rptid);
    if (iter == m_rptable.end())
    {
        TRACE_ERROR((_TX("report(%s) is not found.\n"),
                     rptid.toString().c_str()));
        replymsg->add(new BS2ListItem());    // Set null list
        return replymsg;
    }
    JGReport * report = &((*iter).second);

    BS2Item * item = report->makeVList();    // Making report data

    replymsg->add(item);
    return replymsg;
}

//-----------------------------------------------------------------------------
// Reset CEED by S2F33.
//-----------------------------------------------------------------------------
void JGEventManager::resetCeed(JGReport& report)
{
    TRACE_FUNCTION(TRL_LOW, "JGEventManager::resetCeed");

    JGid rptid = report.rptid();
    JGEventTable::iterator evt_it = m_evtable.begin();
    for ( ; evt_it != m_evtable.end(); ++evt_it)
    {
        JGEvent * evt = &((*evt_it).second);
        JGReports * rptset = evt->reportSet();
        JGReports::iterator iter = rptset->begin();
        for ( ; iter != rptset->end(); ++iter)
        {   // append report
            JGReport * rpt = (*iter);
            if (rpt->rptid() == rptid)
            {
                evt->enabled(false);
            }
        }
    }

}

//-----------------------------------------------------------------------------
// Thread of received message event.
//-----------------------------------------------------------------------------
BS2Message * JGEventManager::msg_svc(JGMessageTrigger * trigger, BS2Message * msg)
{
    ACE_UNUSED_ARG(trigger);

    BS2Message * replymsg = NULL;

    if (msg->sf() == SFCODE(6,12))
    {
        ; // ignore;
    }
    else if (msg->sf() == SFCODE(2,33))
    {
        replymsg = this->defineReport(msg);
    }
    else if (msg->sf() == SFCODE(2,35))
    {
        replymsg = this->linkEventReport(msg);
    }
    else if (msg->sf() == SFCODE(2,37))
    {
        replymsg = this->setEnabled(msg);
    }
    else if (msg->sf() == SFCODE(6,15))
    {
        replymsg = this->eventReportList(msg);
    }
    else if (msg->sf() == SFCODE(6,19))
    {
        replymsg = this->individualReport(msg);
    }
    else
    {   // Unexpected message
        replymsg = this->unrecognized(msg);
    }

    return replymsg;
}



