#include <Windows.h>
#include <string>
#include <iostream>
#include <vector>
#include <array>
#include <shlobj.h>
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")

// このプログラムで使うウィンドウ情報の構造体
struct WindowInfo
{
	HWND		handle ;		// ウィンドウハンドル
	std::string caption ;		// キャプション

	// コンストラクタ
	WindowInfo()
		:handle( NULL )
	{}
	WindowInfo( const HWND& p_handle, const std::string& p_caption )
		:handle( p_handle )
		,caption( p_caption )
	{}
} ;
// このプログラムで使うメニュー情報の構造体
struct MenuInfo
{
	unsigned int id ;		// メニューID
	std::string caption ;	// キャプション
	HMENU handle ;			// メニューハンドル
	unsigned int itemNum ;	// アイテム数

	// コンストラクタ
	MenuInfo()
		:id( 0 )
		,handle( NULL )
		,itemNum( 0 )
	{}
	MenuInfo( const unsigned int& p_id, const std::string& p_caption, const HMENU& p_handle, const int& p_itemNum )
		:id( p_id )
		,caption( p_caption )
		,handle( p_handle )
		,itemNum( p_itemNum )
	{}
} ;

// GetLastErrorのテキストを取得する関数
std::string GetLastErrorMessage()
{
	std::string text ;

	// エラーメッセージ用バッファ
	LPVOID lpMsgBuf = NULL ;
	//エラー表示文字列作成
	if( ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
						 NULL,
						 ::GetLastError(),
						 MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), 
						 (LPTSTR)& lpMsgBuf,
						 0,
						 NULL ) != 0 )
	{
		if( lpMsgBuf != NULL )
		{
			text = reinterpret_cast<const char *>( lpMsgBuf ) ;
			::LocalFree( lpMsgBuf );
		}
	}

	return text ;
}
// ファイル存在チェックします
bool IsFileExists( const std::string& filePath )
{
	bool isExists = ( ::PathFileExists( filePath.c_str() ) != 0 ) ;
	
		// ディレクトリでないことをチェックする
	if( isExists )
	{
		isExists = ( ( ::PathIsDirectory( filePath.c_str() ) != 0 ) == false ) ;
	}

	return isExists ;
}
// 前方一致するかどうかチェックする関数
bool IsStringStartWith( const std::string& targetText, const std::string& searchText )
{
	// 空文字を探さないための対応
	if( targetText.empty() ){ return false ; }
	// 空文字を探さないための対応
	if( searchText.empty() ){ return false ; }

	return ( targetText.substr( 0, searchText.size() ) == searchText ) ;
}

// EnumWindowsのコールバック関数
BOOL CALLBACK EnumWindowsProc( HWND hWnd, LPARAM lParam )
{
	char* buf = new char[256] ;

	// キャプションを取得
	::GetWindowText( hWnd, buf, 256 ) ;

	// 名前が前方一致するかチェック
	if( IsStringStartWith( buf, ((WindowInfo*)lParam)->caption ) )
	{
		// 一致したら戻り値を設定して列挙終了
		((WindowInfo*)lParam)->handle = hWnd ;

		delete[] buf ;

		return FALSE ;
	}

	delete[] buf ;

	return TRUE ;
}
// 指定のキャプションに前方一致するウィンドウハンドルを取得します
HWND GetWindowHandle( const std::string& in_caption )
{
	// 参考 http://7ujm.net/C++/EnumWindows.html

	WindowInfo info = WindowInfo( NULL, in_caption ) ;

	::EnumWindows( EnumWindowsProc, (LPARAM)&info ) ;

	return info.handle ;
}

