#ifndef _KILIB_WINDOW_H_
#define _KILIB_WINDOW_H_
#include "types.h"
#include "memory.h"
#include "ktlaptr.h"
#ifndef __ccdoc__
namespace ki {
#endif



// タイムアウト付きMsgBoxの返値
#define IDTIMEOUT 0



//=========================================================================
//@{ @pkg ki.Window //@}
//@{
//	窓操作用クラス
//
//	外側から、ダイアログやコントロールやプロパティシートや
//	普通のウインドウを全部まとめて共通に扱うためのインターフェイス。
//	実際の実装は、下位クラス XxxImpl で行われている。
//@}
//=========================================================================

class Window : public Object
{
public:

	//@{ メインメッセージループ //@}
	void MsgLoop();

	//@{ メッセージを送って処理されるまで待機 //@}
	LRESULT SendMsg( UINT msg, WPARAM wp=0, LPARAM lp=0 );

	//@{ メッセージを送ってすぐ帰る //@}
	BOOL PostMsg( UINT msg, WPARAM wp=0, LPARAM lp=0 );

	//@{
	//	自動消滅機能付きメッセージボックス
	//	@param msg 表示する文字列
	//	@param caption ダイアログの題名
	//	@param type Win32SDKの説明を見てね
	//@}
	int MsgBox( LPCTSTR msg, LPCTSTR caption=NULL, UINT type=MB_OK ) const;

	//@{ テキスト設定 //@}
	void SetText( const TCHAR* str );

	//@{ 表示 //@}
	void ShowUp( int sw=SW_SHOW );

	//@{ 移動 //@}
	void MoveTo( int l, int t, int r, int b );

	//@{ フォーカス //@}
	void SetFocus();

	//@{ 最前面へGo! //@}
	void SetFront();

	//@{ 画面中央へGo! //@}
	void SetCenter();

public:

	//@{ ウインドウハンドル //@}
	HWND hwnd() const;

	//@{ 位置・サイズ //@}
	void getPos( RECT* rc ) const;

	//@{ サイズ //@}
	void getClientRect( RECT* rc ) const;

	//@{ メインループを回してるウインドウかどうか //@}
	bool isMainWnd() const;

	//@{ 生きてる？ //@}
	bool isAlive() const;

public:

	//@{ 未処理メッセージを適当に処理 //@}
	static void ProcessMsg();

	//@{ 最前面へGo! //@}
	static void SetFront( HWND hwnd );

	//@{
	//	画面中央へGo!
	//	@param hwnd 動かすウインドウ
	//	@param rel 基準にするウインドウ
	//@}
	static void SetCenter( HWND hwnd, HWND rel=NULL );

protected:

	// 何もしないコンストラクタ
	Window();

	// Hwndをセット
	void SetHwnd( HWND wnd );

	// アクセラレータを通すとかダイアログメッセージの処理とか
	virtual bool PreTranslateMessage( MSG* ) = 0;

private:

	HWND wnd_;
	bool isLooping_;

private:

	NOCOPY(Window);
};



//-------------------------------------------------------------------------
#ifndef __ccdoc__

inline LRESULT Window::SendMsg( UINT msg, WPARAM wp, LPARAM lp )
	{ return ::SendMessage( wnd_, msg, wp, lp ); }

inline BOOL Window::PostMsg( UINT msg, WPARAM wp, LPARAM lp )
	{ return ::PostMessage( wnd_, msg, wp, lp ); }

inline int Window::MsgBox( LPCTSTR m, LPCTSTR c, UINT y ) const
	{ return ::MessageBox( wnd_, m, c, y ); }

inline void Window::ShowUp( int sw )
	{ ::ShowWindow( wnd_, sw ), ::UpdateWindow( wnd_ ); }

inline void Window::SetText( const TCHAR* str )
	{ ::SetWindowText( wnd_, str ); }

inline void Window::MoveTo( int l, int t, int r, int b )
	{ ::MoveWindow( wnd_, l, t, r-l, b-t, TRUE ); }

inline void Window::SetFocus()
	{ ::SetFocus( wnd_ ); }

inline void Window::SetFront()
	{ SetFront( wnd_ ); }

inline void Window::SetCenter()
	{ SetCenter( wnd_ ); }

inline HWND Window::hwnd() const
	{ return wnd_; }

inline bool Window::isMainWnd() const
	{ return isLooping_; }

inline void Window::getPos( RECT* rc ) const
	{ ::GetWindowRect( wnd_, rc ); }

inline void Window::getClientRect( RECT* rc ) const
	{ ::GetClientRect( wnd_, rc ); }

inline bool Window::isAlive() const
	{ return FALSE != ::IsWindow( wnd_ ); }



#endif // __ccdoc__
//=========================================================================
//@{
//	IME制御マネージャ
//
//	Global IME をサポートするには、ウインドウメッセージの処理を
//	根本的に入れ替える必要がある。そこで、処理をこのクラスにまとめ
//	Windowクラスと連携処理を行うことで、ライブラリの外からは一切
//	気にせず処理をできるようにしておく。なお、Global IMEに対応
//	するにはバージョンの新しいPlatform SDKが必要なため
//	マクロ USEGLOBALIME が定義されていなければその辺は処理しない。
//@}
//=========================================================================

class IMEManager
{
public:

