/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/12/17
/// 
/// @brief  リストウィンドウクラス。
/// 
/// ****************************************************************************


#include "CPL_WIN_ListWindow.h"


namespace CPL {
namespace WIN {


// _____________________________________________________________________________
/// @brief コンストラクタ　リストボックス生成
/// 
/// @copydetails doxycomment::arg_in_hdl_win_parent
/// @copydetails doxycomment::arg_in_pos
/// @copydetails doxycomment::arg_in_size
/// @copydetails doxycomment::arg_in_win_style
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ListBox::ListBox
( _in<HWND     > in_hdl_win_parent
, _in<td_Coord2> in_pos
, _in<td_Coord2> in_size
, _in<STYLE    > in_win_style
) : Component( 1 )
{
  bool flag_create =
    zzf_Create
      ( in_hdl_win_parent
      , SwS("")
      , in_pos
      , in_size
      , -1
      , in_win_style.style
      , in_win_style.ExStyle.style
      , this
      );
  if( !flag_create )
  {
    CPL_THROW( CPL_WIN_MSG_NOT_CREATE_LISTBOX );// 作成失敗
  }
  Release();

  // サブクラス化
  if( !zzf_LISTBOX_PROC() )
  {
    zzf_LISTBOX_PROC() = zzf_GetProc( m_hdl_window );
  }
  zzf_SetProc( zzf_StaticProcListBox );
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// リストボックス　サブクラス化用プロシージャ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LRESULT CALLBACK
  ListBox::zzf_StaticProcListBox
    ( HWND   in_hdl_window
    , UINT   in_message
    , WPARAM in_wparam
    , LPARAM in_lparam
    )
{
  ListBox* in_ptr_list_box = dynamic_cast<ListBox*>(GetHandleToPointer(in_hdl_window));
  bool ret = false;
  if( in_ptr_list_box  &&  in_ptr_list_box->GetEventMode() != eEventMode::AllCut )
  {
    ret = in_ptr_list_box->DefaultProc_( in_message, in_wparam, in_lparam ) != 0;
  }
  if( !ret )
  {
    return ::CallWindowProc( zzf_LISTBOX_PROC(), in_hdl_window, in_message, in_wparam, in_lparam );
  }
  return 0;
}



// _____________________________________________________________________________
/// @brief 指定した位置の項目を選択。
/// 
/// @param[in] in_index
///   : 選択する項目のインデックスを指定します。
/// 
/// @retval true  : 成功。
/// @retval false : 失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ListBox::SetIdx( _in<int> in_index )
{
  //if( in_flag_send_or_post )
  //{
  //  return PostMessage( m_hdl_window, LB_SETCURSEL, (WPARAM)in_index, 0L ) != 0;
  //}
  if( SendMessage( m_hdl_window, LB_SETCURSEL, (WPARAM)in_index, 0L ) == LB_ERR )
  {
    return false;
  }
  return true;
}


// _____________________________________________________________________________
/// @brief アイテムの追加
/// 
/// @param[in] in_index   : 追加する位置のインデックスで指定ます。
/// @param[in] in_ptr_str : 追加するアイテムの文字列を指定します。
/// 
/// @retval true  : 追加成功。
/// @retval false : 追加失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ListBox::AddItem( _in<int> in_index, pin<char> in_ptr_str )
{
  auto res_send_msg =
    SendMessageA
      ( m_hdl_window
      , LB_INSERTSTRING
      , (WPARAM)in_index
      , (LPARAM)in_ptr_str
      );
  if( res_send_msg == LB_ERR )
  {
    return false;
  }
  SetIdx( (in_index == -1) ? GetNum()-1 : in_index );
  return true;
}
/// @copydoc CPL::WIN::ListBox::AddItem(_in<int>,pin<char>)
bool ListBox::AddItem( _in<int> in_index, pin<wchar_t> in_ptr_str )
{
  auto res_send_msg =
    SendMessageW
      ( m_hdl_window
      , LB_INSERTSTRING
      , (WPARAM)in_index
      , (LPARAM)in_ptr_str
      );
  if( res_send_msg == LB_ERR )
  {
    return false;
  }
  SetIdx( (in_index == -1) ? GetNum()-1 : in_index );
  return true;
}


// _____________________________________________________________________________
/// @brief 選択されているNoの取得
/// 
/// @return 選択中のアイテムのインデックスを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int  ListBox::GetIdx() const
{
  return (int)(DWORD)SendMessage( m_hdl_window, LB_GETCURSEL, 0L, 0L);
}


// _____________________________________________________________________________
/// @brief 項目数を取得
/// 
/// @return 現在の登録アイテム数を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int  ListBox::GetNum() const
{
  return (int)(DWORD)SendMessage( m_hdl_window, LB_GETCOUNT, 0L, 0L );
}


// _____________________________________________________________________________
/// @brief 指定したインデックスのアイテム名の取得
/// 
/// @param[in] in_index : 取得するアイテムのインデックスを指定します。
/// @param[out] out_ptr_str : 文字列取得用のバッファアドレスを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  ListBox::GetItemString
    ( _in <int > in_index
    , pout<char> out_ptr_str
    ) const
{
  char tmp_str[256];
  SendMessageA( m_hdl_window, LB_GETTEXT, (WPARAM)in_index, (LPARAM)tmp_str );
  Core::StringA::str_cpy_( out_ptr_str, tmp_str );
}
/// @copydoc CPL::WIN::ListBox::GetItemString(_in<int>,pout<char>)const
void
  ListBox::GetItemString
    ( _in <int    > in_index
    , pout<wchar_t> out_ptr_str
    ) const
{
  wchar_t tmp_str[256];
  SendMessageW( m_hdl_window, LB_GETTEXT, (WPARAM)in_index, (LPARAM)tmp_str );
  Core::StringW::str_cpy_( out_ptr_str, tmp_str );
}




// _____________________________________________________________________________
/// @brief アイテムの全消去
/// 
/// @retval true  : 成功。
/// @retval false : 失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ListBox::Release()
{
  //if( in_flag_send_or_post )
  //{
  //  return PostMessage( m_hdl_window, LB_RESETCONTENT, 0L, 0L ) != 0;
  //}
  if( SendMessage( m_hdl_window, LB_RESETCONTENT, 0L, 0L ) == LB_ERR )
  {
    return false;
  }
  return true;
}



// _____________________________________________________________________________
/// @brief 指定インデックスのアイテムを削除
/// 
/// @param[in] in_index
///   : 削除する項目のインデックスを指定します。
/// 
/// @retval true  : 削除成功。
/// @retval false : 削除失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ListBox::DeleteItem( _in<int> in_index )
{
  //if( in_flag_send_or_post )
  //{
  //  if( PostMessage( m_hdl_window, LB_DELETESTRING, (WPARAM)in_index, 0 ) == 0 )
  //  {
  //    return false;
  //  }
  //}
  if( SendMessage( m_hdl_window, LB_DELETESTRING, (WPARAM)in_index, 0 ) == LB_ERR )
  {
    return false;
  }
  SetIdx( in_index );

  return true;
}



// _____________________________________________________________________________
/// @brief 指定したアイテムの変更
/// 
/// @param[in] in_index  : 変更するアイテムのインデックスを指定します。
/// @param[in] in_string : 新しい文字列を指定します。
/// 
/// @retval true  : 変更成功。
/// @retval false : 変更失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ListBox::ChangeItemString( _in<int> in_index, pin<char> in_string )
{
  if( !DeleteItem( in_index )  ||  // 削除
      !AddItem( in_index, in_string ) )  // 追加
  {
    return false;
  }
  SetIdx( in_index );
  return true;
}
/// @copydoc CPL::WIN::ListBox::ChangeItemString
bool ListBox::ChangeItemString( _in<int> in_index, pin<wchar_t> in_string )
{
  if( !DeleteItem( in_index ) ||         // 削除
      !AddItem( in_index, in_string ) )     // 追加
  {
    return false;
  }
  SetIdx( in_index );
  return true;
}


// _____________________________________________________________________________
/// @copydoc CPL::WIN::Component::CreateClone
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component*
  ListBox::CreateClone
    ( _in<HWND> in_hdl_win_parent
    , _in<int > in_offs_x
    , _in<int > in_offs_y
    ) const
{
  try{
    auto hdl_win_parent = in_hdl_win_parent;
    Rect rt;
    LONG_PTR style = this->GetStyle();
    this->GetWindowRect( rt );
    if( hdl_win_parent == nullptr )
    {
      hdl_win_parent = this->GetParentWindowHandle();
    }

    ListBox*
      ptr_list_box =
        CPL_NEW(ListBox)
          ( hdl_win_parent
          , td_Coord2( int(rt.left + in_offs_x), int(rt.top + in_offs_y) )
          , td_Coord2( int(rt.GetWidth()), int(rt.GetHeight()) )
          , style
          );
    if( ptr_list_box )
    {
      ptr_list_box->SetFont( this->GetFont() );
      return ptr_list_box;
    }
    else
    {
      return nullptr;
    }
  }
  catch(...)
  {
    return nullptr;
  }
}


// _____________________________________________________________________________
/// @brief 再描画設定
/// 
/// @param[in] in_flag_redraw
///   : false - 描画を抑制します。
///   : true  - 描画を許可します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ListBox::SetupRedraw( _in<bool> in_flag_redraw )
{
  DefWindowProc( GetWindowHandle(), WM_SETREDRAW, in_flag_redraw, 0 );
}




// *****  ListWindow クラス  ****************************************

// _____________________________________________________________________________
/// @brief コンストラクタ
/// 
/// @param[in] in_hdl_win_parent
///   : 親のウィンドウハンドルを指定します。\n
///   : nullptrを指定すると、ルートのウィンドウとなります。
/// @param[in] in_pos
///   : ウィンドウの左上隅の座標を指定します。
/// @param[in] in_size
///   : ウィンドウまたはクライアントのサイズを指定します。
/// @param[in] in_win_style
///   : ウィンドウスタイル。詳しくはSTYLEを参照してください。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ListWindow::ListWindow
( _in<HWND     > in_hdl_win_parent
, _in<td_Coord2> in_pos
, _in<td_Coord2> in_size
, _in<STYLE    > in_win_style
) : m_ptr_list_box( nullptr )
{
  // ウィンドウ作成
  if( !Create( in_hdl_win_parent, in_pos, in_size, false, in_win_style ) )
  {
    CPL_THROW( CPL_WIN_MSG_NOT_CREATE_LISTWINDOW );
  }

  auto size_client = GetClientSize();
  try{
    // リストボックス作成
    m_ptr_list_box = CPL_NEW(ListBox)( m_hdl_window, td_Coord2(0,0), size_client );
    m_ptr_list_box->Show( true );
  }
  catch(...)
  {
    CPL_THROW( CPL_WIN_MSG_NOT_CREATE_LISTWINDOW );
  }

  m_ptr_list_box->Release();
}


// _____________________________________________________________________________
/// @brief デストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ListWindow::~ListWindow()
{
  DEL( m_ptr_list_box );
}



// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// サイズ変更
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF ListWindow::On_Size( _in<eSizeMode> in_size_mode )
{
  if( m_ptr_list_box )
  {
    auto size_client = GetClientSize();
    m_ptr_list_box->SetWindowSize( size_client );
    SetWindowSize( m_ptr_list_box->GetWindowSize(), true, true );
    return true;
  }
  return false;
}


} // namespace WIN
} // namespace CPL
