/* ======================================================================== *\
!
! *
! * This file is part of Stesy, the MAGIC Steering System
! * Software. It is distributed to you in the hope that it can be a useful
! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
! * It is distributed WITHOUT ANY WARRANTY.
! *
! * Permission to use, copy, modify and distribute this software and its
! * documentation for any purpose is hereby granted without fee,
! * provided that the above copyright notice appear in all copies and
! * that both that copyright notice and this permission notice appear
! * in supporting documentation. It is provided "as is" without express
! * or implied warranty.
! *
!
!
!   Author(s): Thomas Bretz 1/2008 <mailto:tbretz@astro.uni-wuerzburg.de>
!
!   Copyright: MAGIC Software Development, 2000-2008
!
!
\* ======================================================================== */

///////////////////////////////////////////////////////////////////////
//
// Ethernet
//
// Class describing the interface to the Janz card in RawCan mode.
//
///////////////////////////////////////////////////////////////////////
#include "ethernet.h"

#include "MLog.h"
#include "MLogManip.h"

#include <TSocket.h>

ClassImp(Ethernet);

#undef DEBUG

using namespace std;

// --------------------------------------------------------------------------
//
// Constructor. Sets logging.
//  Set the receiving thread to priority -10 and detached.
//
//  Open the device.
//  reset the device
//  Enable the fifo buffers
//  Set the baud rate to the given rate
//  Disable passthrough of all cobids (all canbus messages)
//  and switch the can bus communication on
//
Ethernet::Ethernet(const char *addr, const int tx, const int rx, CanOpen *receiver)
    : MTcpIpI(rx),/* MTcpIpO(addr, tx),*/ Interface(receiver), fTxAddress(addr), fTxPort(tx)
{
    gLog << inf2 << "- Ethernet initialized." << endl;
}

// --------------------------------------------------------------------------
//
// Destructor. Stopt the receiver, disables the bus connection and
// close the device
//
Ethernet::~Ethernet()
{
    CancelThread();
    gLog << inf2 << "- Ethernet stopped." << endl;
}

// --------------------------------------------------------------------------
//
Bool_t Ethernet::ReadSocket(TSocket &rx)
{
    Int_t pos = -1;

    Message msg;
    msg.cmd = M_BCAN_RX_ind;
    msg.data[0] = 0;
    msg.data[1] = 0;

    const TString address = MTcpIpO::GetSocketAddress(rx);

    while (!IsThreadCanceled())
    {
        unsigned char c;
        const Int_t len = rx.RecvRaw(&c, 1);

        // For details see TSocket::RecvRaw
        // -1: // ERROR
        // EINVAL, EWOULDBLOCK
        // -5: // EPIPE || ECONNRESET = Pipe broken or connection reset by peer
        //  0: Data received with zero length! (Connection lost/call interrupted)
        if (len<=0)
            return kFALSE;

        // Data received
        if (len>1)
        {
            gLog << err << "Data received from " << address << " is more than one byte!" << endl;
            continue;
        }

        if (pos<0)
        {
            if (c>=MSGLEN)
            {
                gLog << err << "Data received from " << address << " too long (> " << MSGLEN << ")" << endl;
                continue;
            }

            msg.len = c;
            pos = 2;
            continue;
        }

        //            if (pos==2 && c==0x0a)
        //                continue;

        msg.data[pos++] = c;
        if (pos-2<msg.len)
            continue;

#ifdef DEBUG
        cout << "*** RcvdCanFrame len=" << dec << msg.len << ": ";
        for (int i=0; i<msg.len; i++)
            cout << "0x" << setfill('0') << setw(2) << hex << (int)((msg.data+2)[i]) << " ";
        cout << dec << endl;
#endif

        pos = -1;

        // String completed
        HandleMessage(msg);

        return kTRUE;
    }

    return kTRUE;
}

