#include "StdAfx.h"
#include "DirectoryIndexResponse.h"
#include "HtmlLoader.h"
#include "PathManager.h"
#include "Dir.h"
#include "MimeTypeManager.h"
#include "UrlUtility.h"
#include "ServerTextManager.h"
#include "TextFormatter.h"

#define	MAX_LINE	2048

//!	モジュール名
static TCHAR _MODULE_NAME[] = _T("DirectoryIndexResponse");

CDirectoryIndexResponse::CDirectoryIndexResponse(void)
{
}

CDirectoryIndexResponse::~CDirectoryIndexResponse(void)
{
}

/*!
	レスポンス名取得
*/
LPCTSTR CDirectoryIndexResponse::GetResponseName()
{
	return _MODULE_NAME;
};

//////////////////////////////////////////////////////////////////////////////////////////////
//	レスポンス生成
//////////////////////////////////////////////////////////////////////////////////////////////
/*!
	レスポンス生成
*/
void CDirectoryIndexResponse::BuildResponse(IResponseContextPtr responseContext)
{
	CContext			context = responseContext->GetContxet();
	CContextTargetInfo	targetInfo = context.GetTargetInfo();

	//	ディレクトリ表示は許可されているのか？
	if(!targetInfo.GetPathInfo().GetOption().GetEnableDirectoryIndex())
		throw CServerResponseException(403, _T("インデックス表示は許可されていません"));

	//	実は無いとか
	if(!targetInfo.GetTargetIsExist())
		throw CServerResponseException(404, _T("指定されたディレクトリは存在しません"));

	//	メソッドはGET?
	if(responseContext->GetContxet().GetRequestInfo().GetMethod() != "GET")
		throw CServerResponseException(405, _T("対応していないメソッドです"));

	//	ファイルリスト取得
	CBinaryData	fileList;
	BuildFileList(responseContext, fileList);

	//	HTMLファイル取得
	CString	html = LoadHtml(responseContext);

	//	書式処理
	CTextFormatter	formatter;

	//	ディレクトリリスト
	formatter.AddKey("FileList", fileList.GetCString());

	//	コンテキスト基本情報
	formatter.AddKeyFromWorkspace(context);

	//	クライアント情報
	formatter.AddKeyFromWorkspace(context.GetClientInfo());

	//	リクエスト情報
	formatter.AddKeyFromWorkspace(context.GetRequestInfo());
	formatter.AddKeyFromWorkspace(context.GetRequestInfo().GetRequestOption(), "Req:");

	//	レスポンス情報
	formatter.AddKeyFromWorkspace(context.GetResponseInfo());
	formatter.AddKeyFromWorkspace(context.GetResponseInfo().GetResponseOption(), "Res:");

	//	ターゲット情報
	formatter.AddKeyFromWorkspace(context.GetTargetInfo());

	//	書き換え実行
	m_responseBody = formatter.GetFormattedString(html);
}


/*!
	レスポンスヘッダ取得
*/
void CDirectoryIndexResponse::GetResponse(IResponseContextPtr responseContext, CContextResponseInfo responseInfo)
{
	responseInfo.SetResponseCode(200);

	//	レスポンスサイズ設定
	responseInfo.GetResponseOption().SetContentLength(m_responseBody.GetLength());

	//	MimeType
	responseInfo.GetResponseOption().SetContentType("text/html");
}

/*!
	レスポンスボディ取得
*/
int CDirectoryIndexResponse::GetResponseBody(IResponseContextPtr responseContext, CBinaryData &body, int blockSize)
{
	if(m_responseBody.IsEmpty())
		return(0);

	//	データを返す
	body.Empty();
	body.AppendString(m_responseBody);
	m_responseBody.Empty();
	return(body.GetSize());
}


