/* -*- c++ -*-
 *
 * mmpacket.cpp
 *
 * Copyright (C) 2003 Petter Stokke <ummo@hellokitty.com>
 *
 * 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., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "mmpacket.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>

#include <kdebug.h>
#include <qtextcodec.h>

QTextCodec* MMPacket::codec = 0;

void MMPacket::initCodec()
{
    if (!codec) {
	codec = QTextCodec::codecForName("UTF-8");
	if (!codec) codec = QTextCodec::codecForLocale();
    }
}

void MMPacket::setStringCodec(QTextCodec* newCodec)
{
    codec = newCodec;
}

MMPacket::MMPacket( int8 opcode ) : QMemArray<int8>()
{
    initCodec();
    op = opcode;
    pos = 0;
}

MMPacket::MMPacket( const char* data, int len ) : QMemArray<int8>()
{
    initCodec();
    resize(len - 1);
    op = data[0];
    char *p = (char*)data + 1;
    int i;
    for (i=0; i<(len-1); i++)
	(*this)[i] = p[i];
    pos = 0;
}

MMPacket::MMPacket(int8 opcode, int len) : QMemArray<int8>(len)
{
    initCodec();
    op = opcode;
    pos = 0;
}

int8 MMPacket::opcode() const
{
    return op;
}

void MMPacket::setOpcode(int8 opcode)
{
    op = opcode;
}

inline void MMPacket::writeInt(int32 v, int sz)
{
    int j;
    pos = size();
    resize(pos + sz);
    for(j = 0; j < sz; j++)
        (*this)[pos + j] = (v & (-1)) >> (8 * j);
    pos += sz;
}

void MMPacket::writeByte(int8 v)
{
    writeInt((int32)v, 1);
}

void MMPacket::writeShort(int16 v)
{
    writeInt((int32)v, 2);
}

void MMPacket::writeInt(int32 v)
{
    writeInt((int32)v, 4);
}

void MMPacket::writeString(const QString& v)
{
    QCString s = codec->fromUnicode(v);
    if (s.isNull()) {
	kdDebug() << "Unable to convert string into charset " << codec->name() << "." << endl;
	writeString("");
    } else
	writeString((const char*)s);
}

void MMPacket::writeString(const char* v)
{
    int i,l = strlen(v);
    assert(l < 256);
    pos = size();
    writeByte(l);
    resize(pos + l);
    for (i=0; i<l; i++)
	(*this)[pos++] = v[i];
}

void MMPacket::writeByteArray(const QByteArray& v)
{
    int i,l = (int)v.size();
    assert(l < 256);
    writeByte((int8)l);
    resize(pos + l);
    for (i=0; i<l; i++)
	(*this)[pos++] = v[i];
}

void MMPacket::feedBuffer(const char* buf, int sz)
{
    memcpy(data() + pos, buf, sz);
    pos += sz;
}

void MMPacket::resetPosition()
{
    pos = 0;
}

inline int32 MMPacket::readInt(int sz)
{
    if ((uint)(pos + sz) > count()) {
	QString foo(kdBacktrace());
	QString msg(dumpArray());
	kdDebug() << "Position " << pos + sz << " exceeds buffer size " << count() << "\nMessage: " << msg << "\nBT: '" << foo << "'" << endl;
	kdFatal() << "Invalid index access.";
    }
    int i;
    int32 res = 0;
    for(i = 0; i < sz; i++)
        res += ((*this)[pos + i] & 0xFF) << (8 * i);
    pos += sz;
    return res;
}

int8 MMPacket::readByte()
{
    return (int8)readInt(1);
}

int16 MMPacket::readShort()
{
    return (int16)readInt(2);
}

int32 MMPacket::readInt()
{
    return (int32)readInt(4);
}

QString MMPacket::readString()
{
    return codec->toUnicode(readByteArray());
}

QByteArray MMPacket::readByteArray()
{
    int sz = (int)readByte();
    if ((uint)pos + sz > count()) {
	QString foo(kdBacktrace());
	QString msg(dumpArray());
	kdDebug() << "Position " << pos + sz << " exceeds buffer size " << count() << "\nMessage: " << msg << "\nBT: '" << foo << "'" << endl;
	kdFatal() << "Invalid index access.";
    }
    QByteArray buf(sz);
    memcpy(buf.data(), *this + pos, sz);
    pos += sz;
    return buf;
}

QString MMPacket::dumpArray() const
{
    QString out = "Opcode " + QString::number(op) + ", size " + QString::number(size()) + "\n";

    int i;
    QString hex = "", asc = "", tmp;
    for (i=0; i<(int)size(); i++) {
	if (at(i) >= 32 && at(i) <= 127)
	    asc += QChar(at(i));
	else
	    asc += ".";
	tmp.sprintf("%02x", at(i));
	hex += tmp + " ";
	if (i % 16 == 15) {
	    tmp.sprintf("%8d: ", i - 15);
	    out += tmp + hex + "  " + asc + "\n";
	    hex = "";
	    asc = "";
	}
    }
    tmp.sprintf("%8d: ", i - (i % 16));
    for (i %= 16; i < 16; i++)
	hex += "   ";
    out += tmp + hex + "  " + asc + "\n";
    return out;
}

uint MMPacket::packetSize() const
{
    return size() + 3;
}

bool MMPacket::specialHeader() const
{
    return m_specialHeader;
}

void MMPacket::setSpecialHeader(bool sh)
{
    m_specialHeader = sh;
}