// メニューの情報を取得します
bool GetMenuInfo( const HMENU& in_menuHandle, const unsigned int& item, MenuInfo& out_info )
{
	// 戻り値初期化
	out_info = MenuInfo() ;

	// メニュー無しの場合は処理しません
	if( in_menuHandle == NULL ){ return true ; }

	MENUITEMINFO mii ;
	mii.fMask		= 0x3F ;
	mii.dwTypeData	= "\0" ;
	mii.cch			= 0 ;
	mii.cbSize		= sizeof( MENUITEMINFO ) ;

	//-----------------------------------------------
	// メニューアイテムの情報を取得
	//-----------------------------------------------
	if( ::GetMenuItemInfo( in_menuHandle, item, 1, &mii ) == 0 )
	{
		// 取得失敗
		std::cout << GetLastErrorMessage() << std::endl ;
		return false ;
	}

	//-----------------------------------------------
	// キャプションを取得するための準備
	//-----------------------------------------------
	// セパレータ―の場合はスルー
	const unsigned int dataSize = mii.cch ;
	if( dataSize == 0 ){ return true ; }

	std::string tmp ;
	for( unsigned int j=0, m=dataSize; j<m; ++j )
	{
		tmp.append( " " ) ;
	}

	mii.dwTypeData = (LPSTR)tmp.c_str() ;
	mii.cch = dataSize + 1 ;
	mii.cbSize = sizeof( MENUITEMINFO ) ;
		
	//-----------------------------------------------
	// もう一度メニューアイテムの情報を取得
	//-----------------------------------------------
	if( ::GetMenuItemInfo( in_menuHandle, item, 1, &mii ) == 0 )
	{
		// 取得失敗
		std::cout << GetLastErrorMessage() << std::endl ;
		return false ;
	}
	
	//-----------------------------------------------
	// 戻り値設定
	//-----------------------------------------------
	const unsigned int id		= mii.wID ;				// メニューID
	const std::string caption	= mii.dwTypeData ;		// キャプション
	const HMENU handle			= mii.hSubMenu ;		// メニューハンドル(ツリーの末端のときはハンドルがNULL)
	unsigned int itemNum		= 0 ;					// メニューに紐づくサブメニュー数

	// さらにツリーがあるときのみサブメニュー数を取得
	if( handle != NULL )
	{
		int tmpNum	= ::GetMenuItemCount( handle ) ;	
		if( tmpNum <= -1 )
		{
			// 取得失敗
			std::cout << GetLastErrorMessage() << std::endl ;
			return false ;
		}

		//********************************************************************
		// どうしてアイテム数が0件になるやつと、ならないやつがあるのかな？？
		//********************************************************************
		if( tmpNum == 0 )
		{
			std::cout << caption + "というメニューからは正しくアイテム数をカウントできないようです" << std::endl ;
		}

		itemNum = tmpNum ;
	}

	// 戻り値にセット
	out_info = MenuInfo( id, caption, handle, itemNum ) ;

	return true ;
}
// 親メニューハンドルから全てのサブメニュー情報を取得します(再帰あり)
bool GetMenuInfoList( const HMENU& in_menuHandle, std::vector<MenuInfo>& out_infoList )
{
	// 戻り値クリア
	out_infoList.clear() ;
	
	// メニュー無しの場合は処理しません
	if( in_menuHandle == NULL ){ return true ; }

	// このメニューにツリーされているアイテムの数を取得
	const int tmpNum = ::GetMenuItemCount( in_menuHandle ) ; // 失敗すると-1が返ります
	if( tmpNum < -1 )
	{
		// 取得失敗
		std::cout << GetLastErrorMessage() << std::endl ;
		return false ;
	}
	// このメニューにツリーされているアイテムの数
	const unsigned int topItemNum = tmpNum ;

	// 戻り値をリザーブしておきます
	out_infoList.reserve( topItemNum ) ;

	// このメニューにツリーされているアイテムを全て走査して戻り値に追記
	for( unsigned int i=0, n=topItemNum; i<n; ++i )
	{
		// i番目のアイテムのメニュー情報を取得します
		MenuInfo menuInfo ;
		if( GetMenuInfo( in_menuHandle, i, menuInfo ) == false )
		{
			return false ;
		}

		// i番目のアイテムの中にさらにサブアイテムがある場合
		if( menuInfo.itemNum > 0 )
		{
			// 再帰してサブアイテムを全て取得します
			std::vector<MenuInfo> subMenuInfoList ;
			if( GetMenuInfoList( menuInfo.handle, subMenuInfoList ) == false )
			{
				return false ;
			}

			// サブアイテムのキャプションが空(つまりセパレーター)の場合はアイテムとしてカウントしないことにします
			for( unsigned int j=0, m=subMenuInfoList.size(); j<m; ++j )
			{
				if( subMenuInfoList[j].caption.empty() )
				{
					--menuInfo.itemNum ;
				}
			}
		}

		// 戻り値に追記します
		out_infoList.push_back( menuInfo ) ;
	}

	return true ;
}
// 起動するアプリケーションの構造体
struct App
{
	const std::string path ;	// パス
	const std::string caption ;	// キャプション(前方一致)

	// コンストラクタ
	App()
	{}
	App( const std::string p_path, const std::string p_caption )
		:path( p_path )
		,caption( p_caption )
	{}
} ;