	//@{ フォント指定 //@}
	void SetFont( HWND wnd, const LOGFONT& lf );

	//@{ 位置指定 //@}
	void SetPos( HWND wnd, int x, int y );

	//@{ 確定文字列ゲット。受け取ったら delete すること。 //@}
	void GetString( HWND wnd, unicode** str, ulong* len );

	//@{ GlobalIMEを利用可能状態にする //@}
	void EnableGlobalIME( bool enable );

	//@{ GlobalIMEを使えるWindowのリストを登録 //@}
	void FilterWindows( ATOM* lst, UINT siz );

private:

	IMEManager();
	~IMEManager();
	void    TranslateMsg( MSG* msg );
	LRESULT DefProc( HWND wnd, UINT msg, WPARAM wp, LPARAM lp );
	void    MsgLoopBegin();
	void    MsgLoopEnd();

private:

	#ifdef USEGLOBALIME
		IActiveIMMApp*              immApp_;
		IActiveIMMMessagePumpOwner* immMsg_;
	#endif
	static IMEManager* pUniqueInstance_;

private:

	friend class Window;
	friend class WndImpl;
	friend void APIENTRY  Startup();
	friend inline IMEManager& ime();
	NOCOPY(IMEManager);
};



//-------------------------------------------------------------------------

//@{ 唯一のIME管理オブジェクトを返す //@}
inline IMEManager& ime()
	{ return *IMEManager::pUniqueInstance_; }



//=========================================================================
//@{
//	普通のウインドウ実装
//
//	派生クラスを定義して、コンストラクタの引数で WndImpl に
//	WNDCLASS名やスタイルを渡し、初回なら WNDCLASS を Register
//	して、あとは適当なタイミングで Create() という使い方を想定。
//
//	HWNDからWndImpl*への変換はx86/x86-64専用のサンクで行っています。
//	ので、他のアーキテクチャではこのままでは動作しません。移植
//	する場合は、GWL_USERDATA を使うなり clsExtra に入れるなりで
//	もう少し汎用性のある方法に適宜変えてください。
//@}
//=========================================================================

class WndImpl : public Window
{
	enum { THUNK_SIZE = 22 };

public:

	//@{ ウインドウ作成 //@}
	bool Create( LPCTSTR wndName=NULL,      HWND parent=NULL,
	             int  x     =CW_USEDEFAULT, int y      =CW_USEDEFAULT,
	             int  width =CW_USEDEFAULT, int height =CW_USEDEFAULT,
	             void* param=NULL );

	//@{ ウインドウ破棄 //@}
	void Destroy();

protected:

	//@{
	//	コンストラクタ
	//	@param className ウインドウクラス名
	//	@param style 標準スタイル
	//	@param styleEx 標準拡張スタイル
	//@}
	WndImpl( LPCTSTR className, DWORD style, DWORD styleEx=0 );
	~WndImpl();

	//@{ クラス名用の型 //@}
	typedef const TCHAR* const ClsName;

	//@{ ウインドウクラス登録 //@}
	static ATOM Register( WNDCLASSEX* cls );

