/**
@filename ProgressDialog.cpp
@brief Windows95にも対応したProgressDialog
@author d金魚
@since 2006/02/08
@note
this source code licence is NYSL
 */

#define _WIN32_IE 0x0200
#define WINVER 0x0400


#include "ProgressDialog.h"
#include "ProgressDialog.hpp"
#include "resource.h"
#include <vector>
#include <list>
#include <dkutil/string.hpp>
#include <helper/win32_resource.hpp>
#include <commctrl.h>
#pragma comment(lib, "comctl32.lib") 


namespace dkutil{

class MoveStringInTextBox{
  std::vector<char> mv;
  size_t index;
  size_t mTextBoxSize;
  std::string mStr;

  void init(size_t textboxsize,size_t strsize){
    mv.clear();
    for(size_t i=0;i<textboxsize + strsize * 2;i++)
      mv.push_back(' ');
    mv.push_back('\0');
  }
public:
  MoveStringInTextBox(){
    index = 0;
  }
  ~MoveStringInTextBox(){
  }
  bool reset(parm_string str,size_t textboxsize)
  {
    index = 0;
    mStr = str.c_str();
    mTextBoxSize = textboxsize;
    return true;
  }
  void process()
  {
    size_t strsize = mStr.size();
    init(mTextBoxSize,strsize);
    size_t n = mTextBoxSize - strsize;
    if(index >= n){
      index = 0;
    }
    size_t c = 0;
    for(size_t i=index;i<n;i++){
      mv[i] = mStr.at(c);
      c++;
      if(c >= mStr.size()){
        break;
      }
    }
    index++;
    mv[strsize + mTextBoxSize] = '\0';

  }
  const char *getString()const{
    return &(mv[mStr.size()]);
  }
  

};





}

using namespace dkutil;

struct ProgressDialogObject{
	HWND mhwnd;

	DWORD mMoveTextTime;
	MoveStringInTextBox mMoveText;
	unsigned int *mParcentage;
	unsigned int mTempParcentage;
	unsigned int mSecondTime;
	BOOL *mIsCancel;
	BOOL SetText(HWND hwnd ,LPCTSTR lpszText)
	{
		return (BOOL)::SendMessage(hwnd, WM_SETTEXT , 0, (LPARAM)lpszText);
	}
public:

