/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/11/25
/// 
/// @brief  Win32 ボタンコンポーネント
/// 
/// ****************************************************************************


#include "../../Base/CPL_WIN_Mouse.h"
#include "../../GObj/CPL_WIN_DIBitmap.h"
#include "../Frame/CPL_WIN_Window.h"
#include "CPL_WIN_Button.h"



namespace CPL {
namespace WIN {


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ボタンタイプからコンポーネントスタイルを取得。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LONG_PTR Button::zzf_type_to_Style_( U32 in_btn_type )
{
  LONG_PTR win_style = WS_CHILD | WS_VISIBLE;
  if( in_btn_type & eType::PushButton      ){ win_style |= BS_PUSHBUTTON; }
  if( in_btn_type & eType::CheckBox        ){ win_style |= BS_CHECKBOX; }
  if( in_btn_type & eType::RadioBox        ){ win_style |= BS_RADIOBUTTON; }
  if( in_btn_type & eType::CheckButton     ){ win_style |= BS_CHECKBOX | BS_PUSHLIKE; }
  if( in_btn_type & eType::RadioButton     ){ win_style |= BS_RADIOBUTTON | BS_PUSHLIKE; }
  if( in_btn_type & eType::AutoCheckBox    ){ win_style |= BS_AUTOCHECKBOX; }
  if( in_btn_type & eType::AutoRadioBox    ){ win_style |= BS_AUTORADIOBUTTON; }
  if( in_btn_type & eType::AutoCheckButton ){ win_style |= BS_AUTOCHECKBOX | BS_PUSHLIKE; }
  if( in_btn_type & eType::AutoRadioButton ){ win_style |= BS_AUTORADIOBUTTON | BS_PUSHLIKE; }
  if( in_btn_type & eType::ImageButton     ){ win_style |= BS_OWNERDRAW; }

  return win_style;
}


// _____________________________________________________________________________
/// @brief チェック判定
/// 
/// @retval 0 : チェックなし。
/// @retval 1 : チェックあり。
/// @retval -1 : その他エラー等。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int Button::IsCheck() const
{
  switch( SendMessage( GetWindowHandle(), BM_GETCHECK, 0, 0 ) )
  {
  case BST_UNCHECKED: return 0;
  case BST_CHECKED:   return 1;
  default:            return -1;
  }
}


// _____________________________________________________________________________
/// @brief テキストにサイズを合わせる
/// 
/// @param[in] in_offs_size
///   : 自動計算されるサイズに追加する値を指定します。\n
///   : テキストの余白サイズになります。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Button::AdjustSize( _in<td_Coord2> in_offs_size )
{
  int len = ::GetWindowTextLength( GetWindowHandle() );
  Core::Array::Constant<str_type> buf( len + 1 );
  ::GetWindowText
    ( GetWindowHandle()
    , buf.GetTop()
    , static_cast<int>(buf.GetSize())
    );
  auto size_btn = zzf_GetTextSize( buf.GetTop() ); // 文字列の幅と高さを取得
  size_btn += in_offs_size;        // ユーザーオフセットサイズを加算。
  
  // スタイルに応じた調整。
  auto win_style = this->GetStyle();
  if( (win_style & BS_CHECKBOX       ) == BS_CHECKBOX ){ size_btn.x += 20; }
  if( (win_style & BS_AUTORADIOBUTTON) == BS_CHECKBOX ){ size_btn.x += 20; }

  this->SetWindowSize( size_btn ); // サイズ調整
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ボタン用オリジナルプロシージャ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LRESULT CALLBACK
  Button::zzf_StaticProcButton
    ( HWND   in_hdl_window
    , UINT   in_message
    , WPARAM wp
    , LPARAM lp
    )
{
  Button* ptr_button = dynamic_cast<Button*>(GetHandleToPointer(in_hdl_window));
  SDEF flag = false;
  if( ptr_button )
  {
    if( ptr_button->GetEventMode() != eEventMode::AllCut )
    {
      switch( in_message ) // ↓ キャプチャーをすると動作がおかしいので、Component::DefaultProc_()からキャプチャーをぬいた処理
      {
      case WM_LBUTTONDOWN: // マウス左ボタンダウン
        {
          CallStackMsg csm( ptr_button, in_message, wp, lp );
          Mouse::SetPos( in_hdl_window, lp );
          Mouse::downL_();
          flag = ptr_button->On_MouseDown( eMouseButton::Left );
        } break;
      case WM_MBUTTONDOWN: // マウス中ボタンダウン
        {
          CallStackMsg csm( ptr_button, in_message, wp, lp );
          Mouse::SetPos( in_hdl_window, lp );
          Mouse::downM_();
          flag = ptr_button->On_MouseDown( eMouseButton::Middle );
        } break;
      case WM_RBUTTONDOWN: // マウス右ボタンダウン
        {
          CallStackMsg csm( ptr_button, in_message, wp, lp );
          Mouse::SetPos( in_hdl_window, lp );
          Mouse::downR_();
          flag = ptr_button->On_MouseDown( eMouseButton::Right );
        } break;
      case WM_LBUTTONUP: // マウス左ボタンアップ
        {
          CallStackMsg csm( ptr_button, in_message, wp, lp );
          Mouse::SetPos( in_hdl_window, lp );
          flag = ptr_button->On_MouseUp( eMouseButton::Left );
          Mouse::upL_();
        } break;
      case WM_MBUTTONUP: // マウス中ボタンアップ
        {
          CallStackMsg csm( ptr_button, in_message, wp, lp );
          Mouse::SetPos( in_hdl_window, lp );
          flag = ptr_button->On_MouseUp( eMouseButton::Middle );
          Mouse::upM_();
        } break;
      case WM_RBUTTONUP: // マウス右ボタンアップ
        {
          CallStackMsg csm( ptr_button, in_message, wp, lp );
          Mouse::SetPos( in_hdl_window, lp );
          flag = ptr_button->On_MouseUp( eMouseButton::Right );
          Mouse::upR_();
        } break;
      default:
        flag = ptr_button->DefaultProc_( in_message, wp, lp );
      }
    }
    else
    {
      return 0;
    }
  }
  if( !flag )
  {
    return ::CallWindowProc( zzf_BUTTON(), in_hdl_window, in_message, wp, lp );
  }
  return 0;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// マウスダウン
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF Button::On_MouseDown( _in<eMouseButton> in_mouse_btn )
{
  return On_ButtonClick( true );
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// マウスアップ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF Button::On_MouseUp( _in<eMouseButton> in_mouse_btn )
{
  auto size_client = GetClientSize();
  if( 0 <= Mouse::GetX()  &&  Mouse::GetX() <= size_client.x
   && 0 <= Mouse::GetY()  &&  Mouse::GetY() <= size_client.y )
  {
    return On_ButtonClick( false );
  }
  return false;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ボタンＯＮ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF Button::On_ButtonClick( int in_flag_down )
{
  SDEF ret =
    ::SendMessage
        ( GetParentWindowHandle()
        , kWM_CPL_BTN_CLICK
        , in_flag_down
        , reinterpret_cast<LPARAM>(this)
        );
  return ret;
}






// =============================================================================
// イメージ対応ボタン
// =============================================================================

// _____________________________________________________________________________
/// @brief ButtonImageコンストラクタ ボタン生成
/// 
/// @param[in] in_hdl_window_parent : 親ウィンドウのハンドルを指定します。
/// @param[in] in_DIB_ID  : DIBに関連付けるIDを指定します。
/// @param[in] in_ptr_DIB : 使用するDIBのポインタを渡します。
/// @param[in] in_pos  : 左上の座標をtd_CompPos型で指定します。
/// @param[in] in_size : ウィンドウのサイズをtd_CompSize型で指定します。
/// @param[in] in_rect : デフォルトで使用するイメージのUV座標を指定します。
/// @param[in] in_btn_type
///   : ボタンタイプを指定します。\n
///   : 詳しくはeTypeを参照してください。
/// 
/// @return 
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ButtonImage::ButtonImage
( _in<HWND     > in_hdl_window_parent
, _in<int      > in_DIB_ID
, pin<DIBitmap > in_ptr_DIB
, _in<td_Coord2> in_pos
, _in<td_Coord2> in_size
, _in<Rect     > in_rect
, _in<eType    > in_btn_type
) : Button( in_hdl_window_parent, SwS(""), in_pos, in_size, in_btn_type )
{
  if( !m_image_table.Add( in_DIB_ID, (DIBitmap*)in_ptr_DIB ) )
  {
    CPL_THROW( CPL_WIN_MSG_NOT_CREATE_BUTTON );
  }
  m_image_table.Select( in_DIB_ID );

  WIN::Rect rect
  { in_rect.left
  , in_rect.top
  , Min<long>( in_rect.right,  in_rect.left + in_size.x )
  , Min<long>( in_rect.bottom, in_rect.top  + in_size.y )
  };

  if( !m_rect_table.Add( in_DIB_ID, &rect ) )
  {
    CPL_THROW( CPL_WIN_MSG_NOT_CREATE_BUTTON );
  }
  m_rect_table.Select( in_DIB_ID );

  m_up_ID = m_move_ID = m_down_ID = in_DIB_ID;
  SetDrawStateID( eButtonMode::Up );
  SetImageMode( eButtonMode::All );
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 無効化の設定・解除
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ButtonImage::SetInvalid( int in_flag )
{
  SetEnable( !in_flag );
  
  if( in_flag )
  {
    SetDrawStateID( eButtonMode::Invalid );
  }
  else
  {
    SetDrawStateID( eButtonMode::Up );
  }
}

  
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// イメージ追加
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ButtonImage::AddImage( int ID, DIBitmap* in_pDIB )
{
  m_image_table.Add( ID, in_pDIB );
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 矩形情報を追加
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ButtonImage::AddRect( int ID, Rect* in_ptr_rect )
{
  m_rect_table.Add( ID, in_ptr_rect );
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// イメージ削除
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ButtonImage::DelImage( int ID )
{
  m_image_table.Delete( ID );
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 矩形情報の削除
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ButtonImage::DelRect( int ID )
{
  m_rect_table.Delete( ID );
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// イメージ切り替え
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ButtonImage::ChangeImage( int ID )
{
  m_image_table.Select( ID );
}

/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 使用矩形の切り替え
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ButtonImage::SetWindowRect( int ID )
{
  m_rect_table.Select( ID );
}*/

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ButtonImage::SetDrawStateID( short in_state )
{
  m_state = in_state;
  switch( in_state )
  {
  case eButtonMode::Up:      m_rect_table.Select( m_up_ID );      break;
  case eButtonMode::Down:    m_rect_table.Select( m_down_ID );    break;
  case eButtonMode::Move:    m_rect_table.Select( m_move_ID );    break;
  case eButtonMode::Invalid: m_rect_table.Select( m_invalid_ID ); break;
  }
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// イメージ描画処理
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF ButtonImage::On_Paint()
{
  switch( m_state )
  {
  case eButtonMode::Up:
    if( m_mode & eButtonMode::Up )
    {
      On_PaintUp();
    }
    else
    if( m_mode & eButtonMode::Move )
    {
      On_PaintMove();
    }
    else
    if( m_mode & eButtonMode::Down )
    {
      On_PaintDown();
    }
    break;

  case eButtonMode::Down:
    if( m_mode & eButtonMode::Down )
    {
      On_PaintDown();
    }
    else
    if( m_mode & eButtonMode::Move )
    {
      On_PaintMove();
    }
    else
    if( m_mode & eButtonMode::Up )
    {
      On_PaintUp();
    }

  case eButtonMode::Move:
    if( m_mode & eButtonMode::Move )
    {
      On_PaintMove();
    }
    else
    if( m_mode & eButtonMode::Up )
    {
      On_PaintUp();
    }
    else
    if( m_mode & eButtonMode::Down )
    {
      On_PaintDown();
    }
    break;

  default:
    On_PaintInvalid();
    break;
  }
  return true;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ButtonImage::On_PaintUp()
{
  PaintDC pdc( GetWindowHandle() );

  DIBitmap** pp_DIB   = m_image_table.GetActive();
  Rect*      ptr_rect = m_rect_table.GetActive();
  if( pp_DIB  &&  *pp_DIB  &&  ptr_rect )
  {
    DIBitmap::DrawInfo di;
    if( ptr_rect )
    {
      di.rect_source = *ptr_rect;
    }
    (*pp_DIB)->Draw( pdc.GetDC(), di );
  }
  On_PaintUpAfter( pdc.GetDC() );
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ButtonImage::On_PaintDown()
{
  PaintDC pdc( GetWindowHandle() );

  DIBitmap** pp_DIB   = m_image_table.GetActive();
  Rect*      ptr_rect = m_rect_table.GetActive();
  if( pp_DIB  &&  *pp_DIB  &&  ptr_rect )
  {
    DIBitmap::DrawInfo di;
    if( ptr_rect )
    {
      di.rect_source = *ptr_rect;
    }
    (*pp_DIB)->Draw( pdc.GetDC(), di );
  }
  On_PaintDownAfter( pdc.GetDC() );
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ButtonImage::On_PaintMove()
{
  PaintDC pdc( GetWindowHandle() );

  DIBitmap** pp_DIB   = m_image_table.GetActive();
  Rect*      ptr_rect = m_rect_table.GetActive();
  if( pp_DIB  &&  *pp_DIB  &&  ptr_rect )
  {
    DIBitmap::DrawInfo di;
    if( ptr_rect )
    {
      di.rect_source = *ptr_rect;
    }
    (*pp_DIB)->Draw( pdc.GetDC(), di );
  }
  On_PaintMoveAfter( pdc.GetDC() );
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 無効表示
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ButtonImage::On_PaintInvalid()
{
  PaintDC pdc( GetWindowHandle() );

  DIBitmap** pp_DIB   = m_image_table.GetActive();
  Rect*      ptr_rect = m_rect_table.GetActive();
  if( pp_DIB  &&  *pp_DIB  &&  ptr_rect )
  {
    DIBitmap::DrawInfo di;
    if( ptr_rect )
    {
      di.rect_source = *ptr_rect;
    }
    (*pp_DIB)->Draw( pdc.GetDC(), di );
  }
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// マウスダウン
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF ButtonImage::On_MouseDown( _in<eMouseButton> in_mouse_btn )
{
  SetDrawStateID( eButtonMode::Down );
  Update( true );
  return Button::On_MouseDown( in_mouse_btn );
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// マウス移動
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF ButtonImage::On_MouseMove()
{
  if( !::GetCapture() )
  {
    if( Mouse::GetDownL() )
    {
      SetDrawStateID( eButtonMode::Down );
    }
    else
    {
      SetDrawStateID( eButtonMode::Move );
    }
    Update( true );
    ::SetCapture( GetWindowHandle() );
  }
  else
  {
    auto const clt_size = GetClientSize();
    bool const is_in_X = Limit( Mouse::GetX(), 0, clt_size.x );
    bool const is_in_Y = Limit( Mouse::GetY(), 0, clt_size.y );
    bool const is_down = Mouse::GetDownL();
    if( (!is_in_X  ||  !is_in_Y)  &&  !is_down )
    {
      SetDrawStateID( eButtonMode::Up );
      Update( true );
      ::ReleaseCapture();
    }
  }
  return true;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// マウスアップ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF ButtonImage::On_MouseUp( _in<eMouseButton> in_mouse_btn )
{
  SetDrawStateID( eButtonMode::Up );
  Update( true );
  return Button::On_MouseUp( in_mouse_btn );
}



} // namespace WIN
} // namespace CPL
