#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <list>
#include "xim.h"

//
// Routines for byte manipulating
//
int
rup4(int l)
{
    if ((l%4)==0) {
        return l;
    }
    return (l&0xffffffc)+4;
}

void
writeC8(C8 val, int byte_order, unsigned char *buf)
{
    buf[0] = val;
}

void writeC16(C16 val,int byte_order,unsigned char *buf)
{
    if ( byte_order == LSB_FIRST ){
        buf[0] = val & 255;
        buf[1] = (val >> 8) & 255;
    }else{
        buf[1] = val & 255;
        buf[0] = (val >> 8) & 255;
    }
}

void writeC32(unsigned int val,int byte_order,unsigned char *buf)
{
    if ( byte_order == LSB_FIRST ){
        buf[0] = val & 255;
        buf[1] = (val >> 8) & 255;
        buf[2] = (val >> 16) & 255;
        buf[3] = (val >> 24) & 255;
    }else{
        buf[3] = val & 255;
        buf[2] = (val >> 8) & 255;
        buf[1] = (val >> 16) & 255;
        buf[0] = (val >> 24) & 255;
    }
}

C8 readC8(unsigned char *buf)
{
    return buf[0];
}

C16 readC16(unsigned char *buf,int byte_order)
{
    C16 v;
    if ( byte_order == LSB_FIRST ){
        v = buf[0] + buf[1]*256;
    }else{
        v = buf[1] + buf[0]*256;
    }
    return v;
}

C32 readC32(unsigned char *buf,int byte_order)
{
    C32 v;
    if ( byte_order == LSB_FIRST ){
        v = buf[0] + buf[1]*256 + (buf[2]<<16) +(buf[3]<<24);
    }else{
        v = buf[3] + buf[2]*256 + (buf[1]<<16) +(buf[0]<<24);
    }
    return v;
}

//
// TxPacket
//

class TxElement{
public:
    virtual ~TxElement(){};
    virtual int get_size()=0;
    virtual int write_to_buf(unsigned char *buf,int byte_order)=0;
};

class TxC8 : public TxElement{
public:
    TxC8(int v){
        val = v;
    }
    int get_size()
    {
        return 1;
    }
    int write_to_buf(unsigned char *buf,int bo)
    {
        writeC8(val,bo,buf);
        return 1;
    }
private:
    C8 val;
};

class TxC16 : public TxElement{
public:
    TxC16(int v){
        val = v;
    }
    int get_size()
    {
        return 2;
    }
    int write_to_buf(unsigned char *buf,int bo)
    {
        writeC16(val,bo,buf);
        return 2;
    }
private:
    C16 val;
};

class TxC32 : public TxElement{
public:
    TxC32(unsigned int v){
        val = v;
    }
    int get_size()
    {
        return 4;
    }
    int write_to_buf(unsigned char *buf,int bo)
    {
        writeC32(val,bo,buf);
        return 4;
    }
private:
    C32 val;
};

class TxString : public TxElement{
public:
    TxString(char *s){
        init(s,strlen(s));
    }
    TxString(char *s,int len){
        init(s,len);
    }
    ~TxString(){
        free(m_str);
    }
    int get_size(){
        return 2+m_len+pad4(2+m_len);
    }
    int write_to_buf(unsigned char *buf,int bo){
        writeC16(m_len,bo,buf);
        memcpy(&buf[2],m_str,m_len);
        return get_size();
    }
private:
    void init(char *s,int len)
    {
        m_len = len;
        m_str = (char *)malloc(len+1);
        strncpy(m_str,s,len);
    }
    int m_len;
    char *m_str;
};

class TxBytes : public TxElement{
public:
    TxBytes(char *s,int len)
    {
        m_str = (char *)malloc(len);
        m_len = len;
        memcpy(m_str,s,len);
    };
    ~TxBytes()
    {
        free(m_str);
    }
    int get_size()
    {
        return m_len;
    }
    int write_to_buf(unsigned char *buf,int bo){
        memcpy(buf,m_str,m_len);
        return get_size();
    }
private:
    int m_len;
    char *m_str;
};

class TxPacket_impl : public TxPacket{
public:
    TxPacket_impl(int major,int minor);
    ~TxPacket_impl();

    int get_length();
    int write_to_buf(unsigned char *buf,int buflen,int byte_order);

    void dump(int byte_order);
    int get_major();

    int pushC8(unsigned int);
    int pushC16(unsigned int );
    int pushC32(unsigned int );
    int pushSTRING(char *);
    int pushBytes(char *,int );

    int pop_back();
private:
    void write_header(unsigned char *buf,int l,int byte_order);
    int m_major,m_minor;
    std::list <TxElement *> m_elms;
};

TxPacket_impl::TxPacket_impl(int major,int minor)
{
    m_major = major;
    m_minor = minor;
}

TxPacket_impl::~TxPacket_impl()
{
    std::list<TxElement *>::iterator i;
    for ( i = m_elms.begin() ; i != m_elms.end() ; i++){
        delete *i;
    }
}

int TxPacket_impl::get_length()
{
    std::list<TxElement *>::iterator i;
    int l;
    l = 4;
    for ( i = m_elms.begin() ; i != m_elms.end() ; i++){
        l +=  (*i)->get_size();
    }  
    return l;
}

int TxPacket_impl::write_to_buf(unsigned char *buf,int buflen,int byte_order)
{
    std::list<TxElement *>::iterator i;
    int l,m;
    l = 4;
    for ( i = m_elms.begin() ; i != m_elms.end() ; i++){
        m = (*i)->get_size();
        if ( l + m > buflen ){
            return 0;
        }
        m = (*i)->write_to_buf(&buf[l],byte_order);
        l +=m;
    }
    l = rup4(l);
    write_header(buf,l,byte_order);
    return l;
}