//////////////////////////////////////////////////////////////////////////////////////////////
//	ファイルリストの取得
//////////////////////////////////////////////////////////////////////////////////////////////
/*!
	ファイルリスト生成
*/
void CDirectoryIndexResponse::BuildFileList(IResponseContextTempPtr responseContext, CBinaryData &fileListHtml)
{
	CString			iconPath;
	int				sortMode = -1;
	CFileItemArray	linkList, dirList, fileList;

	//	MimeTypeマネージャ取得
	IMimeTypeManagerPtr	mimeTypeMgr = responseContext->GetIServerTool()->GetIMimeTypeManager();
	CContext			context = responseContext->GetContxet();
	CContextRequestInfo	requestInfo = context.GetRequestInfo();
	CContextTargetInfo	targetInfo = context.GetTargetInfo();

	//	UP
	if(requestInfo.GetObject() != "/")
		AddListLine(fileListHtml, "Parent Directory", "../", mimeTypeMgr->GetIconPathByName(".."));

	//	ファイル・ディレクトリリストを取得
	GetDirectoryList(dirList, fileList, targetInfo.GetTarget(), targetInfo.GetPathInfo().GetOption().GetShowHiddenFile(), sortMode);

	//	リンクリストを取得
	GetLinkList(responseContext, linkList, sortMode);

	//	ディレクトリリスト出力
	iconPath = mimeTypeMgr->GetIconPathByName("^^DIRECTORY^^");
	for(int i=0;i<dirList.GetSize();i++)
		AddListLine(fileListHtml, dirList[i].m_name, dirList[i].m_name, iconPath, -1, dirList[i].m_lastUpdate);

	//	リンクリスト出力
	iconPath = mimeTypeMgr->GetIconPathByName("^^LINK^^");
	for(int i=0;i<linkList.GetSize();i++)
		AddListLine(fileListHtml, linkList[i].m_name, linkList[i].m_name, iconPath);

	//	ファイルリスト出力
	for(int i=0;i<fileList.GetSize();i++)
		AddListLine(fileListHtml, fileList[i].m_name, fileList[i].m_name, mimeTypeMgr->GetIconPathByName(fileList[i].m_name), 
					fileList[i].m_size, fileList[i].m_lastUpdate);
}


/*!
	一行追加
*/
void CDirectoryIndexResponse::AddListLine(CBinaryData &fileListHtml, CString name, CString path, CString iconPath, __int64 size, CTime updateTime)
{
	TCHAR		stringBuf[MAX_LINE];

	if(size == -1)
	{
		if(updateTime == 0)
		{
			_snprintf_s(stringBuf, MAX_LINE, MAX_LINE,
				"<IMG SRC=\"%s\"> <A HREF=\"%s\">%-70s -                        -  \n",
				iconPath, CUrlUtility::EncodeUrl(path), name + "</A>");
		}
		else
		{
			_snprintf_s(stringBuf, MAX_LINE, MAX_LINE,
				"<IMG SRC=\"%s\"> <A HREF=\"%s\">%-70s%s       -  \n",
				iconPath, CUrlUtility::EncodeUrl(path), name + "</A>", updateTime.Format("%Y/%m/%d %H:%M:%S"));
		}
	}
	else if(size < 10240)
	{
		_snprintf_s(stringBuf, MAX_LINE, MAX_LINE,
				"<IMG SRC=\"%s\"> <A HREF=\"%s\">%-70s%s      %I64d  \n",
				iconPath, CUrlUtility::EncodeUrl(path), name + "</A>",
				updateTime.Format("%Y/%m/%d %H:%M:%S"), size);
	}
	else if(size < 1048576 * 10)
	{
		_snprintf_s(stringBuf, MAX_LINE, MAX_LINE,
				"<IMG SRC=\"%s\"> <A HREF=\"%s\">%-70s%s      %I64dK  \n",
				iconPath,CUrlUtility::EncodeUrl(path), name + "</A>",
				updateTime.Format("%Y/%m/%d %H:%M:%S"), size/1024);
	}
	else
	{
		_snprintf_s(stringBuf, MAX_LINE, MAX_LINE,
				"<IMG SRC=\"%s\"> <A HREF=\"%s\">%-70s%s      %I64dM  \n",
				iconPath,CUrlUtility::EncodeUrl(path), name + "</A>",
				updateTime.Format("%Y/%m/%d %H:%M:%S"), size/1048576);
	}

	fileListHtml.Append(stringBuf, strlen(stringBuf));
}


