/*******************************************************************************
*	ファイル名：Common.cpp
*	用　　　途：プロジェクト共通グローバル関数群実装
*	備　　　考：
*	履　　　歴：
********************************************************************************/

/****************************
*	インクルードファイル	*
****************************/
#include "StdAfx.h"
#include <shlwapi.h>
#include "Atomic.h"
#include "Common.h"
#include "DiskCheck.h"
#include "System.h"
/****************************
*	　マクロ・定数定義		*
****************************/

/****************************
*		 構造体定義			*
****************************/

/****************************
*	　  関　数　定　義		*
****************************/

/*******************************************************************************
*	関　数　名：GetEnvPath
*	用　　　途：作業フォルダのパス名を取得します
*				パス名先頭が実行ファイル名と同じパスを抽出します
*	引　き　数：作業フォルダパス名返却先
*	戻　り　値：
*	備　　　考：Debug/Releaseの各実行モードやテスト環境になるべく関わらない形で
*			　　デバッグ・実行することが目的です。
*				※非公開メンバー
********************************************************************************/
void GetEnvPath( LPTSTR pszEnvPath )
{
	TCHAR		szExePath[_MAX_PATH] ;			// 実行ファイルパス名
	LPTSTR		psz ;							// 探索ポインタ
	LPTSTR		pszStart ;						// 階層毎探索開始ポインタ
	int			nPathLength ;					// 階層名文字列長
	int			nExeNameLength ;				// 実行ファイル名文字列長
	CString		strExeName ;					// 実行ファイル名

	// 実行ファイル名取得
	pszEnvPath[0]	= _T('\0') ;
	strExeName		= ::AfxGetAppName() ;
	nExeNameLength	= strExeName.GetLength() ;

	// 実行ディレクトリ名の取得
	::GetModuleFileName( NULL, szExePath, sizeof(szExePath) ) ;	// 実行ファイルの絶対パス取得
	::PathRemoveFileSpec(szExePath) ;							// パスからファイル名を除く

	// ループ初期設定
	psz				= szExePath - 1 ;
	pszStart		= szExePath ;

	// 各部分フォルダ名の先頭が実行ファイル名と一致するか、終端に
	// 到達するまでループ
	do{ // loop
		psz++ ;
		if( *psz!=_T('\\') && *psz!=_T('\0') ){ 
			continue ;
		}

		nPathLength = (int)( psz - pszStart ) ;
		if( nPathLength < strExeName.GetLength() ){ 
			pszStart = psz + 1 ;
			continue ;
		}

		// 大文字小文字に関係なく比較
		// フォルダ名の先頭と実行ファイル名が同一ならそこが
		// 実行環境と解釈する
		if( _tcsnicmp(pszStart, strExeName, nExeNameLength) == 0 ){ 
			nPathLength			= (int)( psz - szExePath ) ;
			_tcsncpy_s( pszEnvPath, _MAX_PATH, szExePath, nPathLength ) ;
			pszEnvPath[nPathLength] = _T('\0') ;

			break ;
		}

		pszStart = psz + 1 ;
	}while( *psz != _T('\0') ) ;
}


