/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2010/X/X - 2017/02/03
/// 
/// @brief  スクロールバーコンポーネント
/// 
/// ****************************************************************************

#include "CPL_GUC_ScrollBar.h"


namespace CPL {
namespace GUC {

// _____________________________________________________________________________
/// @brief アップダウンボタンクラス
///
// *****************************************************************************
class UpDownButton
  : public Button
{
  using td_base = Button;
  using td_self = UpDownButton;
  friend Factory<td_self>;
  friend CallPrivateConstructor<td_self>;

  /// 矢印方向
  /// 0: left
  /// 1: right
  /// 2: up
  /// 3: down
  int m_type;

protected:
  UpDownButton( _in<sdptr<Component>> in_sp_component_for_parent, _in<int> in_type )
    : Button
      ( in_sp_component_for_parent
      , kPOS_DEFAULT
      , kSIZE_DEFAULT
      , nullptr
      , nullptr
      )
    , m_type( in_type )
  {
  }
public:
  UpDownButton( cref<UpDownButton> in_UDB )
    : td_base( in_UDB )
    , m_type( in_UDB.m_type )
  {}
  ~UpDownButton(){}

  void On_DrawClient( _in<float4> in_rect ) override
  {
    DX::ColorType color =
      (sm_sp_ac_comp() == this->GetLinkMyself()) ? kCOLOR_BLUE : kCOLOR_BLACK;
    const short img_w = 13;
    switch( m_type )
    {
    case 0: // left
      zzf_DrawParts
        ( float2( (in_rect.x+in_rect.z-img_w)/2, (in_rect.y+in_rect.w-img_w)/2 )
        , eImagePartsID::TriangleLeft
        , color
        );
      break;

    case 1: // right
      zzf_DrawParts
        ( float2( (in_rect.x+in_rect.z-img_w)/2, (in_rect.y+in_rect.w-img_w)/2 )
        , eImagePartsID::TriangleRight
        , color
        );
      break;
      
    case 2: // up
      zzf_DrawParts
        ( float2( (in_rect.x+in_rect.z-img_w)/2, (in_rect.y+in_rect.w-img_w)/2 )
        , eImagePartsID::TriangleUp
        , color
        );
      break;
      
    case 3: // down
      zzf_DrawParts
        ( float2( (in_rect.x+in_rect.z-img_w)/2, (in_rect.y+in_rect.w-img_w)/2 )
        , eImagePartsID::TriangleDown
        , color
        );
      break;
    }
  }

  // _____________________________________________________________________________
  /// @copydoc CPL::GUC::Component::CreateClone
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  sdptr<Component>
    CreateClone
      ( _in<sdptr<Component>> in_sp_component_for_parent       = nullptr
      , _in<float2          > in_offset_position_from_original = float2{0,0}
      ) const override
  {
    return
      zzz_CreateClone_Basic<td_self>
        ( in_sp_component_for_parent
        , in_offset_position_from_original
        );
  }
};




// _____________________________________________________________________________
/// @brief コンストラクタ。
/// 
/// @copydoc doxycomment::arg_in_sp_component_for_parent
/// @copydoc doxycomment::arg_in_position_for_component
/// @copydoc doxycomment::arg_in_size_for_component
/// @param[in] in_type
///   : 横スクロールバーか、立てスクロールバーかを指定します。\n
///   : 0     - 横スクロールバー\n
///   : 0以外 - 縦スクロールバー
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ScrollBar::ScrollBar
( _in<sdptr<Component>> in_sp_component_for_parent
, _in<float2          > in_position_for_component
, _in<float2          > in_size_for_component
, _in<int             > in_type
) : td_base
    ( in_sp_component_for_parent
    , in_position_for_component
    , in_size_for_component
    )
  , m_min ( 0 )
  , m_max ( 0 )
  , m_pos ( 0 )
  , m_page( 0 )
  , m_step( 1 )
  , m_type( in_type )
{
  zzz_setFrameSize_( 1 );

  m_sp_btn_up =
    Factory<UpDownButton>::Create
      ( GetLinkMyself()
      , in_type ? 2 : 0
      );
  m_sp_btn_down =
    Factory<UpDownButton>::Create
      ( GetLinkMyself()
      , in_type ? 3 : 1
      );
  m_sp_btn_knob =
    Factory<Button>::Create
      ( GetLinkMyself()
      , kPOS_DEFAULT
      , kSIZE_DEFAULT
      , nullptr
      , nullptr
      );

  zzf_setupDrawState( true, true, false ); // 描画ステートのデフォルト設定。
  zzf_SetInitializedLevel( zzf_GetClassLevel() );
  
  zzf_setItemComponent_();
}


// _____________________________________________________________________________
/// @brief コピーコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ScrollBar::ScrollBar( cref<ScrollBar> in_scroll_bar )
  : td_base( in_scroll_bar )
  , m_min( in_scroll_bar.m_min )
  , m_max( in_scroll_bar.m_max )
  , m_pos( in_scroll_bar.m_pos )
  , m_page( in_scroll_bar.m_page )
  , m_step( in_scroll_bar.m_step )
  , m_type( in_scroll_bar.m_type )
  , m_mouse_down_pos( in_scroll_bar.m_mouse_down_pos )
  , m_sp_btn_up
    { FindComponentT_by_UserID<Button>( in_scroll_bar.m_sp_btn_up )
    }
  , m_sp_btn_down
    { FindComponentT_by_UserID<Button>( in_scroll_bar.m_sp_btn_down )
    }
  , m_sp_btn_knob
    { FindComponentT_by_UserID<Button>( in_scroll_bar.m_sp_btn_knob )
    }
{
  m_sp_btn_up  ->SetAutoUserID();
  m_sp_btn_down->SetAutoUserID();
  m_sp_btn_knob->SetAutoUserID();
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::CreateClone
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<Component>
  ScrollBar::CreateClone
    ( _in<sdptr<Component>> in_sp_component_for_parent
    , _in<float2          > in_offset_position_from_original
    ) const
{
  return
    zzz_CreateClone_Basic<td_self>
      ( in_sp_component_for_parent
      , in_offset_position_from_original
      );
}


// _____________________________________________________________________________
/// @brief 最小値設定。
/// 
/// @param[in] in_value_of_minimum : 最小値を指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ScrollBar::SetMin( _in<float> in_value_of_minimum )
{
  m_min = in_value_of_minimum;

  m_max = Max( m_min, m_max );
  m_pos = Clamp( m_pos, m_min, m_max-m_page );
  
  zzf_setItemComponent_();

  Update();
}

// _____________________________________________________________________________
/// @brief 最大値設定。
/// 
/// @param[in] in_value_of_maximum : 最大値を指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ScrollBar::SetMax( _in<float> in_value_of_maximum )
{
  m_max = in_value_of_maximum;

  m_min = Min( m_min, m_max );
  m_pos = Clamp( m_pos, m_min, m_max-m_page );
  
  zzf_setItemComponent_();

  Update();
}

// _____________________________________________________________________________
/// @brief 現在位置設定。
/// 
/// @param[in] in_pos_value
///   : 現在値を指定します。
/// @param[in] in_is_step
///   : ステップ単位で値を補正するかどうか。\n
///   : true  - 補正する。\n
///   : false - 補正しない。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ScrollBar::SetPos( _in<float> in_pos_value, _in<bool> in_is_step )
{
  if( in_is_step )
  {
    // ステップ単位に補正
    m_pos = int(in_pos_value / m_step) * m_step;
  }
  else
  {
    m_pos = in_pos_value;
  }

  m_pos = Clamp( m_pos, m_min, m_max-m_page );

  zzf_setItemComponent_();

  SendParentMessage
    ( GetLinkMyself()
    , eID::ScrollBarChangeValue
    , 0
    , *(LPARAM*)&m_pos
    , eParentType::Render
    );

  Update();
}


// _____________________________________________________________________________
/// @brief ページサイズ設定。
/// 
/// @param[in] in_page_size : ページサイズを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ScrollBar::SetPage( _in<float> in_page_size )
{
  m_page = in_page_size;

  m_pos = Clamp( m_pos, m_min, m_max-m_page );
  
  zzf_setItemComponent_();

  Update();
}


// _____________________________________________________________________________
/// @brief 刻み値の設定。
/// 
/// @param[in] in_step_value : アップダウンボタンを押したときの刻み値を指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ScrollBar::SetStep( _in<float> in_step_value )
{
  if( in_step_value <= 0.000001f )
  {
    // 刻み値は最小値以下には設定できないようにする。
    return;
  }

  m_step = in_step_value;
  
  zzf_setItemComponent_();
}

// _____________________________________________________________________________
/// @brief パラメータ設定。
/// 
/// @param[in] in_pos_value  : 現在値。
/// @param[in] in_value_of_minimum  : 最小値。
/// @param[in] in_value_of_maximum  : 最大値。
/// @param[in] in_step_value : アップダウンボタンを押したときの刻み値。
/// @param[in] in_page_size  : ページサイズ。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  ScrollBar::SetParameter
    ( _in<float> in_pos_value
    , _in<float> in_value_of_minimum
    , _in<float> in_value_of_maximum
    , _in<float> in_step_value
    , _in<float> in_page_size
    )
{
  m_pos  = in_pos_value;
  m_min  = in_value_of_minimum;
  m_max  = in_value_of_maximum;
  m_step = in_step_value;
  m_page = in_page_size;

  // 補正。
  m_min = Min( m_min, m_max );
  m_pos = Clamp( m_pos, m_min, m_max-m_page );

  zzf_setItemComponent_();

  Update();
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::On_Size
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  ScrollBar::On_Size( _in<sdptr<Component>> in_sp_component )
{
  if( in_sp_component == this->GetLinkMyself() )
  {
    // サイズ補正
    auto size = this->GetSize();
    if( m_type == 0 ) // 横
    {
      size.y = GetBtnSize();
    }
    else // 縦
    {
      size.x = GetBtnSize();
    }
    this->SetSize( size );

    zzf_setItemComponent_();
  }

  return eEventReturnID::SendParent_Render;
}

// _____________________________________________________________________________
// スライドノブの座標を計算
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
float4 ScrollBar::zzf_getSrideRect_()
{
  auto size = GetSize();
  float4 rt_knob;
  float range = m_max - m_min;
  const int SC_BTN_SIZE = static_cast<int>(GetBtnSize());
  if( m_type == 0 ) // 横タイプ
  {
    float width = static_cast<float>(size.x - SC_BTN_SIZE * 2);
    float page_size = (m_page + m_step) / (range + m_step) * width;
    if( page_size < 5.0f ){ page_size = 5.0f; }
    float pos_x = m_pos / (range - m_page) * (width - page_size) + SC_BTN_SIZE - 1;
    rt_knob.x = pos_x;
    rt_knob.z = pos_x + page_size + 2;
    rt_knob.y = 0;
    rt_knob.w = GetBtnSize();
  }
  else // 縦タイプ
  {
    float width = static_cast<float>(size.y - SC_BTN_SIZE * 2);
    float page_size = (m_page + m_step) / (range + m_step) * width;
    if( page_size < 5.0f ){ page_size = 5.0f; }
    float pos_y = m_pos / (range - m_page) * (width - page_size) + SC_BTN_SIZE - 1;
    rt_knob.y = pos_y;
    rt_knob.w = pos_y + page_size + 2;
    rt_knob.x = 0;
    rt_knob.z = GetBtnSize();
  }

  if( IsDrawFrame() )
  {
    auto const fw = GetFrameSize();
    rt_knob += float4(fw,fw, -fw,-fw);
  }

  return rt_knob;
}

// _____________________________________________________________________________
// アイテムコンポーネントの位置再設定
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ScrollBar::zzf_setItemComponent_()
{
  auto size = this->GetSize();
  auto const SC_BTN_SIZE = GetBtnSize();
  auto const fw = GetFrameSize();

  // 位置設定
  if( m_type == 0 ) // 横タイプ
  {
    GetComponent_UpButton()->SetRectCoord
      ( float4(fw,fw, SC_BTN_SIZE, size.y-fw)
      );
    GetComponent_DownButton()->SetRectCoord
      ( float4(size.x-SC_BTN_SIZE, fw, size.x-fw, size.y-fw)
      );
  }
  else // 縦タイプ
  {
    GetComponent_UpButton()->SetRectCoord
      ( float4(fw,fw, size.x-fw, SC_BTN_SIZE)
      );
    GetComponent_DownButton()->SetRectCoord
      ( float4(fw, size.y-SC_BTN_SIZE, size.x-fw,size.y-fw)
      );
  }
  //
  float4 rt_knob = zzf_getSrideRect_();
  GetComponent_KnobButton()->SetRectCoord( rt_knob );
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::MouseEventInterface::On_MouseMove
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  ScrollBar::On_MouseMove
    ( _in<sdptr<Component>> in_sp_component
    , _in<float           > in_mouse_x
    , _in<float           > in_mouse_y
    )
{
  if( in_sp_component == GetComponent_KnobButton() )
  {
    if( WIN::Mouse::GetDownL() )
    {
      // 値調整
      float range = m_max - m_min + m_step;
      float step_num = range / m_step;
      float const SC_BTN_SIZE = GetBtnSize();

      if( step_num > 0 )
      {
        auto size      = GetSize();
        auto knob_size = GetComponent_KnobButton()->GetSize();
        float value = 0;

        if( m_type == 0 ) // horizontal
        {
          float width = static_cast<float>(size.x - SC_BTN_SIZE * 2);
          float pos   = static_cast<float>(in_mouse_x - m_mouse_down_pos.x- SC_BTN_SIZE);
          value = pos / width * range;
        }
        else // virtical
        {
          float width = static_cast<float>(size.y - SC_BTN_SIZE * 2);
          float pos   = static_cast<float>(in_mouse_y - m_mouse_down_pos.y - SC_BTN_SIZE);
          value = pos / width * range;
        }

        SetPos( value );
        zzf_setItemComponent_();
      }
    }
  }

  return eEventReturnID::SendParent_Position;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::MouseEventInterface::On_MouseDown
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  ScrollBar::On_MouseDown
    ( _in<sdptr<Component>> in_sp_component
    , _in<float           > in_mouse_x
    , _in<float           > in_mouse_y
    , _in<eMouseButton    > in_type_mouse_btn
    )
{
  if( in_sp_component == GetLinkMyself() )
  {
    ActivateTopOrder();
  }

  if( in_sp_component == GetComponent_KnobButton() )
  {
    SetCapture( GetComponent_KnobButton() );

    m_mouse_down_pos.x =
      in_mouse_x - GetComponent_KnobButton()->GetPosition().x;
    m_mouse_down_pos.y =
      in_mouse_y - GetComponent_KnobButton()->GetPosition().y;

    return eEventReturnID::LastCall;
  }

  return eEventReturnID::SendParent_Position;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::MouseEventInterface::On_MouseUp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  ScrollBar::On_MouseUp
    ( _in<sdptr<Component>> in_sp_component
    , _in<float           > in_mouse_x
    , _in<float           > in_mouse_y
    , _in<eMouseButton    > in_type_mouse_btn
    )
{
  if( in_sp_component == GetComponent_UpButton() )
  {
    SetPos( m_pos - m_step );
    zzf_setItemComponent_();
  }
  else
  if( in_sp_component == GetComponent_DownButton() )
  {
    SetPos( m_pos + m_step );
    zzf_setItemComponent_();
  }
  else
  if( in_sp_component == GetComponent_KnobButton() )
  {
    SetCapture( nullptr );
  }

  return eEventReturnID::SendParent_Position;
}


// _____________________________________________________________________________
/// @brief スクロールバーのボタンサイズの取得
/// 
/// @return ボタンサイズ(スクロール方向の幅)が返ります。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
float ScrollBar::GetBtnSize()
{
  return 18;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::On_DrawBackground
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ScrollBar::On_DrawBackground( _in<float4> in_rect )
{
  //zzf_DrawRect_DefaultComponentBG( in_rect, COLOr_DEFAULT_BG );
  zzf_DrawRect
    ( in_rect
    , GetDefaultColor_CompBG()->color_default.color[0] * float4(0.8f,0.8f,0.8f,0.5f)
    );
}



} // namespace GUC
} // namespace CPL