/*
void Ethernet::ReadSocket(TSocket &rx)
{
    Int_t pos = -1;

    Message msg;
    msg.cmd = M_BCAN_RX_ind;
    msg.data[0] = 0;
    msg.data[1] = 0;

    const TString address = MTcpIpO::GetSocketAddress(rx);

    while (!IsThreadCanceled())
    {
        //TThread::CancelPoint();

        unsigned char c;
        const Int_t len = rx.RecvRaw(&c, 1);

        //TThread::CancelPoint();

        // No data received (non-blocking mode)
        if (len<0)
        {
            usleep(1);
            continue;
        }

        // Data received with zero length!
        if (len==0)
        {
            gLog << warn << "WARNING - Connection lost (received 0bytes) to " << address << endl;
            //break; // This break is for TEST PURPOSE FIXME!!!
            return;
        }

        // Data received
        if (len>1)
        {
            gLog << err << "Data received from " << address << " is more than one byte!" << endl;
            continue;
        }

        if (pos<0)
        {
            if (c>=MSGLEN)
            {
                cout << "Data received from " << address << " too long (> " << MSGLEN << ")" << endl;
                continue;
            }

            msg.len = c;
            pos = 2;
            continue;
        }

        //            if (pos==2 && c==0x0a)
        //                continue;

        msg.data[pos++] = c;
        if (pos-2<msg.len)
            continue;

#ifdef DEBUG
        cout << "*** RcvdCanFrame len=" << dec << msg.len << ": ";
        for (int i=0; i<msg.len; i++)
            cout << "0x" << setfill('0') << setw(2) << hex << (int)((msg.data+2)[i]) << " ";
        cout << dec << endl;
#endif

        pos = -1;

        // String completed
        HandleMessage(msg);
    }
}
*/

// --------------------------------------------------------------------------
//
// This is IcSendReqBCAN from the Janz software
//
//  /*
//   * IcSendReqBCAN - Send a CANbus message
//   *
//   * Issue request to send a CAN message. <Spec> controls whether to send with
//   * or without spec/confirmation.
//   * .CS
//   *    spec     action
//   *      0      send only
//   *      1      send with confirmation to the host.
//   *      2      send and echo message to the host.
//   *      3      send and generate both echo and confirmation.
//   * .CE
//   *
//   * SERVICE: CTXreq, CTXCreq, CTXEreq, CTXCEreq
//   *
//   * NOTE:
//   * Raw ICANOS version of the firmware only.
//   */
//
#ifdef DEBUG
#include <TStopwatch.h>
#endif
void Ethernet::SendCanFrame(WORD_t cobid, BYTE_t m[8], BYTE_t rtr)
{
    const WORD_t desc = MsgDescr(cobid, 8, rtr);

    Message msg;

//    msg.cmd = M_BCAN_TX_req;

    msg.len = 12;
//    msg.data[0]  = 0;
    msg.data[1]  = msg.len-2;
    msg.data[2]  = word_to_msb(desc);
    msg.data[3]  = word_to_lsb(desc);

    memcpy(&msg.data[4], m, 8);

    /*
    cout << "*** SendCanFrame len=" << dec << msg.len-2 << ": ";
    for (int i=0; i<msg.len-2; i++)
        cout << "0x" << setfill('0') << setw(2) << hex << (int)((msg.data+2)[i]) << " ";
    cout << endl;
    */

#ifdef DEBUG
    // FIXME: MUST BECOME NON-BLOCKING!!!!!
    cout << "*** Send CanFrame over IP " << endl;
    // FIXME: MUST BECOME NON-BLOCKING!!!!!
#endif

#ifdef DEBUG
    TStopwatch st;
    st.Start();
#endif
    MTcpIpO::SendFrame(fTxAddress, fTxPort, (char*)(msg.data+1), msg.len-1);
#ifdef DEBUG
    st.Print();
#endif
    //Send((char*)(msg.data+1), msg.len-1);

    /*
    const WORD_t desc = MsgDescr(cobid, 8, rtr);

    Message msg;

    msg.cmd = M_BCAN_TX_req;

    msg.len = 12;
    msg.data[0]  = 0;
    msg.data[1]  = 0;
    msg.data[2]  = word_to_msb(desc);
    msg.data[3]  = word_to_lsb(desc);

    memcpy(&msg.data[4], m, 8);

    while (!Send(&msg));*/
}
