#ifndef StorageObjectTemplate_h
#define StorageObjectTemplate_h



#include"../../Setup/CompileMode.h"
#include"../../Auxiliary/Debug/CAssert.h"
#include"../../Auxiliary/CString.h"
#include"../../Template/GlobalPointer.h"

#include"CStorageCore.h"

namespace Maid
{
	//	ファイルの読み込みの補助クラス

	template<int FunctionNo, class OPTION>
	class StorageObjectTemplate : public GlobalPointer<CStorageCore>
	{
	private:
		struct STORAGEINFO
		{
			int		ReferenceCount;
			int		TicketID;
			bool	IsLoadEnd;	//	読み込みが完了したか？
		};

		typedef MySTL::map<mstring,STORAGEINFO> STORAGEMAP;
		static STORAGEMAP	s_Storage;

		typedef MySTL::map<mstring,OPTION> OPTIONMAP;
		static OPTIONMAP	s_Option;

		enum STATE
		{
			STATE_EMPTY,	//	まだ何もしてない
			STATE_LOADING,	//	読み込み中
			STATE_WORKING,	//	稼動中
		};

		STATE	m_State;
		mstring	m_FileName;

		mstring CreateID( const mstring& name ) const
		{
			//	↓の作業、ここでやるべきではないかも
			//	Linux だと大文字小文字区別するからね
			const mstring id = CString::ToLower(name);
			return id;
		}

	protected:
		virtual void LoadedDelete( const mstring& FileName )=0;
		virtual void LoadEnd( const SPSTORAGEFUNCTION_RETURNPARAM& param, OPTION& option  )=0;
		virtual void LoadEnd( const OPTION& option  ){}


	public:
		const OPTION& GetOption() const
		{
			OPTIONMAP::const_iterator ite = s_Option.find(CreateID(m_FileName));
			MAID_ASSERT( ite==s_Option.end(), "これはありえない" );

			return ite->second;
		}

		void Load( const mstring& FileName )
		{
			Delete();

			const mstring id = CreateID(FileName);
			STORAGEMAP::iterator ite = s_Storage.find(id);


			if( ite==s_Storage.end() )
			{	//	もし初めての読み込みだったら、メッセージを発行
				const int rettiket =  GlobalPointer<CStorageCore>::Get()->Execute( FunctionNo, FileName, SPSTORAGEFUNCTION_FUNCTIONPARAM() );

				STORAGEINFO info;

				info.IsLoadEnd = false;
				info.ReferenceCount = 1;
				info.TicketID = rettiket;

				s_Storage[id] = info;
			}
			else
			{
				//	２回目以降はキャッシュのみ
				ite->second.ReferenceCount += 1;
			}

			m_FileName = FileName;
			m_State = STATE_LOADING;

			//	とりあえず調べておく
			IsNowLoading();
		}

		void Delete()
		{
			if( m_State==STATE_EMPTY ) { return ; }
			
			{
				//	Load() を呼んでからm_State==STATE_LOADING のまま
				//	ReferenceCountが０になると読みこみの途中で動作終了しないといけないので
				//	読み込みが終わっていたら更新してから破棄するようにする
				IsNowLoading();
			}

			STORAGEMAP::iterator ite = s_Storage.find(CreateID(m_FileName));
			MAID_ASSERT( ite==s_Storage.end(), "これはありえない" );
			MAID_ASSERT( ite->second.ReferenceCount==0, "これもありえない" );

			STORAGEINFO& info = ite->second;
			info.ReferenceCount -= 1;

			if( info.ReferenceCount==0 )
			{
				if( info.IsLoadEnd )
				{
					LoadedDelete( m_FileName );
					s_Option.erase( s_Option.find(CreateID(m_FileName)) );
				}else
				{
					GlobalPointer<CStorageCore>::Get()->Cancel( info.TicketID );
				}
				s_Storage.erase( ite );
			}

			m_FileName.clear();
			m_State = STATE_EMPTY;
		}

	public:
		mstring GetFileName() const { return m_FileName; }

		bool IsNowLoading()
		{
			MAID_ASSERT( m_State==STATE_EMPTY, "読み込みをしていない" );

			if( m_State==STATE_WORKING ) { return false; }

			STORAGEMAP::iterator ite = s_Storage.find(CreateID(m_FileName));
			MAID_ASSERT( ite==s_Storage.end(), "これはありえない" );
			MAID_ASSERT( ite->second.ReferenceCount==0, "これもありえない" );

			STORAGEINFO& info = ite->second;

			//	とりあえず調べる
			if( info.IsLoadEnd )
			{
				m_State = STATE_WORKING;
				LoadEnd( s_Option[CreateID(m_FileName)] );
				return false; 
			}

			//	まだなら読み込みスレッドに問い合わせる

			CStorageCore* pStorage = GlobalPointer<CStorageCore>::Get();

			if( pStorage->IsNowLoading( info.TicketID ) ) { return true; }

			//	ここまできたら終わっているので更新作業

			SPSTORAGEFUNCTION_RETURNPARAM pParam = pStorage->GetReturnParam( info.TicketID );
			
			info.IsLoadEnd = true;	//	読み込み終了！
			m_State = STATE_WORKING;

			LoadEnd( pParam, s_Option[CreateID(m_FileName)] );

			return false;
		}

		StorageObjectTemplate()
			:m_State(STATE_EMPTY)
		{
		}

		StorageObjectTemplate( const StorageObjectTemplate<FunctionNo,OPTION>& rha )
			:m_State(STATE_EMPTY)
		{
			if( rha.m_State == STATE_EMPTY ) 
			{
				m_State = STATE_EMPTY;
				return ; 
			}

			STORAGEMAP::iterator ite = s_Storage.find(CreateID(rha.m_FileName));
			MAID_ASSERT( ite==s_Storage.end(), "これはありえない" );
			MAID_ASSERT( ite->second.ReferenceCount==0, "これもありえない" );

			ite->second.ReferenceCount += 1;

			m_FileName = rha.m_FileName;
			m_State = rha.m_State;
		}
		virtual ~StorageObjectTemplate()
		{
		}
	};
}


#endif