/*!
	HTMLロード
*/
CString CDirectoryIndexResponse::LoadHtml(IResponseContextTempPtr responseContext)
{
	CString	html = CHtmlLoader::LoadFromFile(responseContext->GetContxet().GetTargetInfo().GetTarget() + "DirectoryList.html");
	if(html.IsEmpty())
		html = responseContext->GetIServerTool()->GetIServerTextManager()->LoadServerHtmlFile("DirectoryList.html");
	if(html.IsEmpty())
		html = responseContext->GetIServerTool()->GetIServerTextManager()->LoadServerHtmlResource(HTML_RESOURCE_DIRECTORY_LIST);

	return(html);
}



/*!
	リンクリストの取得
*/
void CDirectoryIndexResponse::GetLinkList(IResponseContextTempPtr responseContext, CFileItemArray & list, int sortMode)
{
	CWorkspace			pathRoot;
	CPathInfo	pathInfo = pathRoot.GetAccess("Path");

	list.RemoveAll();

	//	ターゲットパス
	CString	targetServerPath = CPathTools::AddBackSlash(responseContext->GetContxet().GetRequestInfo().GetObject());
	int		targetServerPathLen = targetServerPath.GetLength();

	//	ホスト
	CString	host = responseContext->GetContxet().GetRequestInfo().GetRequestOption().GetHost();

	//	リストを取得
	IPathManagerPtr pathManager = responseContext->GetIServerTool()->GetIPathManager();

	//	リスト設定
	for(int i=0;i<pathManager->GetPathCount();i++)
	{
		//	パス設定取得
		pathManager->GetPathByIndex(i, pathInfo);

		//	サーバパス取得(最後の/を削除)
		CString	serverPath = CPathTools::RemoveSlash(pathInfo.GetServerPath());

		//	長い？
		if(targetServerPathLen >= serverPath.GetLength())
			continue;

		//	切り出し
		int index = serverPath.ReverseFind('/');
		if(index == -1)
			continue;

		//	直下？
		if(targetServerPath.CompareNoCase(serverPath.Mid(0,index + 1))==0)
			list.Add(CFileItem(CPathTools::AddSlash(serverPath.Mid(index + 1)), 0, 0, sortMode));
	}

	//	ソートが有効
	if(sortMode != -1)
		list.Sort();
}


/*!
	ディレクトリリストの取得
*/
void CDirectoryIndexResponse::GetDirectoryList(CFileItemArray &directoryList, CFileItemArray &fileList, CString targetDir, int showHiddenFile, int sortMode)
{
	directoryList.RemoveAll();
	fileList.RemoveAll();

	//	検索
	int				done=1;
	CFileFind		find;
	if(find.FindFile(targetDir + "*.*"))
	{
		while(done)
		{
			done=find.FindNextFile();

			if(find.IsDots())
				continue;

			if(find.IsHidden() && showHiddenFile == 0)
				continue;

			//	更新日取得
			CTime	lastUpdate;
			find.GetLastWriteTime(lastUpdate);

			//	ディレクトリ?
			if(find.IsDirectory())
				directoryList.Add(CFileItem(CPathTools::AddSlash(find.GetFileName()), lastUpdate, 0, sortMode));
			else
				fileList.Add(CFileItem(find.GetFileName(), lastUpdate, find.GetLength(), sortMode));
		}
	}
	find.Close();

	//	ソートが有効
	if(sortMode != -1)
	{
		directoryList.Sort();
		fileList.Sort();
	}
}


//////////////////////////////////////////////////////////////////////////////////////////////
//	取得
//////////////////////////////////////////////////////////////////////////////////////////////
/*!
	自分のアイテムか？
*/
int CDirectoryIndexResponseFactory::IsYourRequest(CContext context)
{
	//	ターゲット取得
	CContextTargetInfo targetInfo = context.GetTargetInfo();

	//	ディレクトリ
	if(targetInfo.GetTargetIsDirectory() && targetInfo.GetTargetIsExist())
		return(TRUE);
	return(FALSE);
}

