/*******************************************************************************
  TPI - flexible but useless plug-in framework.
  Copyright (C) 2002-2009 Silky

  This library is free software; you can redistribute it and/or modify it under
  the terms of the GNU Lesser General Public License as published by the Free
  Software Foundation; either version 2.1 of the License, or (at your option)
  any later version.

  This library is distributed in the hope that it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
  for more details.

  You should have received a copy of the GNU Lesser General Public License along
  with this library; if not, write to the Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

  $Id: functions.cpp,v 1.7 2009/08/12 12:26:49 sirakaba Exp $
*******************************************************************************/

//******************************************************************************
//    ファイル読み込み
//******************************************************************************

#include "frontend-wx.h"
#include "frm_main.h"
#include "dlg_make.h"
#include "dlg_process.h"
#include "functions.h"

//******************************************************************************
//    コントロール処理系関数
//******************************************************************************

bool TreeView_CheckNewerItem(wxTreeCtrl * tree_ctrl, wxTreeItemId idParent, const wxString & szDirPath)
{
	// 最初の子アイテムを取得。
	wxTreeItemIdValue idv;
	wxTreeItemId idChild = tree_ctrl->GetFirstChild(idParent, idv);
	if (idChild.IsOk())
	{
		do
		{
			// アイテムと名前が同じならOK。
			if (tree_ctrl->GetItemText(idChild) == szDirPath.BeforeFirst(wxT('/')))
			{
				// これ以下の階層が無い場合。
				if (szDirPath.Find(wxT('/')) == wxNOT_FOUND)
				{
					return true;
				}

				// まだ階層が有る場合、再帰。
				return TreeView_CheckNewerItem(tree_ctrl, idChild, szDirPath.AfterFirst(wxT('/')));
			}

			idChild = tree_ctrl->GetNextSibling(idChild);
		}
		while (idChild.IsOk());
	}

	// 対象が無い場合には作成。
	if (! szDirPath.IsEmpty())
	{
		// 2階層以上深い場合にも対応。
		wxString szTemp = szDirPath;
		wxTreeItemId idTemp = idParent;
		do
		{
			// 展開してから作成。
			idTemp = tree_ctrl->AppendItem(idTemp, szTemp.BeforeFirst(wxT('/')), 1, -1);
			szTemp = szTemp.AfterFirst(wxT('/'));
		}
		while (szTemp.Find(wxT('/')) != wxNOT_FOUND);

		// 最後に空でない場合にはもう1階層作成。
		if (! szTemp.IsEmpty())
		{
			tree_ctrl->AppendItem(idTemp, szTemp, 1, -1);
		}
	}

	return false;
}

wxString TreeView_GetItemPath(wxTreeCtrl * tree_ctrl, wxTreeItemId idItem)
{
	wxString szItemName, szNodePath = wxEmptyString;

	// アイテムの名前を取得。
	while ((szItemName = tree_ctrl->GetItemText(idItem)))
	{
		if (szItemName == wxT("-----"))
		{
			break;
		}

		// パスに追加。
		szNodePath = szItemName + wxFileName::GetPathSeparator() + szNodePath;

		// 親アイテムに遡る。
		idItem = tree_ctrl->GetItemParent(idItem);
		if (idItem == tree_ctrl->GetRootItem())
		{
			// ここにくるのは書庫内全表示時。
			szNodePath = wxT("*");
			break;
		}
	}

	return (szNodePath.Find(wxFileName::GetPathSeparator()) == wxNOT_FOUND) ? szNodePath : szNodePath.BeforeLast(wxFileName::GetPathSeparator());
}

//******************************************************************************
//    ダイアログプロシージャ
//******************************************************************************

bool g_fCancel = false;
ProcessDialog * g_procDlg = NULL;

int __stdcall TPICallbackProc(unsigned int _uMsg, void * _pStructure)
{
	if (_uMsg != TPI_NOTIFY_COMMON)
	{
		return TPI_CALLBACK_UNSUPPORTED;
	}

	TPI_PROCESSINFO * piInfo = (TPI_PROCESSINFO *) _pStructure;
	if (g_procDlg == NULL || piInfo == NULL || piInfo->uMessage != TPI_MESSAGE_STATUS || ! g_procDlg->IsShown())
	{
		return TPI_CALLBACK_CONTINUE;
	}

	switch (piInfo->uStatus)
	{
	case TPI_STATUS_OPENARCHIVE:
		g_procDlg->ebSource->ChangeValue(piInfo->fiInfo.fnFileName.GetFullPath());
		break;
	case TPI_STATUS_BEGINPROCESS:
		g_procDlg->ebTarget->ChangeValue(piInfo->fiInfo.fnFileName.GetFullPath());
		g_procDlg->gauge->SetRange(piInfo->fiInfo.llUnpackedSize.ToULong());
		g_procDlg->gauge->SetValue(0);
		break;
	case TPI_STATUS_INPROCESS:
		g_procDlg->gauge->SetValue(piInfo->llProcessedSize.ToULong());
		g_procDlg->Update();
		break;
	case TPI_STATUS_ENDPROCESS:
		g_procDlg->gauge->SetValue(piInfo->fiInfo.llUnpackedSize.ToULong());
		break;
	// 書庫ロード時用の独自仕様。
	case 0x1000:
		g_procDlg->ebSource->ChangeValue(piInfo->fiInfo.fnFileName.GetFullPath());
		g_procDlg->gauge->SetRange(piInfo->fiInfo.llUnpackedSize.ToULong());
		g_procDlg->gauge->SetValue(0);
		break;
	case 0x1001:
		g_procDlg->ebTarget->ChangeValue(piInfo->fiInfo.fnFileName.GetFullPath());
		g_procDlg->Update();
		break;
	case 0x1002:
		g_procDlg->gauge->SetValue(piInfo->llProcessedSize.ToULong());
		break;
	}

	return g_fCancel ? TPI_CALLBACK_CANCEL : TPI_CALLBACK_CONTINUE;
}

