////////////////////////////////////////////////////////////////////////////
// CNegCmd クラスのインプリメンテーション
//
////////////////////////////////////////////////////////////////////////////

#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>

#include "NegCmd.h"

////////////////////////////////////////////////////////////////////////////
// 構築/破棄
////////////////////////////////////////////////////////////////////////////

CNegCmd::CNegCmd( const char *pStr, int len )
{
	int DataLen;
	if ( len < 4 ) return ;

	// 先頭4バイトにはデータ長が記録されている
	memcpy( &DataLen, pStr, sizeof( int ) );
	pStr += sizeof( int ) / sizeof ( char );

	// 実際のデータは与えられたバッファより短い場合がある
	if ( DataLen < len ) len = DataLen;

	Recognize( pStr, len );
}

// ディスクリプタからデータを取得する
CNegCmd::CNegCmd( int fd )
{
	char Buf[512];
	const int BufLen = sizeof( Buf ) / sizeof( Buf[0] );
	int len;
	int DataLength;
	string wstr;

	// データ長を取得する
	DataLength = 0;
	len = read( fd, &DataLength, sizeof( int ) );
	if ( len < 0 ) return ;

	// データを取得する
	len = 0;
	wstr = "";
	while ( len < DataLength ) {
		int r = read( fd, Buf, BufLen );
		if ( r < 0 ) break;
		len += r;
		wstr += string( Buf, r );
	}

	// 与えられたバイト列を解釈する
	Recognize( wstr.c_str(), wstr.length() );
}

CNegCmd::CNegCmd()
{
	mapCmd.clear();
}

CNegCmd::CNegCmd( const CNegCmd& r ) :
	mapCmd( r.mapCmd )
{
}

CNegCmd::~CNegCmd()
{

}

////////////////////////////////////////////////////////////////////////////
// メソッド
////////////////////////////////////////////////////////////////////////////

// データを解析する
bool CNegCmd::Recognize( const char *pStr, int len )
{
	const char *wp = pStr;
	int ID;
	int DataLen;
	string data;
	bool r = true;

	mapCmd.clear();

	while ( len > 0 ) {
		// IDを取得
		if ( !GetInt( wp, len, &ID ) ) goto ERR_EXIT;
		len -= sizeof( int );
		wp += sizeof( int );
		// データ長を取得
		if ( !GetInt( wp, len, &DataLen ) ) goto ERR_EXIT;
		len -= sizeof( int );
		wp += sizeof( int );
		// データ長を確認
		if ( len < DataLen ) goto ERR_EXIT;
		// mapに追加
		AddData( ID, wp, DataLen );
		len -= DataLen;
		wp += DataLen;
	}
	r = true;

	return r;

ERR_EXIT:
	mapCmd.clear();
	return false;
}

// 4バイトの数値データを取得
bool CNegCmd::GetInt( const char *pStr, int len, int *val )
{
	int i;

	assert( this && val && pStr );
	if ( len < sizeof( int ) ) return false;

	memcpy( val, pStr, sizeof( int ) );
	return true;
}

// 整数値を取得
int CNegCmd::GetData( int ID, int def ) const
{
	map< int, string >::const_iterator itr = mapCmd.find( ID );
	if ( itr != mapCmd.end() )
		return atoi( itr->second.c_str() );
	else
		return def;
}

// 文字列値を取得
const string& CNegCmd::GetData( int ID, const string& def ) const
{
	map< int, string >::const_iterator itr = mapCmd.find( ID );
	if ( itr != mapCmd.end() )
		return itr->second;
	else
		return def;
}

// 指定されたIDのデータが存在するか否か
bool CNegCmd::HasData( int ID ) const
{
	map< int, string >::const_iterator itr = mapCmd.find( ID );
	return ( itr != mapCmd.end() );
}

// 値を追加
void CNegCmd::AddData( int ID, const void *pData, int DataLen )
{
	// IDのデータがすでに登録されていた場合は、そのデータを削除する
	map< int, string >::iterator itr = mapCmd.find( ID );
	if ( itr != mapCmd.end() ) mapCmd.erase( itr );

	// 追加する
	string wstr( reinterpret_cast< const char* >( pData ), DataLen );
	mapCmd.insert( pair< int, string >( ID, wstr ) );
}
void CNegCmd::AddData( int ID, const char *pData )
{
	AddData( ID, pData, strlen( pData ) );
}
void CNegCmd::AddData( int ID, const string& rData )
{
	AddData( ID, rData.c_str(), rData.length() );
}
void CNegCmd::AddData( int ID, int d )
{
	char buf[30];
	int len;

	snprintf( buf, 30, "%d", d );
	len = strlen( buf );
	AddData( ID, buf, sizeof( char ) * len );
}

// 送信用文字列を取得
string CNegCmd::GetSendData() const
{
	char buf[4];
	int i;
	map< int, string >::const_iterator itr;
	string r;

	// データを連結する
	r = "";
	for ( itr = mapCmd.begin(); itr != mapCmd.end(); itr++ ) {
		i = itr->first;
		memcpy( buf, &i, sizeof( int ) );
		r += string( buf, sizeof( int ) );
		i = itr->second.length();
		memcpy( buf, &i, sizeof( int ) );
		r += string( buf, sizeof( int ) );
		r += itr->second;
	}

	// 先頭にデータ長を連結する
	i = r.length();
	memcpy( buf, &i, sizeof( int ) );
	r = string( buf, sizeof( int ) ) + r;

	return r;
}

// 代入
const CNegCmd& CNegCmd::operator =( const CNegCmd &r )
{
	mapCmd = r.mapCmd;
	return (*this);
}

// 初期化
void CNegCmd::Initialize()
{
	mapCmd.clear();
}

