/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2015/11/15
/// 
/// @brief  Win32 コンテナ。
/// 
/// ※ 複数のコンポーネントを管理する
/// 
/// ****************************************************************************


#include "CPL_WIN_Container.h"
#include "CPL_WIN_TextBox.h"
#include "CPL_WIN_ComboBox.h"
#include "CPL_WIN_ListWindow.h"
#include "CPL_WIN_ToolTip.h"
#include "CPL_WIN_Scrollbar.h"
#include "../Frame/CPL_WIN_TabWindow.h"


namespace CPL {
namespace WIN {


// _____________________________________________________________________________
/// @brief コンストラクタ
/// 
/// in_msg_throw をtrueにすると、次のメッセージはそのまま親に送る。\n
/// WM_COMMAND  WM_NOTIFY  WM_HSCROLL  WM_VSCROLL\n
/// および、CorePLiオリジナルメッセージとユーザー定義メッセージ。
/// 
/// @param[in] in_hdl_win_parent : 親をとなるウィンドウハンドル。
/// @param[in] in_pos   : コンテナの左上隅の座標を指定します。
/// @param[in] in_size  : コンテナサイズを指定します。
/// @param[in] in_msg_throw
///   : true  - 自身のイベントメッセージを親にも転送する。\n
///   : false - 自身のイベントメッセージを親にも転送しない。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Container::Container
( _in<HWND     > in_hdl_win_parent
, _in<td_Coord2> in_pos
, _in<td_Coord2> in_size
, _in<bool     > in_msg_throw
) : m_flag_message_throw( in_msg_throw )
{
  Create( in_hdl_win_parent, in_pos, in_size );
}


// _____________________________________________________________________________
/// @brief デフォルトプロシージャ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF Container::DefaultProc_( _in<UINT> in_message, _in<WPARAM> in_wparam, _in<LPARAM> in_lparam )
{
  if( m_flag_message_throw )
  {
    if( in_message == WM_COMMAND   ||
        in_message == WM_NOTIFY    ||
        in_message == WM_HSCROLL   ||
        in_message == WM_VSCROLL   ||
        in_message >= kID_CPL )
    {
      return (SendParentMessage( in_message, in_wparam, in_lparam ) != 0);
    }
    else
    {
      return Component::DefaultProc_( in_message, in_wparam, in_lparam );
    }
  }
  else
  {
    switch( in_message )
    {
    case WM_COMMAND: // コマンド
      if( GetEventMode() != eEventMode::ChildCut )
      {
        auto ID          = LOWORD(in_wparam);
        auto notify_code = HIWORD(in_wparam);
        CallStackMsg csm( this, in_message, in_wparam, in_lparam );
        Component* ptr_comp = GetHandleToPointer((HWND)in_lparam);
        if( ptr_comp )
        {
          if( notify_code == EN_UPDATE ) // テキストボックス系処理
          {
            TextBox* ptr_text_box = dynamic_cast<TextBox*>(ptr_comp);
            if( ptr_text_box )
            {
              return On_TextChange( ptr_text_box );
            }
          }
          else
          if( notify_code == CBN_SELCHANGE )
          {
            ComboBox* ptr_combo_box = dynamic_cast<ComboBox*>(ptr_comp);
            if( ptr_combo_box )
            {
              return On_ComboSelect( ptr_combo_box );
            }
            ListBox* ptr_list_box = dynamic_cast<ListBox*>(ptr_comp);
            if( ptr_list_box )
            {
              return On_ListSelect( ptr_list_box );
            }
          }
        }
        return On_Command( ID, notify_code, (HWND)in_lparam );
      }
      break;

    case WM_NOTIFY:
      if( GetEventMode() != eEventMode::ChildCut )
      {
        CallStackMsg csm( this, in_message, in_wparam, in_lparam );
        NMHDR* ptr_nmhdr = (NMHDR*)in_lparam;
        if( ptr_nmhdr->code == TCN_SELCHANGE )
        {
          TabWindow* ptr_tab_win = dynamic_cast<TabWindow*>(GetHandleToPointer(ptr_nmhdr->hwndFrom));
          if( ptr_tab_win )
          {
            if( ptr_tab_win->IsAutoTabs() )
            {
              ptr_tab_win->ShowSelectFormOnly();
            }
            return On_TabChange( ptr_tab_win );
          }
          else
          {
            return On_Notify( ptr_nmhdr );
          }
        }
        else
        {
          return On_Notify( ptr_nmhdr );
        }
      }
      break;

    case WM_VSCROLL: // 縦スクロール
    case WM_HSCROLL: // 横スクロール
      if( GetEventMode() != eEventMode::ChildCut )
      {
        CallStackMsg csm( this, in_message, in_wparam, in_lparam );
        ScrollBar* ptr_scroll = dynamic_cast<ScrollBar*>(GetHandleToPointer( (HWND)in_lparam ));
        if( ptr_scroll  &&  ptr_scroll->GetAuto() )
        {
          switch( (int)LOWORD(in_wparam) )
          {
          case SB_LEFT:
          case SB_LINELEFT:   ptr_scroll->AddPos( -1 ); break;
          case SB_RIGHT:
          case SB_LINERIGHT:  ptr_scroll->AddPos(  1 ); break;
          case SB_PAGELEFT:   ptr_scroll->AddPos( -ptr_scroll->GetPage() ); break;
          case SB_PAGERIGHT:  ptr_scroll->AddPos(  ptr_scroll->GetPage() ); break;
          case SB_THUMBTRACK: // 操作中
            {
              SCROLLINFO si;
              ::memset( &si, 0, sizeof(si) );
              si.cbSize = sizeof(si);
              si.fMask = SIF_TRACKPOS;
              ::GetScrollInfo( (HWND)in_lparam, SB_CTL, &si );
              ptr_scroll->SetPos( si.nTrackPos );
            }
            break;
          }
          ::ShowScrollBar( ptr_scroll->GetWindowHandle(), SB_CTL, true );
        }
//        return On_Scroll( (HWND)in_lparam, (int)LOWORD(in_wparam), (int)HIWORD(in_wparam) );
        return On_Scroll( ptr_scroll, (int)LOWORD(in_wparam) );
      }
      break;

    case WM_DROPFILES: // ドラッグ＆ドロップ
      {
        CallStackMsg csm( this, in_message, in_wparam, in_lparam );
        str_type str[256];
        DropFileControl dfc_str_list;
        HDROP hdl_drop = (HDROP)in_wparam;       // HDROPを取得
        int num = ::DragQueryFile( hdl_drop, -1, nullptr, 0 ); // ファイル数を取得
        dfc_str_list.Resize( num ); // 文字列バッファ数確保
        for( int i = 0; i < num; ++i )
        {
          DragQueryFile( hdl_drop, i, str, CPL_ARRAY_NUM(str) ); // ファイル名を取得
          dfc_str_list[ i ] = str;
        }

        DragFinish( hdl_drop ); // ドラッグ＆ドロップ終了処理

        return On_DropFile( dfc_str_list );

      }
      break;

    case WM_MENUSELECT: // メニュー選択
      {
        CallStackMsg csm( this, in_message, in_wparam, in_lparam );
        return
          On_MenuSelect
            ( (HMENU)in_lparam
            , LOWORD(in_wparam)
            , HIWORD(in_wparam)
            );
      }

    // CPLオリジナルメッセージ
    case kWM_CPL_BTN_CLICK: // ボタンクリック
      {
        CallStackMsg csm( this, in_message, in_wparam, in_lparam );
        return On_Button( reinterpret_cast<Button*>(in_lparam), (int)in_wparam );
      }

    case kWM_CPL_GROUPBOX_EXPAND:
      {
        CallStackMsg csm( this, in_message, in_wparam, in_lparam );
        return On_ExpandGroupBox( reinterpret_cast<GroupBox*>(in_lparam) );
      }

    default:
      break;
    }

    return Component::DefaultProc_( in_message, in_wparam, in_lparam );
  }
}


// _____________________________________________________________________________
/// @brief 全ての子コンポーネントを含むサイズを取得。
/// 
/// @param[in] in_flag_show
///   : 非表示コンポーネントを含めるかどうか。\n
///   : true  - 含める。\n
///   : false - 含めない。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
td_Coord2 Container::GetAdjustSize( _in<BOOL> in_flag_show ) const
{
  ADJUST_ data( m_hdl_window, in_flag_show );
  ::EnumChildWindows( m_hdl_window, EnumChildProc_AdjustSize, (LPARAM)&data );
  return { data.size_x, data.size_y };
}


// _____________________________________________________________________________
/// @brief 自動サイズ調整
/// 
/// @param[in] in_offs_size
///   : 最小サイズからのオフセットサイズ。
/// @param[in] in_show
///   : 非表示コンポーネントを含めるかどうか。\n
///   : true  - 含める。\n
///   : false - 含めない。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Container::AdjustSize( _in<td_Coord2> in_offs_size, _in<BOOL> in_show )
{
  SetWindowSize( GetAdjustSize(in_show)+in_offs_size, true, true );
}


// _____________________________________________________________________________
/// @brief アドジャスト用の子供ウィンドウ列挙用コールバック関数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BOOL
  CALLBACK Container::EnumChildProc_AdjustSize
    ( HWND   in_hdl_window
    , LPARAM in_lparam
    )
{
  ADJUST_* ptr_adjust_data = reinterpret_cast<ADJUST_*>(in_lparam);
  LONG_PTR style = GetWindowStyle( in_hdl_window );
  if( ptr_adjust_data->hdl_win_parent == ::GetParent(in_hdl_window)  &&
      (ptr_adjust_data->view  ||  (style & WS_VISIBLE) ) )
  {
    Rect  rt;
    ::GetWindowRect( in_hdl_window, &rt );
    ::MapWindowPoints( ::GetDesktopWindow(), ::GetParent(in_hdl_window), (POINT*)&rt, 2 );
    if( ptr_adjust_data->size_x < rt.right  ){ ptr_adjust_data->size_x = rt.right;  }
    if( ptr_adjust_data->size_y < rt.bottom ){ ptr_adjust_data->size_y = rt.bottom; }
  }
  return TRUE;
}



// _____________________________________________________________________________
/// @brief 有効化設定
/// 
/// ※ 子供の設定も変えられる（見た目変更のため）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Container::SetEnable( _in<bool> in_flag_enable )
{
  ::EnumChildWindows
    ( m_hdl_window
    , EnumChildProc_SetEnable
    , (LPARAM)in_flag_enable
    );
  Component::SetEnable( in_flag_enable );
}


// _____________________________________________________________________________
/// @brief 有効化設定用の子供ウィンドウ列挙用コールバック関数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BOOL CALLBACK
  Container::EnumChildProc_SetEnable
    ( HWND   in_hdl_window
    , LPARAM in_lparam
    )
{
  ::EnableWindow( in_hdl_window, (BOOL)in_lparam );
  return TRUE;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// イベント制御設定
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Container::SetEventMode( _in<eEventMode> in_evt_mode )
{
  ::EnumChildWindows( m_hdl_window, EnumChildProc_SetEventMode, (LPARAM)in_evt_mode );
  Component::SetEventMode( in_evt_mode );
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// イベント制御用の子供ウィンドウ列挙用コールバック関数
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BOOL CALLBACK
  Container::EnumChildProc_SetEventMode( HWND in_hdl_window, LPARAM in_lparam )
{
  Component* ptr_comp = GetHandleToPointer(in_hdl_window);
  if( ptr_comp )
  {
    ptr_comp->SetEventMode( static_cast<eEventMode>(in_lparam) );
  }
  return TRUE;
}



} // namespace WIN
} // namespace CPL