////////////////////////////////////////////////////////////////////////////
// VPtr.h
// 仮想的なポインタを提供する
//

#if !defined( _VPTR_H_B0FFA0FA_A5B9_4A44_B7AF_3BAB6DD2FC3C_INCLUDED_ )
#define _VPTR_H_B0FFA0FA_A5B9_4A44_B7AF_3BAB6DD2FC3C_INCLUDED_

#include <stdlib.h>
#include <malloc.h>
#include <assert.h>

namespace NNabikiVPtr {

template < class _T >
class VPtr
{
public:
	// 作業用領域の形式
	struct tagWDATA {
		_T *ptr;	// ポインタ
		unsigned long len;	// 領域長
		unsigned long cnt;	// 参照カウンタ
	} *pData;	// 値が記憶されたのアドレスを保持

public:
	// コンストラクタ
	// 値をクリア
	VPtr() : pData( NULL ){};

	// NULL値
	explicit VPtr( void *p ) : pData( NULL )
	{ assert( NULL == p ); };

	// コピーコンストラクタ
	VPtr( const VPtr& r ) : pData( NULL ){
		(*this) = r;
	};

	// デストラクタ
	virtual ~VPtr() {
		Free();
	};

	// 代入演算子
	virtual const VPtr& operator =( const VPtr& r ){
		assert( NULL != this );

		if ( NULL != pData ) {
			// 今まで参照していた領域の参照カウンタを減算する
			Free();
			pData = NULL;
		}

		// ポインタをコピー
		pData = r.pData;

		// 参照カウンタを加算
		if ( pData ){
			(pData->cnt)++;
		}

		return *this;
	};

	// 等号
	bool operator ==( const VPtr& r ) const {
		return pData == r.pData;
	};

	// キャスト
/*	operator _T*(){
		if ( pData ) return pData->ptr;
		return NULL;
	};
	operator const _T*() const{
		if ( pData ) return pData->ptr;
		return NULL;
	};*/

	// 参照
	virtual _T& operator []( unsigned long i ) {
		assert( NULL != this && NULL != pData );
		assert( i >= 0 && i < pData->len );
		return pData->ptr[ i ];
	};
	virtual const _T& operator []( unsigned long i ) const {
		assert( NULL != this && NULL != pData );
		assert( i >= 0 && i < pData->len );
		return pData->ptr[ i ];
	};

	// bool型へのキャスト
	operator bool(){
		return ( pData ? true : false );
	};
	operator !(){
		return !( operator bool() );
	};

	// メンバアクセス
	_T* operator ->(){
		assert( NULL != pData );
		return pData->ptr;
	};

	// 値の取得
	_T* GetPtr() {
		if ( pData ) return pData->ptr;
		return NULL;
	};
	const _T* GetPtr() const {
		if ( pData ) return pData->ptr;
		return NULL;
	};

	// 領域長の取得
	unsigned long GetLen() const{
		if ( pData ) return pData->len;
		return 0;
	};

	// 参照カウンタ数を取得
	unsigned long GetCnt() const{
		if ( pData ) return pData->cnt;
		return 0;
	};

	// 領域が確保されているかどうかを取得（確保されていなければ真を返す）
	bool IsFree() const {
		return NULL == pData;
	};

	// 領域確保
	// 引数：確保する要素数
	// 戻り値：成功したら真
	bool Malloc( unsigned long size ){

		// すでに領域を確保している場合は、失敗
		if ( NULL != pData ) return false;

		// 作業用領域を確保
		pData = (tagWDATA*)malloc( sizeof( tagWDATA ) );
		if ( NULL == pData ) return false;

		pData->ptr = (_T*)malloc( sizeof( _T ) * size );
		if ( NULL == pData->ptr ){
			free( pData );
			pData = NULL;
			return false;
		}

		// 領域長・参照カウンタを設定
		pData->cnt = 1;
		pData->len = size;
		return true;
	};

