/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/11/25
/// 
/// @brief  ファイルリスト管理。
/// 
/// ****************************************************************************



#include "CPL_WIN_FileList.h"

#include <Shlwapi.h>
#pragma comment(lib, "shlwapi.lib")


namespace CPL {
namespace WIN {


// _____________________________________________________________________________
/// @brief DirectoryInfo デフォルトコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DirectoryInfo::DirectoryInfo()
  : m_file_num(0)
  , m_folder_num(0)
  , m_all_num(0)
{}


// _____________________________________________________________________________
/// @brief コピー
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DirectoryInfo& DirectoryInfo::operator = ( DirectoryInfo const& in_obj )
{
  (FileInfo&)*this = (FileInfo&)in_obj;
  m_file_num       = in_obj.m_file_num;
  m_folder_num     = in_obj.m_folder_num;
  m_all_num        = in_obj.m_all_num;

  return *this;
}


// _____________________________________________________________________________
/// @brief ファイルリストの
/// 
/// 管理しているディレクトリ下に指定したファイルが存在するかチェックします。
/// 
/// @param[in] in_path
///   : 管理するディレクトリまたはファイルのパスを指定します。\n
///   : ファイル名にはワイルドカード(?,*)が使用可です。
/// 
/// @return 
/// 下層を含む全ての該当するファイル数を返します。\n
/// 該当なし、もしくはフォルダでなければ-1を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int DirectoryInfo::SetupDirectoryInfo( str_cptr in_path )
{
  HANDLE          handle;
  WIN32_FIND_DATA w32fd;

  // フォルダチェック
  bool f_folder = PathIsDirectory( in_path ) != 0;
  if( !f_folder )
  {
    return -1;
  }
  m_file_path = in_path;
  if( (*m_file_path.GetEnd() != SwS('\\')) ) // フォルダには必ず区切り文字列をつける。
  {
    m_file_path += SwS("\\");
  }

  m_file_num   = 0;
  m_folder_num = 0;
  m_all_num    = 0;

  Core::String check_path = m_file_path;
  check_path += SwS("*");  // 全て検査対象

  try{
    m_sp_file_head = nullptr; // すでに何らかが確保されていたら解放する。

    handle = FindFirstFile( check_path, &w32fd );
    if( handle == INVALID_HANDLE_VALUE )
    {
      return 0;
    }

    sdptr<FileInfo>* pp_file_info = &m_sp_file_head;
    do{
//      if( _tcscmp( w32fd.cFileName, SwS(".") ) == 0  ||  _tcscmp( w32fd.cFileName, SwS("..") ) == 0 )
      if( Core::String::str_cmp_( w32fd.cFileName, SwS(".") ) == 0  ||
          Core::String::str_cmp_( w32fd.cFileName, SwS("..") ) == 0 )
      {
        continue;
      }

      if( w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) // ディレクトリチェック。
      {
        *pp_file_info = DirectoryInfo::Create( m_file_path + w32fd.cFileName );
        m_folder_num++;
      }
      else // ファイルの場合
      {
        *pp_file_info = CPL_MAKE_SHARED(FileInfo);
        (*pp_file_info)->m_file_path        = m_file_path + w32fd.cFileName;
        (*pp_file_info)->m_ptr_parent_class = this;
        m_file_num++;
      }

      m_all_num++;
      pp_file_info = &(*pp_file_info)->m_sp_next_file;
    } while( FindNextFile( handle, &w32fd ) );

    FindClose( handle );
    return m_all_num;
  }
  catch(...)
  {
    return 0;
  }
}


// _____________________________________________________________________________
/// @brief ファイル検索（ファイル検査後のみ有効）
/// 
/// @param[in] in_name : 検索するファイル名を指定します。
/// 
/// @return
/// 該当したファイルオブジェクトのポインタを返します。\n
/// 該当しない場合nullptrが返ります。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<FileInfo> DirectoryInfo::Find( pin<str_type> in_name )
{
  sdptr<FileInfo> sp_file_check = m_sp_file_head;

  while( sp_file_check )
  {
    if( sp_file_check->m_file_path == in_name )
    {
      return sp_file_check;
    }
    if( auto* p_di = SmartCast<DirectoryInfo*>(sp_file_check) ) // DirectoryInfoならば
    {
      auto sp_file_target = p_di->Find( in_name );
      if( sp_file_target )
      {
        return sp_file_target;
      }
    }
    sp_file_check = sp_file_check->m_sp_next_file;
  }

  return nullptr;
}

/// @copydoc DirectoryInfo::Find
sdptr<FileInfo> DirectoryInfo::Find( pin<str_type> in_name ) const
{
  sdptr<FileInfo> sp_file_check = m_sp_file_head;

  while( sp_file_check )
  {
    if( sp_file_check->m_file_path == in_name )
    {
      return sp_file_check;
    }
    if( auto* p_di = SmartCast<DirectoryInfo const*>(sp_file_check) ) // DirectoryInfoならば
    {
      sdptr<FileInfo>
        sp_file_target = p_di->Find( in_name );
      if( sp_file_target )
      {
        return sp_file_target;
      }
    }
    sp_file_check = sp_file_check->m_sp_next_file;
  }

  return nullptr;
}


// _____________________________________________________________________________
/// @brief FileInfoファクトリ
/// 
/// @param[in] in_path : ディレクトリ(フォルダ)パスを指定します。
/// 
/// @return 作成されたFileInfoオブジェクトのポインタを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<FileInfo> FileInfo::Create( pin<str_type> in_path )
{
  if( PathIsDirectory( in_path ) )
  {
    auto sp_dir_info = CPL_MAKE_SHARED(DirectoryInfo);
    if( sp_dir_info->SetupDirectoryInfo( in_path ) < 0 )
    {
      return nullptr;
    }
    return sp_dir_info;
  }
  else
  {
    auto sp_file_info = CPL_MAKE_SHARED(FileInfo);
    sp_file_info->m_file_path = in_path;

    return sp_file_info;
  }
}


// _____________________________________________________________________________
/// @brief コピー
/// 
/// @param[in] in_obj : コピー元のFileInfoオブジェクトを指定します。
/// 
/// @return 自身の参照を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FileInfo& FileInfo::operator = ( cref<FileInfo> in_obj )
{
  m_file_path         = in_obj.m_file_path;
  m_sp_next_file      = in_obj.m_sp_next_file;
  m_ptr_parent_class  = in_obj.m_ptr_parent_class;

  return *this;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// フルパスの取得。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//int FileInfo::GetFullPath( str_ptr out_full_path, int in_buf_length ) const
//{
//  Core::String get_path;
//  GetFullPath( get_path );
//
//  int copy_num = Min<int>( get_path.GetLength(), in_buf_length-1 );
//  Core::String::str_ncpy_( out_full_path, &get_path[0], copy_num );
//  // null文字設定。
//  out_full_path[ copy_num ] = 0;
//
//  return copy_num;
//}
//
//
//// _____________________________________________________________________________
//// フルパスの取得。
//// _____________________________________________________________________________
//int FileInfo::GetFullPath( Core::String& out_full_path ) const
//{
//  if( GetClass() )
//  {
//    out_full_path = GetClass()->GetDirectoryName();
//    int len = out_full_path.GetLength();
//    if( out_full_path[ len ] != SwS('\\') )
//    {
//      out_full_path += SwS("\\");
//    }
//    out_full_path += this->m_file_path;
//  }
//  else
//  {
//    out_full_path = m_file_path;
//  }
//
//  return out_full_path.GetLength();
//}





} // namespace WIN
} // namespace CPL

