// $Id: BS2Receiver.cpp,v 1.12 2003/03/15 04:36:49 fukasawa Exp $

//=============================================================================
/**
 *  @file    BS2Receiver.cpp
 *
 *  @author Fukasawa Mitsuo
 *
 *
 *    Copyright (C) 1998-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 "beesecs.h"
#include "BS2Device.h"
#include "BS2Receiver.h"
#include "BS2Sender.h"
#include "BS2MessageDictionary.h"
#include "BS2Message.h"
#include "BS2Stream.h"
#include "BS2BlockHeader.h"
#include "BS2ItemHeader.h"
#include "BS2Driver.h"
#include "BS2MessageInfo.h"
#include "BS2ErrorMessage.h"

//-----------------------------------------------------------------------------
// Transaction time out.
//-----------------------------------------------------------------------------
void BS2TransactionTimer::handle_time_out(const ACE_Time_Value&,
                                          const void *)
{
    TRACE_FUNCTION(TRL_LOW, "BS2TransactionTimer::handle_time_out");
    BS2MessageInfo * msgcont = NULL;
    BS2MessageInfo * evtinfo = m_trinfo->eventHeader();

    BS2OStream * ostmbuf = m_trinfo->buffer();
    BCHAR * hptr = ostmbuf->ptop();
    switch (m_trinfo->type())
    {
        case TRANSACTION_SEND_PRIMARY:
            if (evtinfo != NULL)
            {
                msgcont = new BS2MessageInfo;
                evtinfo->moveto(*msgcont);   // Move message
                msgcont->setResult(BS2RET_TIMEOUT);
                m_device->getReceiver()->notifyEventInfo(msgcont);
            }
            break;
        case TRANSACTION_RECV_PRIMARY:
            break;
        case TRANSACTION_RECV_SECONDARY:
            if (evtinfo != NULL)
            {
                msgcont = new BS2MessageInfo;
                evtinfo->moveto(*msgcont);    // Move message
                evtinfo->setResult(BS2RET_TIMEOUT);
                m_device->getReceiver()->notifyEventInfo(msgcont);
            }
            break;
        case TRANSACTION_SEND_SESSION:
            break;
        default:
            TRACE_ERROR((_TX("Unknown transaction type (%d).\n"), m_trinfo->type()));
            hptr = NULL;
            break;
    }

    // Logging error information.
    TRACE_DEBUG((_TX("Transaction(0x%X) timeout.\n"), m_trinfo->self()));

    // Send S9F9(Transaction Timer timeout)
    if (hptr != NULL)
    {
        if (m_device->isEquipment() &&
            (m_trinfo->type() == TRANSACTION_RECV_PRIMARY))
        {
            BS2BlockHeader * header = (BS2BlockHeader *)hptr;
            BS2S9F9Message * s9f9 = new BS2S9F9Message(*header);
            m_device->getSender()->send(s9f9);
            delete s9f9;
        }
    }

    if (m_trinfo->getTransactionManager())
    {   // delete transaction.
        m_trinfo->getTransactionManager()->remove(m_trinfo);
    }
    delete this;
    return ;
}

//-----------------------------------------------------------------------------
// Receive time out.
//-----------------------------------------------------------------------------
void BS2ReceiveTimer::handle_time_out(const ACE_Time_Value&,
                                      const void *)
{
    TRACE_FUNCTION(TRL_LOW, "BS2ReceiveTimer::handle_time_out");

    m_receiver->stopReceive();
    delete this;
    return ;
}

//-----------------------------------------------------------------------------
//
// BS2Receiver
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Spawn off a new thread.
//-----------------------------------------------------------------------------
int BS2Receiver::open(void *)
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::open");

    if (this->activate(THR_NEW_LWP | THR_DETACHED) == -1)
    {
        TRACE_ERROR((_TX("%p\n"), _TX("spawn")));
        return -1;
    }
    return 0;
}

//-----------------------------------------------------------------------------
int BS2Receiver::close(u_long exit_status)
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::close");
    ACE_DEBUG((LM_DEBUG,
        ACE_TEXT("(%t) thread is exiting with status %d in module %s\n"),
        exit_status, "BS2Receiver"));

    return 0;
}

//-----------------------------------------------------------------------------
// Simply enqueue the Message_Block into the end of the queue.
//-----------------------------------------------------------------------------
int BS2Receiver::put(ACE_Message_Block *mb, ACE_Time_Value *tv)
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::put");
    return this->putq(mb, tv);
}

//-----------------------------------------------------------------------------
// Schedule a new timer.
//-----------------------------------------------------------------------------
int BS2Receiver::addTimer(const ACE_Time_Value& interval,
                          BS2TransactionInfo * trinfo)
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::addTimer");

    BS2TransactionTimer * h = new BS2TransactionTimer(interval, trinfo, m_device);

    int id = ACE_Proactor::instance()->schedule_timer(*h, 0, interval);
    if (id == -1)
    {
        TRACE_ERROR((_TX("schedule failed")));
        return BEE_ERROR;
    }
    // We store the id into the handler, this is only used to produce
    // nicer messages.
    // @@ Should this be something that a user can extract?
    h->set_id(id);
    trinfo->timer_id(id);

    // TRACE_DEBUG((_TX("Start timer(%d) : timer id = %d, transaction = 0x%x\n"),
    //              interval.msec(), id, trinfo->self()));

    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Schedule a new timer.
//-----------------------------------------------------------------------------
int BS2Receiver::addTimer(const ACE_Time_Value& interval,
                          BS2TimerHandler * tmh)
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::addTimer");

    int id = ACE_Proactor::instance()->schedule_timer(*tmh, 0, interval);
    if (id == -1)
    {
        TRACE_ERROR((_TX("schedule failed")));
        return BEE_ERROR;
    }
    // We store the id into the handler, this is only used to produce
    // nicer messages.
    tmh->set_id(id);

    // TRACE_DEBUG((_TX("Start timer(%d) : scheduling timer id = %d\n"),
    //              interval.msec(), id));

    return id;
}

//-----------------------------------------------------------------------------
// Cancel a timer.
//-----------------------------------------------------------------------------
int BS2Receiver::cancelTimer(int timer_id)
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::cancelTimer");

    if (timer_id < 0)
    {
        return BEE_ERROR;
    }
    int result = ACE_Proactor::instance()->cancel_timer(timer_id);

    // TRACE_DEBUG((_TX("scheduling timer id = %d\n"), timer_id));
    return result;
}

//-----------------------------------------------------------------------------
// Lists the timers in the queue.  Ignores the argument.
//-----------------------------------------------------------------------------
int BS2Receiver::listTimer()
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::listTimer");

    // Dump the timer queue contents.
    this->dump();

    return 0;
}

//-----------------------------------------------------------------------------
// Dispatch receive data.
//-----------------------------------------------------------------------------
int BS2Receiver::dispatch(BCHAR * data, int size)
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::dispatch");
    BS2BlockHeader * bheader = (BS2BlockHeader *)(data);
    int stnum = bheader->getStreamNum();
    int fcnum = bheader->getFunctionNum();

    // Check device ID.
    if ((bheader->getDeviceId() != m_device->getDriver()->m_deviceId) &&
        (stnum != 9))
    {
        if (m_device->isEquipment())
        {
            BS2S9F1Message * s9f1 = new BS2S9F1Message(*bheader);
            m_device->getSender()->send(s9f1);
        }
        return BEE_SUCCESS;       // not send NAK
    }

    // Check stream number.
    if (! BS2MessageDictionary::instance()->existStreamNum(stnum))
    {
        if (m_device->isEquipment())
        {
            BS2S9F3Message * s9f3 = new BS2S9F3Message(*bheader);
            m_device->getSender()->send(s9f3);
        }
        return BEE_SUCCESS;       // not send NAK
    }

    // Check function number.
    if (BS2MessageDictionary::instance()->search(SFCODE(stnum, fcnum)) == NULL)
    {
        if (m_device->isEquipment())
        {
            BS2S9F5Message * s9f5 = new BS2S9F5Message(*bheader);
            m_device->getSender()->send(s9f5);
        }
        return BEE_SUCCESS;       // not send NAK
    }

    ACE_Message_Block * mb;
    ACE_NEW_RETURN(mb, ACE_Message_Block(size + 16), BEE_ERROR);
    memcpy(mb->rd_ptr(), data, size);
    mb->length(size);
    this->put(mb);

    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Receive message.
//-----------------------------------------------------------------------------
int BS2Receiver::receive(BS2MessageInfo& ret)
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::receive");

    ret.setMessage(NULL);
    ret.setResult(BS2RET_NORMAL);
    ret.setTransactionID(0);
    if (m_requestReceive == 1)
        return BEE_ERROR;
    m_requestReceive = 1;
    m_recvEvent.reset();
    BS2MessageInfo * rinfo = this->getEventInfo();
    if (rinfo == NULL)
    {
        if (m_recvEvent.wait() == -1)
        {
            TRACE_ERROR((_TX("SECS stream buffer receiving error.\n")));
            rinfo = NULL;
        }
        else
            rinfo = this->getEventInfo();
    }

    // received message
    if (rinfo != NULL)
    {
        rinfo->moveto(ret);
        if (rinfo->canDelete())
            delete rinfo;
    }
    m_requestReceive = 0;                // unlock request
    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Receive message with limit time.
//-----------------------------------------------------------------------------
int BS2Receiver::receiveWithLimit(BS2MessageInfo& ret, ACE_Time_Value *tv)
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::receive");

    ret.setMessage(NULL);
    ret.setResult(BS2RET_NORMAL);
    ret.setTransactionID(0);
    if (m_requestReceive == 1)
        return BEE_ERROR;
    if (tv != NULL)
    {
        BS2ReceiveTimer * timer = new BS2ReceiveTimer(*tv, this);
        this->addTimer(*tv, timer);
    }

    return this->receive(ret);
}

//-----------------------------------------------------------------------------
// Sense received message.
//-----------------------------------------------------------------------------
int BS2Receiver::sense(BS2MessageInfo& ret)
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::sense");

    int result;
    BS2MessageInfo * rinfo;

    ret.setMessage(NULL);
    ret.setResult(BS2RET_NORMAL);
    ret.setTransactionID(0);
    if (m_requestReceive == 1)
    {
        rinfo = NULL;
        result = BEE_ERROR;
    }
    else
    {
        m_requestReceive = 1;
        if ((rinfo = this->getEventInfo()) != NULL)
        {   // received message
            rinfo->moveto(ret);
            if (rinfo->canDelete())
                delete rinfo;
            result = BEE_SUCCESS;
        }
        else
            result = BEE_ERROR;
        m_requestReceive = 0;                // unlock request
    }
    return result;
}

//-----------------------------------------------------------------------------
// Get received message in queue.
//-----------------------------------------------------------------------------
BS2MessageInfo * BS2Receiver::getEventInfo()
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::getEventInfo");

    ACE_MT(ACE_GUARD_RETURN(ACE_Thread_Mutex, ace_mon, this->m_lock, NULL));
    if (this->m_events.empty())
        return NULL;
    BS2MessageInfo * evtinfo = (BS2MessageInfo *)this->m_events.front();
    this->m_events.pop_front();
    return evtinfo;
}

//-----------------------------------------------------------------------------
// Put received message in queue.
//-----------------------------------------------------------------------------
int BS2Receiver::putEventInfo(BS2MessageInfo * evtinfo)
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::putEventInfo");

    ACE_MT(ACE_GUARD_RETURN(ACE_Thread_Mutex, ace_mon, this->m_lock, BEE_ERROR));
    this->m_events.push_back(evtinfo);
    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// End wait receiving.
//-----------------------------------------------------------------------------
int BS2Receiver::stopReceive()
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::stopReceive");
    m_recvEvent.signal();   // signal to wait in receive method.

    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Dummy thread
//-----------------------------------------------------------------------------
int BS2Receiver::svc(void)
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::svc");

    int result = 0;
    bool receiving = false;
    ACE_Message_Block * mb;

    for (;;)
    {
        result = this->getq(mb);
        if (result == -1)
            break;

        if (receiving)
        {
            this->ungetq(mb);
            continue;
        }

        int length = mb->length();
        if (length > 0)
        {
            receiving = true;
            BCHAR * datap = mb->rd_ptr();

            result = this->parse(datap, length);   // block receive.
            if (result != BEE_SUCCESS)
            {
                TRACE_ERROR((_TX("Error message.\n")));
            }
            receiving = false;
        }
        mb->release();

        if (length == 0)             // shutdown
            break;
    }

    if (result == -1 && errno == EWOULDBLOCK)
    {
        ACE_ERROR((LM_ERROR,
            ACE_TEXT("(%t) %p\n"), ACE_TEXT("timed out waiting for message")));
    }

    return 0;
}

//-----------------------------------------------------------------------------
// Dummy parse proc.
//-----------------------------------------------------------------------------
int BS2Receiver::parse(BCHAR *, int)
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::parse");
    return 0;
}

//-----------------------------------------------------------------------------
// parse stream-9
//-----------------------------------------------------------------------------
int BS2Receiver::parseStreamNine(BCHAR * data, int size)
{
    TRACE_FUNCTION(TRL_LOW, "BS2Receiver::parseStreamNine");

    BS2TransactionManager * trmgr = m_device->getTransactionManager();
    BS2BlockHeader * header = (BS2BlockHeader *)data;
    BS2BlockHeader * mhead;
    BS2ItemHeader * itemhead = (BS2ItemHeader *)(data + sizeof(BS2BlockHeader));
    BS2TransactionInfo * trinfo;
    BS2MessageInfo * evtinfo;
    BS2OStream * ostmbuf;
    BS2Message * msg;

    mhead = (BS2BlockHeader *)(itemhead->data());
    if (header->getFunctionNum() == 9 || header->getFunctionNum() == 13)
    {   // Conversation timeout
        trinfo = trmgr->buffer(mhead, TRANSACTION_RECV_PRIMARY);
        if (trinfo != NULL)
        {
            trmgr->remove(trinfo);

            // evtinfo = trmgr->info(trinfo, BS2MessageInfo::TIMEOUT_MESSAGE, NULL);
            // this->putEventInfo(evtinfo);
            // m_recvEvent.signal();   // signal to wait in receive method.
        }
        return 0;
    }
    else
    {
        trinfo = trmgr->buffer(mhead, TRANSACTION_SEND_PRIMARY);
        if (trinfo != NULL)
        {
            // Cancel reply timer
            this->cancelTimer(trinfo->timer_id());

            // Create S9F??
            ostmbuf = trinfo->buffer();
            ostmbuf->reset();
            ostmbuf->write(data, size);

            msg = BS2MessageDictionary::instance()->make(ostmbuf);
            if (msg == NULL)
            {   // Un-supportted message
                ACE_ERROR((LM_ERROR, ACE_TEXT("Receive illegal S9Fx data.\n")));
                // Remove the transaction
                trmgr->remove(trinfo);

                // evtinfo = trmgr->info(trinfo, BS2MessageInfo::ILLEGAL_MESSAGE, NULL);
                // this->putEventInfo(evtinfo);
                // m_recvEvent.signal();   // signal to wait in receive method.
            }
            else
            {
                evtinfo = trmgr->info(trinfo, BS2RET_NORMAL, msg);
                this->putEventInfo(evtinfo);
                m_recvEvent.signal();   // signal to wait in receive method.
            }
        }
        return 1;
    }
}