	void init_hwnd(HWND hWnd)
	{
		mhwnd = hWnd;
	}
	void setProgressRange(HWND hWnd){
		HWND h = GetDlgItem(hWnd,IDC_PROGRESS1);
		SendMessage(h, PBM_SETRANGE, 0, MAKELONG(0,100));
		SendMessage(h, PBM_SETSTEP, (WPARAM)1, 0);

	}
	bool setMoveText(parm_string str,unsigned int t)
	{
		if(0==t){
			t = ID_DPD_TEXTBOX_SIZE;
		}
		return mMoveText.reset(str,t);
	}
	bool reset(unsigned int *per,parm_string movestr,BOOL *isCancel)
	{
		mTempParcentage = 0;
		mSecondTime = 0;
		mMoveTextTime = GetTickCount();
		mIsCancel = isCancel;
		mParcentage = per;

		return setMoveText(movestr,0);
	}
	void drawTextNonTimer(){
		
			mMoveText.process();
			const char *sss = mMoveText.getString();
#if 1//こちらじゃないとタイマーの関係でStack over flowする時がある。
			HWND wnd = GetDlgItem(mhwnd,IDC_EDIT1);
			SetText(wnd,sss);
			//ShowWindow(wnd,SW_SHOW);
			//UpdateWindow(wnd);
#else
			SetDlgItemText(mhwnd,IDC_EDIT1,sss);
#endif
	}
	void drawText(){
		DWORD t = GetTickCount();
		if(t - mMoveTextTime > 50){//0.25 second
			mMoveTextTime = t;
			drawTextNonTimer();
		}
		
	}
	void updateProgress(){
		if(*mParcentage != 0){
			//dODS("OK %d\n",*mParcentage);
		}
		if(mTempParcentage != *mParcentage)
		{
			HWND h = GetDlgItem(mhwnd,IDC_PROGRESS1);
			SendMessage(h, PBM_SETPOS , (WPARAM)*mParcentage, 0);
			//ShowWindow(h,SW_SHOW);
			//UpdateWindow(h);
			mTempParcentage = *mParcentage;
		}
	}
	void endDialog(){
		*mIsCancel = FALSE;
		killTimer();
		EndDialog(mhwnd,TRUE);

	}
	void cancelDialog()
	{
		*mIsCancel = TRUE;
		//killTimer();
		//EndDialog(mhwnd,FALSE);
		ShowWindow(mhwnd,SW_HIDE);
	}
	enum{
		ID_DPD_DRAW_TIMER = 141421356,
		ID_DPD_ONE_SECOND_TIMER = 17320508,
		ID_DPD_TIME = 50,
		ID_DPD_TEXTBOX_SIZE = 64 * 2 + 13,

	};
	void setTimer(){
		SetTimer(mhwnd, ID_DPD_DRAW_TIMER, ID_DPD_TIME, NULL);
		SetTimer(mhwnd, ID_DPD_ONE_SECOND_TIMER,1000,NULL);
	}
	void killTimer(){
		KillTimer(mhwnd, ID_DPD_DRAW_TIMER);
		KillTimer(mhwnd,ID_DPD_ONE_SECOND_TIMER);
	}
	void updateOneSecond(){
		std::string s = to_string_uint32(mSecondTime);
		s += " second";
		SetDlgItemText(mhwnd,IDC_TIMESHOW,s.c_str());
		mSecondTime++;
	}
	void setInfomation(){
		HINSTANCE h = (HINSTANCE)GetWindowLong(mhwnd , GWL_HINSTANCE);
		std::string str ,copyright,ts;
		GetResourceString(h,IDS_COPYRIGHT,copyright);
		
		if(false==dkutil::GetResourceVersionString(str,h)){
			//SetDlgItemText(mhwnd,IDC_COPYRIGHT,
			return;
		}else{
			ts += "dKingyo Progress Dialog ";
			ts += str;
			ts += "\n";
			if(copyright.empty()){
				ts += "Copyright(c) d.Kingyo all rights reserved.";
			}else{
				ts += copyright;
			}
			SetDlgItemText(mhwnd,IDC_COPYRIGHT,ts.c_str());
		}

	}
	


};

static BOOL CALLBACK ProgressDialogProc(HWND hWnd,UINT msg,UINT wParam,LONG lParam){


	static ProgressDialogObject *Obj=NULL;

	
	if(Obj)
	{
		Obj->init_hwnd(hWnd);

	}
	
	switch(msg){
	case WM_INITDIALOG:
	case WM_CREATE:
		Obj = (ProgressDialogObject *)lParam;
		Obj->init_hwnd(hWnd);
		Obj->setProgressRange(hWnd);
		Obj->setTimer();
		Obj->setInfomation();

		break;
	case WM_DESTROY:
		//break;
	case WM_CLOSE:
		Obj->endDialog();
		break;
	case WM_COMMAND:
		switch(LOWORD(wParam)){
			case IDCANCEL:
				Obj->cancelDialog();
				break;
			default:
				break;
		}
		break;
	case WM_TIMER:
		//Obj->killTimer();
		switch(wParam){
		case ProgressDialogObject::ID_DPD_DRAW_TIMER:
			Obj->drawTextNonTimer();
			Obj->updateProgress();
			break;
		case ProgressDialogObject::ID_DPD_ONE_SECOND_TIMER:
			Obj->updateOneSecond();
			break;

		}
		//Obj->setTimer();
		break;
	default:
		//return DefWindowProc( hWnd,msg,wParam,lParam);
		break;
	}



	
	return 0;
}






static HINSTANCE g_hinstDLL = NULL;
typedef std::pair<HWND,ProgressDialogObject> data_pair;
typedef std::list<data_pair> list_type;
static list_type gList;


static ProgressDialogObject *createPDDomain(unsigned int *percentage,const char *movetext,BOOL *isCancel){
	ProgressDialogObject d,*pd;
	if(movetext){
		d.reset(percentage,movetext,isCancel);
	}else{
		d.reset(percentage,"(-_-) Now Processing ...",isCancel);
	}
	gList.push_front(data_pair(NULL,d));
	pd = &(gList.front().second);
	return pd;
}


