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

//=============================================================================
/**
 *  @file    JGTerminalManager.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 "JGTerminalManager.h"
#include "JGEquipment.h"
#include "JGInfoManager.h"
#include "JGTaskTrigger.h"
#include "BS2ACKMessage.h"
#include "BS2ErrorMessage.h"
#include "BS2ListItem.h"
#include "BS2DeclAtoms.h"

static JGTerminalManager * _manager = NULL;

//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
JGTerminalManager::JGTerminalManager() : JGManager(CATEGORY_TERMINAL)
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGTerminalManager::JGTerminalManager");
}

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

}

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

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

    // initial equipment variables
    m_hostmsg = m_equipment->variable(VAR_HOST_MESSAGE);
    BS2Assert(m_hostmsg != NULL);
    m_recognition = m_equipment->variable(VAR_HOST_MESSAGE_RECOGNITION);
    BS2Assert(m_recognition != NULL);
    m_received = m_equipment->variable(VAR_HOST_MESSAGE_RECEIVED);
    BS2Assert(m_received != NULL);
    m_termmsg = m_equipment->variable(VAR_TREMINAL_MESSAGE);
    BS2Assert(m_termmsg != NULL);
    m_termreq = m_equipment->variable(VAR_TREMINAL_MESSAGE_SEND);
    BS2Assert(m_termreq != NULL);

    m_termCount = _tcstol(m_equipment->getConf(CONF_TERMINAL_COUNT).c_str(),
                          NULL, 10);
    return 0;
}

//-----------------------------------------------------------------------------
// Accept trigger from the variable.
//-----------------------------------------------------------------------------
int JGTerminalManager::notify(const string& category, JGVariable * var,
                              void * arg)
{
    TRACE_FUNCTION(TRL_LOW, "JGTerminalManager::notify");
    ACE_UNUSED_ARG(arg);

    TRACE_ERROR((_TX("%s : %s = %s\n"), category.c_str(), var->charName(),
                                        var->getv().toString().c_str()));

    if (category == TRID_TERMINAL || category == TRG_TERMINAL_MESSAGE)
    {
        // Set data item for S10F1
        // message text
        BS2Atom * atom = m_termmsg->getAtom();
        if (atom == NULL)
        {
            TRACE_ERROR((_TX("Request terminal message is nothing.\n")));
            return BEE_ERROR;
        }
        // terminal id
        BS2Atom * tidAtom;
        if (m_termCount > 1)
        {   // if equipment have multi terminal, request code is terminal id
            JGvalue tidval = m_termreq->getv();
            int tid = tidval.getInt();
            tidAtom = new BS2Binary((BYTE)(tid & 0xFF));
        }
        else
        {   // only one terminal
            tidAtom = new BS2Binary((BYTE)0);
        }
        BS2Item * tidItem = BS2Item::factory(_TX("TID"), tidAtom);
        BS2Item * textItem = BS2Item::factory(_TX("TEXT"), atom);

        // Send terminal request(S10F1).
        BS2ListItem * listItem = new BS2ListItem();
        listItem->add(tidItem);
        listItem->add(textItem);
        BS2Message * msg = BS2Message::factory(SFCODE(10,1));
        if (msg != NULL)
        {
            msg->add(listItem);
            this->send(msg);
        }

        JGvalue init_val(0);
        m_termreq->setv(init_val);      // Clear request
    }
    else if (category == TRG_MESSAGE_RECOGNITION)
    {
        m_equipment->sendEvent(this, EVT_MSG_RECOGNITION);
        JGvalue init_val(0);
        m_recognition->setv(init_val);      // Clear request
    }
    else
    {
        TRACE_ERROR((_TX("Illegal trigger name: %s.\n"), category.c_str()));
        return BEE_ERROR;
    }
    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Accept trigger from the variable.
//-----------------------------------------------------------------------------
int JGTerminalManager::notAllowMultiBlock()
{
    TRACE_FUNCTION(TRL_LOW, "JGTerminalManager::notAllowMultiBlock");

    // Set data item for S10F7
    // terminal id
    BS2Atom * atom;
    if (m_termCount > 1)
    {   // if equipment have multi terminal, received code is terminal id
        JGvalue tidval = m_received->getv();
        int tid = tidval.getInt();
        atom = new BS2Binary((BYTE)(tid & 0xFF));
    }
    else
    {   // only one terminal
        atom = new BS2Binary((BYTE)0);
    }
    BS2Item * item = BS2Item::factory(_TX("TID"), atom);

    // Send Multi-Block Not Allowed (S10F7).
    BS2Message * msg = BS2Message::factory(SFCODE(10,7));
    if (msg != NULL)
    {
        msg->add(item);
        this->send(msg);
    }
    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Teminal Display, Single message (S10F3 -> S10F4)
//-----------------------------------------------------------------------------
class _ParseSingle : public BS2Traverser
{
    friend class JGTerminalManager;
public:
    _ParseSingle() : m_tid(0) {}
    virtual ~_ParseSingle() {}

    JGid    m_tid;
    JGvalue m_text;

    virtual int parseItem(BS2Item * item) {
        BS2Atom * atom = item->atom();
        if (item->name() == _TX("TID"))
        {
            atom->get(m_tid);
        }
        else if (item->name() == _TX("TEXT"))
        {
            atom->get(m_text);
        }
        return 0;
    }
};

//-----------------------------------------------------------------------------
BS2Message * JGTerminalManager::dispBlock(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGTerminalManager::dispBlock");

    _ParseSingle hostText;
    msg->traverse(&hostText);    // Parse S10F3

    JGvalue tid;
    if (hostText.m_text.isNil())
    {   // As text is empty, clear display and reset recognition.
        tid = 0;                            // Cancel signal
        m_hostmsg->setv(hostText.m_text);   // Set received host mesage.
        // m_received->set(tid);            // Reset !?
    }
    else
    {
        if (hostText.m_tid.getInt() == 0)
        {
            if (m_termCount > 1)
            {
                TRACE_ERROR((
                    _TX("Though terminal is only one, id is not zero.\n")));
            }
            tid = 1;              // signal code must be not zero
        }
        else
        {
            tid = hostText.m_tid;
        }
        m_hostmsg->setv(hostText.m_text); // Set received host mesage.
        m_received->setv(tid);            // Notify display process.
    }

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

//-----------------------------------------------------------------------------
// Teminal Display, Multi-block message (S10F5 -> S10F6)
//-----------------------------------------------------------------------------
class _ParseMulti: public BS2Traverser
{
    friend class JGTerminalManager;
public:
    _ParseMulti() : m_tid(0) {}
    virtual ~_ParseMulti() {}

    JGid          m_tid;
    vector<string> m_texts;

    virtual int parseItem(BS2Item * item) {
        TRACE_FUNCTION(TRL_LOW, "JGTerminalManager::_ParseMulti::parseItem");
        BS2Atom * atom = item->atom();
        if (item->name() == _TX("TID"))
        {
            atom->get(m_tid);
        }
        else if (item->name() == _TX("TEXT"))
        {
            if (atom->isAscii())
            {
                string txt = ((BS2Ascii *)atom)->value();
                m_texts.push_back(txt);
            }
            else
            {
                TRACE_ERROR((
                    _TX("Received terminal text is illegal format.\n")));
            }
        }
        return 0;
    }
};

//-----------------------------------------------------------------------------
BS2Message * JGTerminalManager::dispMultiBlock(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGTerminalManager::dispMultiBlock");

    string rcvmsg(_TX(""));
    _ParseMulti hostTexts;
    msg->traverse(&hostTexts);    // Parse S10F5

    JGvalue tid;
    JGvalue msgval;
    if (hostTexts.m_texts.size() == 0)
    {   // As text is empty, clear display and reset recognition.
        tid = 0;                         // Cancel signal
        msgval = rcvmsg;
        m_hostmsg->setv(msgval);         // Clear host mesage.
        // m_received->set(tid);         // Reset !?
    }
    else
    {
        if (hostTexts.m_tid.getInt() == 0)
        {
            if (m_termCount > 1)
            {
                TRACE_ERROR((
                    _TX("Though terminal is only one, id is not zero.\n")));
            }
            tid = 1;              // signal code must be not zero
        }
        else
        {
            tid = hostTexts.m_tid;
        }
        // Convert multi block to single
        for (size_t i = 0; i < hostTexts.m_texts.size(); i++)
        {
            rcvmsg += hostTexts.m_texts[i];
            rcvmsg += _TX("\n");
        }
        msgval = rcvmsg;
        m_hostmsg->setv(msgval);   // Set received host mesage.
        m_received->setv(tid);     // Notify display process.
    }

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


//-----------------------------------------------------------------------------
// Thread of received trigger event.
//-----------------------------------------------------------------------------
BS2Message * JGTerminalManager::msg_svc(JGMessageTrigger * trigger,
                                        BS2Message * msg)
{
    ACE_UNUSED_ARG(trigger);
    BS2Message * replymsg = NULL;

    if (msg->sf() == SFCODE(10,3))
    {
        replymsg = this->dispBlock(msg);
    }
    else if (msg->sf() == SFCODE(10,5))
    {
        replymsg = this->dispMultiBlock(msg);
    }
    else
    {   // Unexpected message
        replymsg = this->unrecognized(msg);
    }
    return replymsg;
}