/*******************************************************************************
*	関　数　名：EnvPath
*	用　　　途：作業フォルダパス取得
*	引　き　数：最後のバックスラッシュの有無
*	戻　り　値：作業フォルダパス名
*	備　　　考：静的固定領域に作業パス文字列を１度だけ取得しておき、
*				２度目以降は、この領域の文字列を返却する
*				本来はありえないが、メインスレッド以外のスレッドが最初に
*				本関数を呼んだ場合のロックを行っている
********************************************************************************/
CString EnvPath( BOOL bAddLastSlash )
{
	/// 静的変数宣言と初期化
	static	ATOMIC_LONG	s_lbInitEnvPath				= FALSE ;		// 初期化(パス取得済みフラグ)
	static	ATOMIC_LONG	s_lbInitLock				= 0 ;			// 初期化用ロック
	static	TCHAR		s_szEnvPath[_MAX_PATH+1]	= { 0 } ;		// 取得済み作業パス名保持

	/// 本文
						CString	strEnvPath = _T("") ;		// 返却パス

	do{ // once
		// 初期化済みならば終了
		if( s_lbInitEnvPath ){ 
			strEnvPath = s_szEnvPath ; 
			break ;
		}

		// 初期化ロック獲得
		while( ::InterlockedCompareExchange(&s_lbInitLock, 1, 0) != 0 ){ 
			::Sleep( 10 ) ;
		}

		// 再度初期化状態を確認し、ロック獲得の一瞬の間に初期化されていたならば
		// 終了
		// (スレッドセーフ対応用double checked locking)
		if( s_lbInitEnvPath ){ 
			strEnvPath = s_szEnvPath ; 
			break ;
		}

		// 作業パスを取得して保持
		//GetEnvPath( s_szEnvPath ) ;
		GetMyPath(s_szEnvPath);

		// 初期化ロック解除
		::InterlockedExchange( &s_lbInitLock, 0 ) ;

		// 作業パスセットおよび初期化済みセット
		strEnvPath		= s_szEnvPath ;
		s_lbInitEnvPath	= TRUE ; 
	} while ( FALSE ) ;

	// 必要であれば、バックスラッシュを追加する
	if( bAddLastSlash ){ 
		strEnvPath += _T('\\') ;
	}

	return strEnvPath ;
}


/*******************************************************************************
*	関　数　名：AbsPath
*	用　　　途：作業フォルダからの相対パスを絶対パスに変換する
*	引　き　数：対象相対パス
*	戻　り　値：絶対パス
*	備　　　考：
********************************************************************************/
CString AbsPath( LPCTSTR szRelativePath )
{
	if( szRelativePath != NULL ){ 
		return EnvPath(FALSE) + szRelativePath ;
	}
	else{ 
		return EnvPath(FALSE) ;
	}
}


/*******************************************************************************
*	関　数　名：UpdateContents
*	用　　　途：更新処理通知
*	引　き　数：通知コード
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：UpdateAllViewsのグローバル関数版
********************************************************************************/
BOOL UpdateContents( LPARAM lNotifCode )
{
	CFrameWnd*	pMainFrame ;
	CDocument*	pDoc ;

	//***** アプリケーションフレームウィンドウを頼りにドキュメントを取得する
	pMainFrame	= (CFrameWnd*)AfxGetApp()->m_pMainWnd ;
	ASSERT_KINDOF( CFrameWnd, pMainFrame ) ;
	pDoc		= (CDocument*)pMainFrame->GetActiveDocument() ;
	ASSERT_KINDOF( CDocument, pDoc ) ;

	pDoc->UpdateAllViews( NULL, lNotifCode, NULL ) ;

	return TRUE ;
}


/*******************************************************************************
*	関　数　名：PostToView
*	用　　　途：Viewへメッセージイベントを通知する
*	引　き　数：通知メッセージID、パラメータ1、パラメータ2
*	戻　り　値：成功(TRUE) / 失敗(FALSE)
*	備　　　考：
********************************************************************************/
BOOL PostToView( UINT uEventID, WPARAM wParam, LPARAM lParam )
{
	CFrameWnd*	pMainFrame ;
	CView*		pView ;

	//***** アプリケーションフレームウィンドウを頼りにドキュメントを取得する
	pMainFrame	= (CFrameWnd*)AfxGetApp()->m_pMainWnd ;
	ASSERT_KINDOF( CFrameWnd, pMainFrame ) ;
	pView		= pMainFrame->GetActiveView() ;

	return ::PostMessage( pView->m_hWnd, uEventID, wParam, lParam ) ;
}

/********************************************************************************
*	関数名：CheckInputPath															
*	用　途：入力元パス確認																
*	引き数：																	
*	戻り値：																	
*	備　考：入力元の存在 & 入力元がフォルダ を確認																	
********************************************************************************/
BOOL CheckInputPath( CString strPath )
{
	BOOL bExists	= ( ::PathFileExists ( strPath ) == FALSE ) ? FALSE : TRUE ;
	BOOL bDirectory = ( ::PathIsDirectory( strPath ) == FALSE ) ? FALSE : TRUE ;

	return ( bExists & bDirectory ) ;
}