int wxCALLBACK ListViewCompareProc(long i1, long i2, long col)
{
	TPI_FILEINFO item1 = * (TPI_FILEINFO *) i1, item2 = * (TPI_FILEINFO *) i2;
	switch (col)
	{
	case 0:// Filename
		return ::wxStricmp(item1.fnFileName.GetFullName(), item2.fnFileName.GetFullName());
	case 1:// Unpacked
		return (item1.llUnpackedSize < item2.llUnpackedSize) ? -1 : ((item1.llUnpackedSize > item2.llUnpackedSize) ? 1 : 0);
	case 2:// Packed
		return (item1.llPackedSize < item2.llPackedSize) ? -1 : ((item1.llPackedSize > item2.llPackedSize) ? 1 : 0);
	case 3:// Ratio
		return (item1.wCompressRatio < item2.wCompressRatio) ? -1 : ((item1.wCompressRatio > item2.wCompressRatio) ? 1 : 0);
	case 6:// Last Modified
		return (item1.tmModified < item2.tmModified) ? -1 : ((item1.tmModified > item2.tmModified) ? 1 : 0);
	case 7:// Path
		return ::wxStricmp(item1.fnFileName.GetPath(), item2.fnFileName.GetPath());
	case 8:// Type
		{

			wxString s1, s2;
			wxMimeTypesManager mimeFile;
			s1 = item1.fnFileName.GetExt().MakeUpper() + wxT(" file");
			wxFileType * ftFile = mimeFile.GetFileTypeFromExtension(item1.fnFileName.GetExt());
			if (ftFile)
			{
				ftFile->GetDescription(& s1);
			}
			s2 = item2.fnFileName.GetExt().MakeUpper() + wxT(" file");
			ftFile = mimeFile.GetFileTypeFromExtension(item2.fnFileName.GetExt());
			if (ftFile)
			{
				ftFile->GetDescription(& s2);
			}
			return ::wxStricmp(s1, s2);
		}
	case 9:// No
		return (item1.llFileID < item2.llFileID) ? -1 : ((item1.llFileID > item2.llFileID) ? 1 : 0);
	default:
		return 0;
	}
}

//******************************************************************************
//    諸処理関数
//******************************************************************************

// ディレクトリを新規に作成。
wxFileName MakeDirPath(wxFileName fnPath, wxString szBase, bool bAct)
{
	// とりあえず作ってみる。
	wxFileName fnDir = fnPath;
	fnDir.AppendDir(szBase);
	if (! fnDir.DirExists() && ! ::wxFileExists(fnDir.GetPath()))
	{
		if (! bAct || fnDir.Mkdir())
		{
			return fnDir;
		}
	}

	// ダメなら"_%d"を付加して試行。
	for (int i = 0; i < 255; i++)
	{
		wxString s;
		s.Printf(wxT("_%d"), i);
		fnDir = fnPath;
		fnDir.AppendDir(szBase + s);
		if (! fnDir.DirExists() && ! ::wxFileExists(fnDir.GetPath()))
		{
			if (! bAct || fnDir.Mkdir())
			{
				return fnDir;
			}
		}
	}

	// エラー終了。
	fnDir.Clear();
	return fnDir;
}

wxArrayString MakeTargetFileList(MainFrame * frm)
{
	wxArrayString as;
	for (int i = 0; i < frm->list_ctrl->GetItemCount() && as.GetCount() < (unsigned) frm->list_ctrl->GetSelectedItemCount(); i++)
	{
		if (frm->list_ctrl->GetItemState(i, wxLIST_STATE_SELECTED) & wxLIST_STATE_SELECTED)
		{
			wxListItem item;
			item.SetId(i);
			item.SetColumn(9);
			item.SetMask(wxLIST_MASK_TEXT);
			frm->list_ctrl->GetItem(item);
			long l = 0;
			item.GetText().ToLong(& l);
			as.Add(frm->fileinfo[l].szStoredName);
		}
	}

	return as;
}

bool WillMakeDirByArcName(MainFrame * frm, MakeDialog * mk)
{
	if (mk->chDirMake->GetSelection() != 2)
	{
		return mk->chDirMake->GetSelection() == 1;
	}

	// ルートにディレクトリとファイル合わせて2個以上あるとき、ディレクトリを作成。
	switch (frm->tree_ctrl->GetChildrenCount(frm->tree_ctrl->GetLastChild(frm->tree_ctrl->GetRootItem()), false))
	{
	case 0:
	{
		// ディレクトリがないので、ルートにファイルが2個以上あるかどうかを判定。
		bool fFlag = false;
		for (size_t i = 0; i < frm->fileinfo.GetCount(); i++)
		{
			if (frm->fileinfo[i].fnFileName.GetPath().IsEmpty())
			{
				// ファイルはルートにある。
				if (fFlag)
				{
					return true;
				}

				fFlag = true;
			}
		}
		break;
	}
	case 1:
		// ディレクトリが1個なので、ルートにファイルがないかどうかを判定。
		for (size_t i = 0; i < frm->fileinfo.GetCount(); i++)
		{
			if (frm->fileinfo[i].fnFileName.GetPath().IsEmpty())
			{
				return true;
			}
		}
		break;
	default:
		// ディレクトリだけで2個以上あるので、自動的に展開先ディレクトリを作成。
		return true;
	}

	return false;
}
