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

#include <door.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <assert.h>
#include <unistd.h>

#include "DoorClient.h"
#include "VPtr.h"

using namespace NNabikiVPtr;

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

CDoorClient::CDoorClient() :
	DoorDescriptor( -1 )
{

}

CDoorClient::~CDoorClient()
{
	Close();
}

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

// Doorを開く
bool CDoorClient::Open( const char *pFnmae )
{
	assert( NULL != this && pFnmae );

	// 指定されたファイルを確認する
	if ( !CheckDoor( pFnmae ) ) return false;

	// 開く
	DoorDescriptor = open( pFnmae, O_RDWR );
	if ( DoorDescriptor < 0 ) return false;

	return true;
}

// Doorを呼び出す
VPtr< char > CDoorClient::Call( const CNegCmd *cmd, bool *pSrvDownFlg ) const
{
	string SendData;
	door_arg_t DoorArg;
	VPtr< char > pSendBuf;
	VPtr< char > pRetBuf;
	char RetBuf_Auto[256];
	int ByteSeqLen;

	assert( NULL != this && NULL != cmd );
	assert( DoorDescriptor >= 0 );
	if ( pSrvDownFlg ) (*pSrvDownFlg) = false;

	// doorが使用可能か否か確認する
	if ( !CheckDoor( DoorDescriptor ) ) {
		if ( pSrvDownFlg ) (*pSrvDownFlg) = true;
		return VPtr< char >( NULL );
	}

	// サーバに送信するデータを構築する
	SendData = cmd->GetSendData();

	if ( SendData.length() == 0 ) return VPtr< char >( NULL );

	// 作業用バッファを用意して、送信用データをコピーする
	if ( !pSendBuf.Malloc( sizeof( char ) * SendData.length() ) )
		return VPtr< char >( NULL );
	memcpy( pSendBuf.GetPtr(), SendData.c_str(), sizeof( char ) * SendData.length() );
	SendData = "";

	// DoorArg構造体に値を設定する。
	DoorArg.data_ptr = pSendBuf.GetPtr();
	DoorArg.data_size = pSendBuf.GetLen();
	DoorArg.desc_ptr = NULL;
	DoorArg.desc_num = 0;
	DoorArg.rbuf = RetBuf_Auto;
	DoorArg.rsize = sizeof( RetBuf_Auto ) * sizeof( char );

	// doorを呼び出す
	if ( door_call( DoorDescriptor, &DoorArg ) )
		return VPtr< char >( NULL );
	pSendBuf.Free();

	// 戻り値用のデータを取得
	if ( pRetBuf.Malloc( sizeof( char ) * DoorArg.rsize ) )
		memcpy( pRetBuf.GetPtr(), DoorArg.rbuf, sizeof( char ) * DoorArg.rsize );

	if ( DoorArg.rbuf != RetBuf_Auto )
		munmap( DoorArg.rbuf, DoorArg.rsize );

	return pRetBuf;
}

// 閉じる
void CDoorClient::Close()
{
	if ( DoorDescriptor > 0 )
		close( DoorDescriptor );
	DoorDescriptor = -1;
}

// doorを開いているか否か
bool CDoorClient::Enable() const
{
	return ( DoorDescriptor >= 0 );
}

// doorが使用可能か否か確認する
bool CDoorClient::CheckDoor( const char *pFnmae )
{
	struct stat wStat;
	int dd;
	bool r = false;

	assert( pFnmae );

	// ファイルの状態を確認する
	if ( lstat( pFnmae, &wStat ) )
		return false;	// ファイルがない

	// doorか否か
	if ( !wStat.st_mode & S_IFDOOR )
		return false;

	// doorファイルを開いてみる
	dd = open( pFnmae, O_RDWR );
	if ( dd < 0 ) return false;

	// 開いたdoorが使えるかどうか確認する
	r = CheckDoor( dd );

	close( dd );

	return r;
}

// 指定されたディスクリプタのdoorが使用可能か否か確認する
bool CDoorClient::CheckDoor( int dd )
{
	struct door_info info;

	// doorの情報を取得する
	if ( door_info( dd, &info ) )
		return false;

	// revokeで無ければdoorは利用可能である
	if ( info.di_attributes & DOOR_REVOKED )
		return false;

	return true;
}