//**************************************************************
// 起動するアプリケーションのパスとか(64bit自分用)
//**************************************************************
static const std::array<App, 5> APP_LIST =
{
	// メモ帳
	App( "C:\\WINDOWS\\system32\\notepad.exe",	// パス
		 "無題" ),		// キャプション(前方一致)
	// 電卓
	App( "C:\\WINDOWS\\system32\\calc.exe",		// パス
		 "電卓" ),		// キャプション(前方一致)
	// サクラエディタ
	App( "C:\\Program Files (x86)\\sakura\\sakura.exe",	// パス
		 "(無題)" ),	// キャプション(前方一致)
	// 秀丸エディタ
	App( "C:\\Program Files (x86)\\Hidemaru\\Hidemaru.exe",	// パス
		 "(無題)" ),	// キャプション(前方一致)
	// DF(差分チェックツール)
	App( "C:\\DF\\DF.exe",	// パス
		 "DF" ),		// キャプション(前方一致)
} ;

// 処理本体です
bool Proc(const char *a, const char *b)
{
	//**************************************************************
	// ↓お好みを選んでください(^v^)
	//**************************************************************
	//const App app = APP_LIST[0] ;	// メモ帳
	//const App app = APP_LIST[1] ;	// 電卓
	//const App app = APP_LIST[2] ;	// サクラエディタ
	//const App app = APP_LIST[3] ;	// 秀丸エディタ
	//const App app = APP_LIST[4] ;	// DF(差分チェックツール)
	const App app = App(a, b);

	// ウィンドウが開くまでの待機時間
	const unsigned int waitMSec = 2000 ;	// ミリ秒

	//-----------------------------------------------------------------------
	// アプリケーションがあるかどうか一応チェックします
	//-----------------------------------------------------------------------
	if( IsFileExists( app.path ) == false )
	{
		std::cout << app.path + "はありません" << std::endl ;
		return false ;
	}

	//-----------------------------------------------------------------------
	// アプリケーションを起動します
	//-----------------------------------------------------------------------
	std::cout << app.path + "を起動" << std::endl ;
	STARTUPINFO si ;
	PROCESS_INFORMATION pi ;
	::GetStartupInfo( &si ) ;
	if( ::CreateProcess( NULL, (LPSTR)app.path.c_str(), NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi ) == 0 )
	{
		std::cout << GetLastErrorMessage() << std::endl ;
		return false ;
	}

	// ウィンドウが生成されるまで少し待たないといけません
	Sleep( waitMSec ) ;

	//-----------------------------------------------------------------------
	// アプリケーションのウィンドウハンドルを取得します
	//-----------------------------------------------------------------------
	// キャプションから前方一致で探してきます
	const HWND windowHandle = GetWindowHandle( app.caption ) ;
	// ハンドルが取得できなければ終了
	if( windowHandle == NULL )
	{
		std::cout << app.caption + "に前方一致するウィンドウを見つけられません" << std::endl ;
		return false ;
	}

	//-----------------------------------------------------------------------
	// アプリケーションをアクティブにします
	//-----------------------------------------------------------------------
	::SetForegroundWindow( windowHandle ) ;

	//-----------------------------------------------------------------------
	// アプリケーションのメニューを取得します
	//-----------------------------------------------------------------------
	// まず、ウィンドウハンドルからメニューハンドルを取得します
	HMENU menuHandle = ::GetMenu( windowHandle ) ;

	// 次に、ウィンドウのメニューを全て取得します
	std::vector<MenuInfo> menuInfoList ;
	if( GetMenuInfoList( menuHandle, menuInfoList ) == false )
	{
		return false ;
	}

	//-----------------------------------------------------------------------
	// デバッグプリントしてみます
	//-----------------------------------------------------------------------
	std::cout << "---------メニューアイテム数の列挙---------" << std::endl ;
	for( unsigned int i=0, n=menuInfoList.size(); i<n; ++i )
	{
		std::cout << menuInfoList[i].caption + " = " << menuInfoList[i].itemNum << std::endl ;
	}
	std::cout << "------------------------------------------" << std::endl ;

	//-----------------------------------------------------------------------
	// 処理終了する
	//-----------------------------------------------------------------------
	std::cout << "正常終了ですよ(^o^)" << std::endl ;
	return true ;
}

// メイン関数
int main(int argc, char *argv[])
{
	// 処理実施
	if(argc < 3) return 1;
	Proc(argv[1], argv[2]) ;

    //std::cin.ignore() ;
	return 0 ;
}

