
/*!
@auther d金魚
@since 2003/11/15
*/

#ifndef _dkutilDLLManager__h_
#define _dkutilDLLManager__h_


#include "dkutilMap.h"
#include "dKingyoMacro.h"
#include "dkutilGetParam.h"
#include "dkutilBuffer.h"
#include "dkutilString.h"

namespace dkutil{




class DLLManager{
public:
	/*
	typedef std::map<std::string,void *> CONTAINER_TYPE;
	typedef std::pair<std::string,void *> DATA_TYPE;
	typedef std::pair<CONTAINER_TYPE::iterator,bool> RESULT;
	typedef std::pair<bool,void *> GET_DATA;
	typedef CONTAINER_TYPE::iterator iterator;
	*/
	typedef map_ex<std::string,void *> CONTAINER_TYPE;
	typedef CONTAINER_TYPE::DATA_TYPE DATA_TYPE;
	typedef CONTAINER_TYPE::RESULT RESULT;
	
	//typedef CONTAINER_TYPE::GET_DATA GET_DATA;
	typedef CONTAINER_TYPE::iterator iterator;

private:
	CONTAINER_TYPE mMap;
	HMODULE mhLib;
	std::string mDLLName;

	bool LoadDLL(const char *filename){
		if(mhLib){
			if(false==UnloadDLL()){
				throw std::runtime_error("ありえないエラー");
			}
		}
		if(!filename) return false;
		mhLib = LoadLibrary(filename);
		if(mhLib==NULL) return false;
		return true;
	}
	bool UnloadDLL(){
		if(mhLib){
			FreeLibrary(mhLib);
			mhLib=NULL;
			return true;
		}
		return false;
	}
	void *GetAddress(const char *name){
		if(!name || mhLib==NULL) return NULL;
		void *ptr=NULL;

		ptr = (void *)::GetProcAddress(mhLib,name);


		if(ptr==NULL) return NULL;
		RESULT r;
		DATA_TYPE data(name,ptr);
  	r = mMap.insert(data);
		if(r.second == false) return NULL;
		return ptr;
	}

public:
	/*!
	@param dllname[in] dllの名前へのポインタ
	@return trueだとロード完了
	*/
	bool reset(const char *dllname){
		mMap.clear();
		mhLib=NULL;
		mDLLName.clear();
		if(!dllname){ return false;}
		if(false==LoadDLL(dllname)){ return false;}
		mDLLName = dllname;
		return true;
	}
	const char *name()const{return mDLLName.c_str();}
	bool empty()const{	return mDLLName.empty();}
		
		
	DLLManager(const char *dllname=NULL){
		if(dllname){
			reset(dllname);
		}
	}
	virtual ~DLLManager(){
		UnloadDLL();
	}
	/*!
	@param key[in] DLL内から探したい関数の名前
	@return NULLだとエラー
	*/
	void *find(const char *key){
		iterator it = mMap.find(key);
		if(it!=mMap.end()){
			return (*it).second;
		}
		return GetAddress(key);
	}
	///@return trueだったら関数のロード完了。
	bool load_function(const char *function_name){
		return (NULL != find(function_name));
	}
	struct DLLVersion{
		int v1,v2,v3,v4;
	};
	bool GetDLLFileVersion(DLLVersion *dll)const{
		GetVersionInfo v;
		char buff[1024]="";
		if(dll==NULL || empty()) return false;

		if(false==v.GetInfo(name(),buff,sizeof(buff),"FILEVERSION"))
		{
			return false;
		}
		VS_FIXEDFILEINFO viif={0};
		v.GetFileInfo(name(),&viif);
		size_t len = strlen(buff);
		{
			//バージョン文字列をパーシング
			const char bn = 4;
			char b[bn][32];
			{for(int i=0;i<bn;i++){
				memset(b[i],0,sizeof(b[i]));
			}}

			int co = 0;

			offset_pusher<char> p(b[co],sizeof(b[co]) - 1);
			for(size_t i=0;i<len;i++)
			{
				if(buff[i] == ',' || buff[i] == '.')
				{
					co ++;
					// 第二引数が　-1　なのは、NULL文字考慮のため
					p.reset(b[co],sizeof(b[co]) - 1);
					continue;
				}
				p.push_back(&buff[i],sizeof(char));
			}
			
			//バージョンをint値にして格納
			{
				int vb[bn];
				memset(vb,0,sizeof(vb));
				radix_convert conv;
				int *target = vb;
				for(int i=0;i<bn;i++)
				{
					bool isHEX = false;
					{
						size_t se = strlen(b[i]);
						int c;
						for(size_t j=0;j<se;j++){
							c = b[i][j];
							if(('A' <= c && c <= 'F') || ('a' <= c && c <= 'f')){
								isHEX = true;
								break;
							}else if(isAlpha(c))
							{//16進法じゃないみたいだから、これは数字化できないよん！
								return false;
							}
						}
					}
					if(isHEX){
						(*target) = conv.a16toi(b[i]);
					}else{
						(*target) = ::atoi(b[i]);
					}
					target++;
				}
				dll->v1 = vb[0];
				dll->v2 = vb[1];
				dll->v3 = vb[2];
				dll->v4 = vb[3];
			}
		
		}
		return true;
	}


};//end of class




}//end of dkutil namespace


#endif//end of include once