	// てけとーに実装して反応してください。
	// on_commandは、処理しなかったらfalseを返すこと。
	// on_messageは、処理しなかったらWndImpl::on_messageを呼び出すこと。
	// PreTranslateMessageは、処理してもしなくても中で呼び出すこと。
	virtual void    on_create( CREATESTRUCT* cs );
	virtual void    on_destroy();
	virtual bool    on_command( UINT id, HWND ctrl );
	virtual LRESULT on_message( UINT msg, WPARAM wp, LPARAM lp );
	virtual bool    PreTranslateMessage( MSG* msg );

private:

	static LRESULT CALLBACK StartProc( HWND, UINT, WPARAM, LPARAM );
	static LRESULT CALLBACK MainProc( WndImpl*, UINT, WPARAM, LPARAM );
	void SetUpThunk( HWND wnd );

private:

	LPCTSTR     className_;
	const DWORD style_, styleEx_;
	byte*       thunk_;
};



//=========================================================================
//@{
//	ダイアログ実装
//
//	派生クラスを定義して、コンストラクタの引数で DlgImpl に
//	リソースIDを渡し、後は適当なタイミングで GoModal なり
//	Modeless なり、という使い方を想定。
//
//	HWNDからDlgImpl*への変換は GWL_USERDATA で行っています。
//	ので、それは使わないようにしましょう。
//@}
//=========================================================================

class DlgImpl : public Window
{
public:

	enum dlgtype { MODAL, MODELESS };

	//@{ モーダルで実行 //@}
	void GoModal( HWND parent=NULL );

	//@{ モードレスで作成 //@}
	void GoModeless( HWND parent=NULL );

	//@{ 強制的に終わらせる //@}
	void End( UINT code );

public:

	//@{ モーダルかモードレスか //@}
	dlgtype type() const;

	//@{ 終了コード取得 //@}
	UINT endcode() const;

protected:

	//@{ コンストラクタ //@}
	DlgImpl( UINT id );
	~DlgImpl();

	//@{ 子アイテムID→HWND変換 //@}
	HWND item( UINT id ) const;

	//@{ アイテムに対してメッセージ送信 //@}
	LRESULT SendMsgToItem( UINT id, UINT msg, WPARAM wp=0, LPARAM lp=0 );

	//@{ アイテムに対してメッセージ送信（ポインタ送る版） //@}
	LRESULT SendMsgToItem( UINT id, UINT msg, void* lp );

	//@{ アイテムに対してメッセージ送信（文字列送る版） //@}
	LRESULT SendMsgToItem( UINT id, UINT msg, const TCHAR* lp );

	// てけとーに実装して反応してください。
	// on_ok/on_cancelは、終了して良いならtrueを返すこと。
	// on_cmd/on_msgは、処理済みならtrueを返すこと。
	virtual void on_init();
	virtual void on_destroy();
	virtual bool on_ok();
	virtual bool on_cancel();
	virtual bool on_command( UINT cmd, UINT id, HWND ctrl );
	virtual bool on_message( UINT msg, WPARAM wp, LPARAM lp );
	virtual bool PreTranslateMessage( MSG* msg );

private:

	static BOOL CALLBACK MainProc( HWND, UINT, WPARAM, LPARAM );

private:

	dlgtype    type_;
	UINT       endCode_;
	const UINT rsrcID_;
};



//-------------------------------------------------------------------------
#ifndef __ccdoc__

inline DlgImpl::dlgtype DlgImpl::type() const
	{ return type_; }

inline UINT DlgImpl::endcode() const
	{ return endCode_; }

inline HWND DlgImpl::item( UINT id ) const
	{ return ::GetDlgItem( hwnd(), id ); }

inline LRESULT DlgImpl::SendMsgToItem
	( UINT id, UINT msg, WPARAM wp, LPARAM lp )
	{ return ::SendDlgItemMessage( hwnd(), id, msg, wp, lp ); }

inline LRESULT DlgImpl::SendMsgToItem( UINT id, UINT msg, void* lp )
	{ return ::SendDlgItemMessage( hwnd(), id, msg, 0,
	                            reinterpret_cast<LPARAM>(lp) ); }

inline LRESULT DlgImpl::SendMsgToItem( UINT id, UINT msg, const TCHAR* lp )
	{ return ::SendDlgItemMessage( hwnd(), id, msg, 0,
	                            reinterpret_cast<LPARAM>(lp) ); }



//=========================================================================

#endif // __ccdoc__
}      // namespace ki
#endif // _KILIB_WINDOW_H_