static HWND pd_modeless(HINSTANCE hinst,unsigned int *percentage,const char *movetext,BOOL *isCancel)
{
	synchronized swimming;
	ProgressDialogObject *pd;
	pd = createPDDomain(percentage,movetext,isCancel);

	HWND tw = CreateDialogParam(
			hinst,
			MAKEINTRESOURCE(IDD_PROGRESS_DIALOG) , NULL , ProgressDialogProc,
			(long)pd
		);
	if(NULL==tw){
		gList.pop_front();
		return tw;
	}
	gList.front().first = tw;
	//ShowWindow(tw,SW_SHOW);
	return tw;
}
static HWND pd_modeless2(HWND hwnd,unsigned int *percentage,const char *movetext,BOOL *isCancel)
{
	synchronized swimming;
	ProgressDialogObject *pd;
	pd = createPDDomain(percentage,movetext,isCancel);

	

	HWND tw = CreateDialogParam(
			(HINSTANCE)GetWindowLong(hwnd , GWL_HINSTANCE) ,
			MAKEINTRESOURCE(IDD_PROGRESS_DIALOG) , hwnd , ProgressDialogProc,
			(long)pd
		);
	if(NULL==tw){
		gList.pop_front();
		return tw;
	}
	gList.front().first = tw;
	//ShowWindow(tw,SW_SHOW);
	return tw;


}


static BOOL pd_free(HWND hwnd){
		list_type::iterator it = gList.begin();
	for(;it != gList.end();it++)
	{
		if((*it).first == hwnd){
			//	synchronized swimming;　create時にもぐっているのでいらない。
			
			//@todo ダイアログがすべて消えている時にWin95系ではバグルかもしれない。
			//いや、cancel時にSW_HIDEする。
			DestroyWindow(hwnd);
			gList.erase(it);
			return TRUE;

		}
	}
	return FALSE;//not found
}
static list_type::iterator pb_find(HWND hwnd){
	list_type::iterator it = gList.begin();
	for(;it != gList.end();it++)
	{
		if((*it).first == hwnd){
			break;
		}
	}
	return it;
}
static BOOL pb_setmovetext(HWND hwnd,const char *movetext,unsigned int t){
	list_type::iterator it = pb_find(hwnd);
	if(gList.end()==it) return FALSE;

	return (*it).second.setMoveText(movetext,t);
}






namespace dkutil{

bool ProgressDialog::open(const char* movestr)
{
	close();
	mhwnd = pd_modeless(mhInstance,&mPercentage,movestr,&mIsCancel);
	if(NULL==mhwnd){
		return false;
	}
	show_dialog();
	return true;
}
void ProgressDialog::close()
{
	if(mhwnd)
	{
		pd_free(mhwnd);
	}
	init();
}
bool ProgressDialog::setMoveText(const char *s,unsigned int t){
	return 0 != pb_setmovetext(mhwnd,s,t);
}



}


#ifdef DKINGYOPROGRESSDIALOG_DLL

HWND WINAPI dkcProgressDialogDoModeless(unsigned int *percentage,const char *movetext,BOOL *isCancel){
	return pd_modeless(g_hinstDLL ,percentage,movetext,isCancel);
}


HWND WINAPI dkcProgressDialogDoModeless2(HWND hwnd,unsigned int *percentage,const char *movetext,BOOL *isCancel){

	return pd_modeless2(hwnd,percentage,movetext,isCancel);

}
BOOL WINAPI dkcProgressDialogFree(HWND hwnd){
	return pd_free(hwnd);
}




BOOL WINAPI dkcProgressDialogSetMoveText(HWND wnd,const char *s,unsigned int t){
	return pb_setmovetext(wnd,s,t);
}

BOOL WINAPI DllMain ( HINSTANCE hinstDLL,  // handle to DLL module
                           DWORD fdwReason,     // reason for calling function
                           LPVOID lpvReserved   // reserved
                         )
{
	switch(fdwReason)
  {
    case    DLL_PROCESS_ATTACH:
        	InitCommonControls();
        break;

    case    DLL_PROCESS_DETACH:
        /*  ほげほげ    */
        break;

    case    DLL_THREAD_ATTACH:
        	InitCommonControls();
        break;

    case    DLL_THREAD_DETACH:
        /*  ほげほげ    */
        break;
	}

	g_hinstDLL = hinstDLL;
	return TRUE;
}

#endif