	// 再割り当て
	bool Realloc( unsigned long size ){
		_T *wp = NULL;	// 作業用ポインタ

		// 領域が確保されていない場合は失敗
		if ( NULL == pData ) return false;

		// サイズが０の場合はFreeを呼ぶ
		if ( 0 == size ){
			Free();
			return true;
		}

		// 領域長が同じならば何もしない
		if ( size == pData->len ) return true;

		// 再割り当てを行う
		wp = (_T*)realloc( pData->ptr, sizeof( _T ) * size );
		if ( NULL == wp ) return false;

		pData->ptr = wp;
		pData->len = size;
		return true;
	};

	// 領域開放
	virtual void Free(){
		// 領域を確保していなければ何もしない
		if ( NULL == pData ) return ;

		// 参照カウンタを減算
		(pData->cnt)--;

		// 参照カウンタが0になったら領域を開放
		if ( 0 == pData->cnt ){
			free( pData->ptr );
			free( pData );
		}

		// 自分が保持しているポインタは破棄する
		pData = NULL;
	};
};

template < class _T >
class MVPtr : public VPtr< _T >
{
protected:
	struct tagWDATA2 : public VPtr< _T >::tagWDATA {
		unsigned long ulen;	// 使用領域長
	} *pData2;

public:
	// コンストラクタ　確認終了
	MVPtr() : VPtr< _T >(), pData2( NULL ){};

	explicit MVPtr( void *p ) : VPtr< _T >( NULL ), pData2( NULL )
	{ assert( NULL == p ); };

	MVPtr( const MVPtr& r ) : VPtr< _T >( r ){
		pData2 = r.pData2;
	};

	// デストラクタ
	virtual ~MVPtr() {
		Free();
	};

	// 代入演算子
	const MVPtr& operator =( const MVPtr& r ){
		assert( NULL != this );
		pData2 = r.pData2;
		VPtr< _T >::operator =( r );
		return (*this);
	};

	// 参照　確認終了
	virtual _T& operator []( unsigned long i ) {
		assert( NULL != this && NULL != pData2 );
		assert( i < pData2->ulen );
		return VPtr< _T >::operator []( i );
	};
	virtual const _T& operator []( unsigned long i ) const {
		assert( NULL != this && NULL != pData2 );
		assert( i < pData2->ulen );
		return VPtr< _T >::operator []( i );
	};
	// 領域開放
	virtual void Free(){
		assert( NULL != this );
		VPtr< _T >::Free();
		pData2 = NULL;
	};

	// 領域確保　確認終了
	virtual bool Malloc( unsigned long size ){

		// すでに領域を確保している場合は、失敗
		if ( NULL != pData2 ) return false;

		// 作業用領域を確保
		pData2 = (tagWDATA2*)malloc( sizeof( tagWDATA2 ) );
		pData = static_cast< tagWDATA* >( pData2 );
		if ( NULL == pData2 ) return false;

		pData2->ptr = (_T*)malloc( sizeof( _T ) * size );
		if ( NULL == pData2->ptr ){
			free( pData2 );
			pData2 = NULL;
			pData = NULL;
			return false;
		}

		// 領域長・参照カウンタを設定
		pData2->cnt = 1;
		pData2->len = size;
		pData2->ulen = 0;
		return true;
	};

	// 配列の末尾にデータを挿入する
	// 確認終了
	bool PushBack( _T& r ){
		int idx;
		assert ( NULL != this );
		if ( NULL == pData2 ){
			// 領域が確保されていない場合
			if ( !Malloc( 1 ) ) return false;
		}
		if ( pData2->ulen >= pData2->len ) {
			// 領域が不足するため拡張する
			if ( !Realloc( pData2->len * 2 ) ) {
				return false;
			}
		}
		idx = pData2->ulen;
		pData2->ulen++;	// 先に使用領域長を加算しないと、配列要素に代入できない。
		operator []( idx ) = r;
		return true;
	};

	// 使用長を０にする
	void Clear() {
		assert( NULL != this );
		if ( pData2 ) pData2->ulen = 0;
	};

	// 配列の使用長を取得する　確認終了
	unsigned long GetUsedLen() const {
		assert( NULL != this );
		if ( pData2 )
			return pData2->ulen;
		else
			return 0;
	};
};

}; // NNabikiVPtr

#endif // _VPTR_H_B0FFA0FA_A5B9_4A44_B7AF_3BAB6DD2FC3C_INCLUDED_