int TxPacket_impl::pushC8(unsigned int v)
{
    TxElement *e;
    e = new TxC8(v);
    m_elms.push_back(e);
    return e->get_size();
}

int TxPacket_impl::pushC16(unsigned int v)
{
    TxElement *e;
    e = new TxC16(v);
    m_elms.push_back(e);
    return e->get_size();
}

int TxPacket_impl::pushC32(unsigned int v)
{
    TxElement *e;
    e = new TxC32(v);
    m_elms.push_back(e);
    return e->get_size();
}

int TxPacket_impl::pushSTRING(char *s)
{
    TxElement *e;
    e = new TxString(s);
    m_elms.push_back(e);
    return e->get_size();
}

int TxPacket_impl::pushBytes(char *b,int len)
{
    TxElement *e;
    e = new TxBytes(b,len);
    m_elms.push_back(e);
    return e->get_size();
}

int TxPacket_impl::pop_back()
{
    int len;
    TxElement *e;
    e = m_elms.back();
    len = e->get_size();
    delete e;
    m_elms.pop_back();
    return len;
}

void TxPacket_impl::write_header(unsigned char *buf,int l,int byte_order)
{
    buf[0] = m_major;
    buf[1] = m_minor;
    writeC16(l/4-1,byte_order,&buf[2]);
}

void TxPacket_impl::dump(int byte_order)
{
    unsigned char *buf;
    int len;
    len = get_length();
    buf = (unsigned char *)malloc(len);
    write_to_buf(buf,len,byte_order);
    hex_dump(buf,len);
    free(buf);
}

int TxPacket_impl::get_major()
{
    return m_major;
}

TxPacket *createTxPacket(int major,int minor)
{
    return new TxPacket_impl(major,minor);
}
//
// Routines for RxPacket
//

class RxPacket_impl : public RxPacket{
public:
    RxPacket_impl(unsigned char *buf,int byte_order);

    void rewind();
    C8 getC8();
    C16 getC16();
    C32 getC32();
    int getStrLen();
    void getStr(char *buf);
    int getStr8Len();
    void getStr8(char *);

    int getMajor();

    void dump();
    bool isOverRun(){
	if ( (g_option_mask & OPT_TRACE) && mIsOverRun ){
	    printf("RxPacket Overrun.\n");
	}
	return mIsOverRun;
    };
private:
    bool canRead(int );
    int mLen;
    unsigned char *mBuf;
    int mIndex;
    int mByteOrder;
    bool mIsOverRun;
};

RxPacket_impl::RxPacket_impl(unsigned char *b,int byte_order)
{
    mLen = getPacketLength(b,byte_order);
    mBuf = (unsigned char *)malloc(mLen);
    memcpy(mBuf,b,mLen);
    mByteOrder = byte_order;
    rewind();
}

void RxPacket_impl::rewind()
{
    mIndex = 4;
    mIsOverRun = false;
}

C8 RxPacket_impl::getC8()
{
    C8 v;
    if ( !canRead(1)){
	mIsOverRun = true;
	return 0;
    }
    v = readC8(&mBuf[mIndex]);
    mIndex += 1;
    return v;
}

C16 RxPacket_impl::getC16()
{
    C16 v;
    if ( !canRead(2)){
	mIsOverRun = true;
	return 0;
    }
    v = readC16(&mBuf[mIndex], mByteOrder);
    mIndex += 2;
    return v;
}

C32 RxPacket_impl::getC32()
{
    C32 v;
    if ( !canRead(4)){
	mIsOverRun = true;
	return 0;
    }
    v = readC32(&mBuf[mIndex], mByteOrder);
    mIndex += 4;
    return v;
}

int RxPacket_impl::getStrLen()
{
    if ( !canRead(2)){
	mIsOverRun = true;
	return 0;
    }
    return readC16(&mBuf[mIndex],mByteOrder);
}

void RxPacket_impl::getStr(char *buf)
{
    int l;
    l = getStrLen();
    if ( !canRead(l+2)){
	mIsOverRun = true;
	return ;
    }
    memcpy(buf,&mBuf[mIndex+2],l);
    mIndex += (2 + l + pad4(2+l));
}

int RxPacket_impl::getStr8Len()
{
    if ( !canRead(1)){
	mIsOverRun = true;
	return 0;
    }
    return readC8(&mBuf[mIndex]);
}

void RxPacket_impl::getStr8(char *buf)
{
    int l;
    l = getStr8Len();
    if ( !canRead(l+1)){
	mIsOverRun = true;
	return ;
    }
    memcpy(buf,&mBuf[mIndex+1],l);
    mIndex += (1+l);
}

int RxPacket_impl::getMajor()
{
    return mBuf[0];
}

void RxPacket_impl::dump()
{
    hex_dump(mBuf,mLen);
}

bool RxPacket_impl::canRead(int s)
{
    if ( mIndex +s > mLen ){
	return false;
    }
    return true;
}

// static methods
int RxPacket::getPacketLength(unsigned char *buf, int byte_order)
{
    if (byte_order == BYTEORDER_UNKNOWN) {
        return 0;
    }
    return readC16(&buf[2], byte_order)*4+4;
}

RxPacket *createRxPacket(unsigned char *buf,int byte_order)
{
    return new RxPacket_impl(buf,byte_order);
}
/*
 * Local variables:
 *  c-indent-level: 4
 *  c-basic-offset: 4
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1