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

#pragma once

#include "CPL_WIN_Component.h"
#include "../../GObj/CPL_WIN_Graphics.h"


namespace CPL {
namespace WIN {

// ↓後から実装しよう！
//enum class ButtonStyle : U32
//{
//  PUSHBUTTON      = BIT0,
//  CHECKBOX        = BIT1,
//  RADIOBOX        = BIT2,
//  CHECKBUTTON     = BIT3,
//  RADIOBUTTON     = BIT4,
//  AUTOCHECKBOX    = BIT5,
//  AUTORADIOBOX    = BIT6,
//  AUTOCHECKBUTTON = BIT7,
//  AUTORADIOBUTTON = BIT8,
//  IMAGEBUTTON     = BIT9,
//};

// _____________________________________________________________________________
/// @brief ボタンクラス
///
// *****************************************************************************
class Button
  : public Component
{
public:
  CPL_SCOPED_ENUM
    ( eType, int
    , PushButton      = BIT0
    , CheckBox        = BIT1
    , RadioBox        = BIT2
    , CheckButton     = BIT3
    , RadioButton     = BIT4
    , AutoCheckBox    = BIT5
    , AutoRadioBox    = BIT6
    , AutoCheckButton = BIT7
    , AutoRadioButton = BIT8
    , ImageButton     = BIT9
    );

  virtual void  SetCheck    ( int in_flag )   { SendMessage( m_hdl_window, BM_SETCHECK, in_flag, 0 ); }
  virtual int   IsCheck     () const;
  virtual void  SetPushStyle( int in_flag )   { SendMessage( m_hdl_window, BM_SETSTATE, in_flag, 0 ); }
  virtual int   GetPushStyle() const          { return static_cast<int>(SendMessage( m_hdl_window, BM_GETSTATE, 0, 0 )); }
  //virtual void  ChangeText  ( str_cptr in_title ){ ::SetWindowText( m_hdl_window, in_title ); }
  virtual void  AdjustSize  ( _in<td_Coord2> in_offs_size = int2(4,4) );

  template<class T>
  Button
  ( _in<HWND     > in_hdl_window_parent
  , T const&       in_title
  , _in<td_Coord2> in_pos      = td_Coord2(0,0)
  , _in<td_Coord2> in_size     = kSIZE_DEFAULT
  , _in<U32      > in_btn_type = BS_PUSHBUTTON
  )
  {
    LONG_PTR win_style = zzf_type_to_Style_( in_btn_type );
    if( !zzf_Create( in_hdl_window_parent, &in_title[0], in_pos, in_size, -1, win_style, 0, this ) )
    {
      CPL_THROW( CPL_WIN_MSG_NOT_CREATE_BUTTON );
    }
    // サイズが指定されていない場合は、アドジャストする。
    if( in_size.x < 0  &&  in_size.y < 0 )
    {
      AdjustSize();
    }

    // サブクラス化
    if( !zzf_BUTTON() )
    {
      zzf_BUTTON() = zzf_GetProc( m_hdl_window );
    }
    zzf_SetProc( zzf_StaticProcButton );
  }
  virtual ~Button() {}

protected:
  static LRESULT CALLBACK
    zzf_StaticProcButton
      ( HWND   in_hdl_window
      , UINT   in_message
      , WPARAM in_wParam
      , LPARAM in_lParam
      );

  CPL_SINGLETON_INIT( WNDPROC, zzf_BUTTON, nullptr )

  CPL_WIN_COMPONENT_GET_CLASS_NAME( "BUTTON" );

  SDEF On_Paint    () override { return false; }
  SDEF On_MouseDown( _in<eMouseButton> in_btn ) override;
  SDEF On_MouseUp  ( _in<eMouseButton> in_btn ) override;

  virtual SDEF On_ButtonClick( int f_down );

private:
  Button(){}
  Button( Button const& ){}

private:
  LONG_PTR zzf_type_to_Style_( U32 in_btn_type );

public:

  // _____________________________________________________________________________
  /// @brief ボタンクラス　※ Pimpl実装用
  ///
  // *****************************************************************************
  class Binder
    : public Component::Binder
  {
  private:
    Button*       getEntity()       { return dynamic_cast<Button*>(&*impl); }
    Button const* getEntity() const { return dynamic_cast<Button const*>(&*impl); }

  public:
    virtual void  SetCheck    ( _in<int> in_flag )                  {        getEntity()->SetCheck( in_flag );     }
    virtual int   IsCheck     ()                                    { return getEntity()->IsCheck();               }
    virtual void  SetPushStyle( _in<int> in_flag )                  {        getEntity()->SetPushStyle( in_flag ); }
    virtual int   GetPushStyle()                                    { return getEntity()->GetPushStyle();          }
    virtual void  AdjustSize  ( _in<td_Coord2> in_offs_size = int2(4,4) ){ getEntity()->AdjustSize( in_offs_size ); }

    Binder
    ( _in<HWND         > in_hdl_window_parent
    , _in<str_cptr     > in_title    = nullptr
    , _in<td_Coord2    > in_pos      = td_Coord2(0,0)
    , _in<td_Coord2    > in_size     = kSIZE_DEFAULT
    , _in<Button::eType> in_btn_type = Button::eType::PushButton
    ) : Component::Binder
        ( CPL_NEW(Button)
          ( in_hdl_window_parent
          , in_title
          , in_pos
          , in_size
          , in_btn_type
          )
        )
    {}
  protected:
    // 継承用
    Binder( Button* in_ptr_button ) : Component::Binder( in_ptr_button ) {}
  };

};



// _____________________________________________________________________________
/// @brief イメージ対応ボタンクラス
///
// *****************************************************************************
class ButtonImage
  : public Button
{
public:
  CPL_SCOPED_ENUM
    ( eButtonMode, short
    , Up      = 1
    , Down    = 2
    , Move    = 4
    , Invalid = 8
    , UpDown          = Up   | Down
    , UpDownInvalid   = Up   | Down | Invalid
    , UpMove          = Up   | Move
    , UpMoveInvalid   = Up   | Move | Invalid
    , DownMove        = Down | Move
    , DownMoveInvalid = Down | Move | Invalid
    , UpDownMove      = Up   | Down | Move
    , All             = Up   | Down | Move | Invalid
    );

protected:
  Core::Selector<int,DIBitmap*> m_image_table;
  Core::Selector<int,Rect>      m_rect_table;
  short       m_up_ID;
  short       m_move_ID;
  short       m_down_ID;
  short       m_invalid_ID;
  short       m_state;  // ボタンの状態 0:アップ(通常) 1:ダウン 2:ボタン上にカーソルあり  -1:無効
  eButtonMode m_mode;   // 

//  long On_EraseBkGnd( HDC in_hdc );
  virtual SDEF On_Paint    ();
  virtual SDEF On_MouseDown( _in<eMouseButton> in_btn );
  virtual SDEF On_MouseMove();
  virtual SDEF On_MouseUp  ( _in<eMouseButton> in_btn );
  virtual void On_PaintDown();
  virtual void On_PaintMove();
  virtual void On_PaintUp  ();
  virtual void On_PaintDownAfter( _in<HDC> in_hdc ){}
  virtual void On_PaintMoveAfter( _in<HDC> in_hdc ){}
  virtual void On_PaintUpAfter  ( _in<HDC> in_hdc ){}

  //void zzf_Draw();

private:
  virtual void On_PaintInvalid();

public:
  virtual void SetRectUpID     ( int id ){ m_up_ID = id; }
  virtual void SetRectMoveID   ( int id ){ m_move_ID = id; }
  virtual void SetRectDownID   ( int id ){ m_down_ID = id; }
  virtual void SetRectInvalidID( int id ){ m_invalid_ID = id; }
  virtual void SetRectID       ( short up, short move, short down, short invalid = 0 )
  {
    m_up_ID      = up;
    m_move_ID    = move;
    m_down_ID    = down;
    m_invalid_ID = invalid;
    m_rect_table.Select( up );
  }
  virtual void        SetDrawStateID( short in_state );
  virtual int         GetDrawStateID() const { return m_state; }
  virtual void        SetImageMode ( eButtonMode mode ){ m_mode = mode; }
  virtual void        SetInvalid   ( int in_flag );
  virtual eButtonMode GetImageMode () const { return m_mode; }

  virtual void AddImage   ( int ID, DIBitmap* in_ptr_DIB );
  virtual void DelImage   ( int ID );
  virtual void ChangeImage( int ID );
  virtual void AddRect    ( int ID, Rect* in_ptr_rect );
  virtual void DelRect    ( int ID );

  virtual DIBitmap* GetActiveImage(){ return *m_image_table.GetActive(); }
  virtual Rect*     GetActiveRect (){ return m_rect_table.GetActive(); }

  ButtonImage
  ( _in<HWND     > in_hdl_window_parent
  , _in<int      > in_DIB_ID
  , pin<DIBitmap > in_ptr_DIB
  , _in<td_Coord2> in_pos  = td_Coord2(0,0)
  , _in<td_Coord2> in_size = kSIZE_DEFAULT
  , _in<Rect     > in_rect = Rect(0,0,0,0)
  , _in<eType    > in_btn_type = eType::ImageButton
  );
  virtual ~ButtonImage() {}
  

  // _____________________________________________________________________________
  /// @brief イメージ対応ボタン　Pimpl実装用
  ///
  // *****************************************************************************
  class Binder
    : public Button::Binder
  {
  private:
    ButtonImage*       getEntity()       { return dynamic_cast<ButtonImage*>(&*impl); }
    ButtonImage const* getEntity() const { return dynamic_cast<ButtonImage const*>(&*impl); }

  public:
    void                  SetRectUpID     ( int id )                                             {        getEntity()->SetRectUpID     ( id );                      }
    void                  SetRectMoveID   ( int id )                                             {        getEntity()->SetRectMoveID   ( id );                      }
    void                  SetRectDownID   ( int id )                                             {        getEntity()->SetRectDownID   ( id );                      }
    void                  SetRectInvalidID( int id )                                             {        getEntity()->SetRectInvalidID( id );                      }
    void                  SetRectID       ( short up, short move, short down, short invalid = 0 ){        getEntity()->SetRectID       ( up, move, down, invalid ); }
    void                  SetDrawStateID   ( short in_state )                                     {        getEntity()->SetDrawStateID   ( in_state );                }
    int                   GetDrawStateID   ()                                                     { return getEntity()->GetDrawStateID   ();                          }
    void                  SetImageMode    ( ButtonImage::eButtonMode in_mode )                      {        getEntity()->SetImageMode    ( in_mode );                 }
    void                  SetInvalid      ( int in_flag )                                        {        getEntity()->SetInvalid      ( in_flag );                 }
    ButtonImage::eButtonMode GetImageMode    ()                                                     { return getEntity()->GetImageMode    ();                          }

    void AddImage   ( int ID, DIBitmap* in_ptr_DIB ){ getEntity()->AddImage( ID, in_ptr_DIB ); }
    void DelImage   ( int ID )                      { getEntity()->DelImage( ID ); }
    void ChangeImage( int ID )                      { getEntity()->ChangeImage( ID ); }
    void AddRect    ( int ID, Rect* in_ptr_rect )   { getEntity()->AddRect( ID, in_ptr_rect ); }
    void DelRect    ( int ID )                      { getEntity()->DelRect( ID ); }

    DIBitmap* GetActiveImage(){ return getEntity()->GetActiveImage(); }
    Rect*     GetActiveRect (){ return getEntity()->GetActiveRect(); }

    Binder
    ( _in<HWND         > in_hdl_window_parent
    , _in<int          > in_DIB_ID
    , pin<DIBitmap     > in_ptr_DIB
    , _in<td_Coord2    > in_pos  = td_Coord2(0,0)
    , _in<td_Coord2    > in_size = kSIZE_DEFAULT
    , _in<Rect         > in_rect = Rect(0,0,1,1)
    , _in<Button::eType> in_btn_type = Button::eType::ImageButton
    ) : Button::Binder
        ( CPL_NEW(ButtonImage)
            ( in_hdl_window_parent
            , in_DIB_ID
            , in_ptr_DIB
            , in_pos
            , in_size
            , in_rect
            , in_btn_type
            )
        )
    {}
  protected:
    // 継承用
    Binder( ButtonImage* in_ptr_button_image )
      : Button::Binder( in_ptr_button_image )
    {}
  };

};


} // namespace WIN
} // namespace CPL
