/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2010/X/X - 2017/02/03
/// 
/// @brief  レンダリング用クライアント管理クラス。
/// 
/// ****************************************************************************

#include "CPL_GUC_RenderOwner.h"


namespace CPL {
namespace GUC {


// _____________________________________________________________________________
/// @brief コンストラクタ
/// 
/// @copydoc doxycomment::arg_in_sp_component_for_parent
/// @copydoc doxycomment::arg_in_position_for_component
/// @copydoc doxycomment::arg_in_size_for_component
/// @copydoc doxycomment::arg_in_position_for_client
/// @copydoc doxycomment::arg_in_size_for_client
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RenderOwner::RenderOwner
( _in<sdptr<Component>> in_sp_component_for_parent
, _in<float2          > in_position_for_component
, _in<float2          > in_size_for_component
, _in<float2          > in_position_for_client
, _in<float2          > in_size_for_client
) : td_base
    ( in_sp_component_for_parent
    , in_position_for_component
    , in_size_for_component
    )
{
  float const fw = GetFrameSize();
  float2 pos =
  { in_position_for_component.x >= 0 ? in_position_for_component.x : fw
  , in_position_for_component.y >= 0 ? in_position_for_component.y : fw
  };
  float2 size =
  { in_size_for_component.x >= 0 ? in_size_for_component.x : 1
  , in_size_for_component.y >= 0 ? in_size_for_component.y : 1
  };
  m_rect_client.x = pos.x;
  m_rect_client.y = pos.y;
  m_rect_client.z = pos.x + size.x;
  m_rect_client.w = pos.y + size.y;

  // クライアントコンポーネントの作成。
  m_sp_client =
    Factory<RenderBox>::Create
      ( GetLinkMyself()
      , in_position_for_client
      , in_size_for_client
      );

  // スクロールバーの作成。
  m_sp_scroll_h =
    Factory<ScrollBar>::Create( GetLinkMyself(), kPOS_DEFAULT, kSIZE_DEFAULT, 0 );
  m_sp_scroll_v =
    Factory<ScrollBar>::Create( GetLinkMyself(), kPOS_DEFAULT, kSIZE_DEFAULT, 1 );

  // 
  SetupWorkingArea();

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


// _____________________________________________________________________________
/// @brief コピーコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RenderOwner::RenderOwner( cref<RenderOwner> in_render_owner )
  : td_base( in_render_owner )
  , m_sp_client
    { FindComponentT_by_UserID<RenderBox>( in_render_owner.m_sp_client )
    }
  , m_rect_client( in_render_owner.m_rect_client )
  , m_size_work( in_render_owner.m_size_work )
  , m_sp_scroll_h
    { FindComponentT_by_UserID<ScrollBar>( in_render_owner.m_sp_scroll_h )
    }
  , m_sp_scroll_v
    { FindComponentT_by_UserID<ScrollBar>( in_render_owner.m_sp_scroll_v )
    }
{
  m_sp_client  ->SetAutoUserID();
  m_sp_scroll_h->SetAutoUserID();
  m_sp_scroll_v->SetAutoUserID();
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::CreateClone
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<Component>
  RenderOwner::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
      );
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::On_Paint
/// 
/// ※ Windowクラスは自身がオーナーとなります。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
float2
  RenderOwner::On_Paint
    ( _in<sdptr<Component>> in_sp_draw_owner
    , _in<float2          > in_offset_position_from_parent
    )
{
  auto const sp_comp_parent = GetLinkParent();
  auto const sp_comp_belong = GetBelongComponent( eParentType::Position );
  // オーナー空間への変換
  // ※ 背景およびフレーム描画はオーナー系で描画するため、
  //    ここではOn_DrawBackgroundとOn_DrawFrameのみ呼び出す。
  //    その他はコールバック関数はクライアントのコンポーネントから呼び出される
  float4 rt = GetRectCoord();
  if( sp_comp_parent == sp_comp_belong )
  {
    rt.x += in_offset_position_from_parent.x;
    rt.z += in_offset_position_from_parent.x;
    rt.y += in_offset_position_from_parent.y;
    rt.w += in_offset_position_from_parent.y;
  }
  else
  {
    MappingPosition
      ( sp_comp_belong
      , in_sp_draw_owner //IsOwner() ? nullptr : in_sp_draw_owner
      , (float2_ptr)&rt
      , 2
      );
  }
  
  float const fw = IsDrawFrame() ? GetFrameSize() : 0;
  if( IsDrawBG() )
  {
    On_DrawBackground( rt + float4(fw,fw, -fw+1,-fw+1) );
  }
  if( IsDrawFrame() )
  {
    On_DrawFrame( rt );
  }
  // ↓クライアント描画はレンダーボックス側から呼び出される。
  //if( IsDrawClient() )
  //{
  //  On_DrawClient( rt + short4(fw,fw, -fw,-fw) );
  //}
  
  //
  if( IsUpdate() )
  {
    GetComponent_RenderBox()->Update();
  }

  // 子供コンポーネントの描画
  float2 offs_new{rt.x, rt.y};
  zzf_CallChildPaint
    ( in_sp_draw_owner
    , offs_new
    );

  SetUpdated();

  return offs_new;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::On_Size
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID RenderOwner::On_Size( _in<sdptr<Component>> in_sp_component )
{
  if( in_sp_component == this->GetLinkMyself()  &&
      GetInitialized() >= zzf_GetClassLevel() )
  {
    // スクロールバー調整のため呼び出し。
    auto const size_frame = GetFrameSize() * IsDrawFrame();
    auto const rect_comp = GetRectCoord();
    SetupWorkingArea
      ( float4
          ( size_frame
          , size_frame
          , rect_comp.Width()  - size_frame
          , rect_comp.Height() - size_frame
          )
      , m_size_work
      );
    //
    Update();
  }

  return eEventReturnID::SendParent_Position;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::On_UserProc
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GUC::eEventReturnID
  RenderOwner::On_UserProc
    ( _in<sdptr<GUC::Component>> in_sp_component
    , _in<long                 > in_message
    , _in<WPARAM               > in_wparam
    , _in<LPARAM               > in_lparam
    )
{
  switch( in_message )
  {
  case eID::ScrollBarChangeValue:
    if( m_sp_client )
    {
      m_sp_client->GetComponent_ItemLayer()->SetPosition
        ( float2
          ( -GetComponent_ScrollH()->GetPos()
          , -GetComponent_ScrollV()->GetPos()
          )
        );
      GetComponent_RenderBox()->Update();
    }
    break;

  case eID::ValueChanged:
    break;
  }

  return
    td_base::On_UserProc
      ( in_sp_component
      , in_message
      , in_wparam
      , in_lparam
      );
}


// _____________________________________________________________________________
/// @brief ワーキングエリアの設定（スクロールバーを調整する）。
/// 
/// ここで言うワーキングエリアとは、クライアントのアイテム配置等の使用領域を指します。\n
/// ワーキングエリアがクライアント領域(表示領域)を超えると、スクロールバーを表示します。
/// 
/// @param[in] in_rect_of_client
///   : クライアントエリア（クライアントの表示エリア）の矩形座標を指定します。\n
///   : このクライアントエリアの右または下側にスクロールバーを表示されます。
/// @param[in] in_rect_of_workarea
///   : ワーキングエリアの矩形座標を指定。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  RenderOwner::SetupWorkingArea
    ( _in<float4> in_rect_of_client
    , _in<float4> in_rect_of_workarea
    )
{
  m_size_work = float2( in_rect_of_workarea.Width(), in_rect_of_workarea.Height() );
  auto rect_client = m_rect_client = in_rect_of_client;

  float2 size_client( in_rect_of_client.Width(), in_rect_of_client.Height() );
  auto size_scb_h = m_sp_scroll_h->GetSize().y;
  auto size_scb_v = m_sp_scroll_v->GetSize().x;

  bool is_scb_view_h = size_client.x < m_size_work.x;
  bool is_scb_view_v = size_client.y < m_size_work.y;
  // もう一度チェック。
  is_scb_view_h = (size_client.x - is_scb_view_v * size_scb_h) < m_size_work.x;
  is_scb_view_v = (size_client.y - is_scb_view_h * size_scb_v) < m_size_work.y;
  //
  rect_client.right  -= is_scb_view_v * size_scb_h; // 縦スクロールバーが表示されていたら、横幅をマイナス。
  rect_client.bottom -= is_scb_view_h * size_scb_v; // 横スクロールバーが表示されていたら、縦幅をマイナス。

  // 横方向スクロールバー調整。
  m_sp_scroll_h->SetParameter
    ( m_sp_scroll_h->GetPos()
    , 0
    , (float)m_size_work.x
    , m_sp_scroll_h->GetStep()
    , (float)rect_client.Width()
    );
  m_sp_scroll_h->SetRectCoord
    ( float4
        ( rect_client.left
        , rect_client.bottom
        , rect_client.right
        , rect_client.bottom+scb_size
        )
    );
  m_sp_scroll_h->SetStateVisible( is_scb_view_h );

  // 縦方向スクロールバー調整。
  m_sp_scroll_v->SetParameter
    ( m_sp_scroll_v->GetPos()
    , 0
    , (float)m_size_work.y
    , m_sp_scroll_v->GetStep()
    , (float)rect_client.Height()
    );
  m_sp_scroll_v->SetRectCoord
    ( float4
        ( rect_client.right
        , rect_client.top
        , rect_client.right + scb_size
        , rect_client.bottom
        )
    );
  m_sp_scroll_v->SetStateVisible( is_scb_view_v );

  //
  GetComponent_RenderBox()->SetRectCoord( rect_client );

  // アイテム配置用レイヤーの位置補正。
  GetComponent_RenderClient()->SetPosition
    ( float2
        ( -m_sp_scroll_h->GetPos()
        , -m_sp_scroll_v->GetPos()
        )
    );

  //
  //Update();
}


// _____________________________________________________________________________
/// @brief ワーキングエリアのサイズの指定
/// 
/// ここで言うワーキングエリアとは、クライアントのアイテム配置等の使用領域を指します。\n
/// ワーキングエリアがクライアント領域(表示領域)を超えると、スクロールバーを表示します。
/// 
/// @param[in] in_rect_of_client
///   : クライアントエリア（クライアントの表示エリア）の矩形座標を指定します。\n
///   : このクライアントエリアの右または下側にスクロールバーが表示されます。
/// @param[in] in_size_of_workarea
///   : ワーキングエリアのサイズを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  RenderOwner::SetupWorkingArea
    ( _in<float4> in_rect_of_client
    , _in<float2> in_size_of_workarea
    )
{
  float offs_x = m_sp_scroll_h->GetPos();
  float offs_y = m_sp_scroll_v->GetPos();
  SetupWorkingArea
    ( in_rect_of_client
    , float4
        ( -offs_x + in_rect_of_client.x
        , -offs_y + in_rect_of_client.y
        , -offs_x + in_rect_of_client.x + in_size_of_workarea.x
        , -offs_y + in_rect_of_client.y + in_size_of_workarea.y
        )
    );
}

// _____________________________________________________________________________
/// @brief ワーキングエリアのサイズ指定
/// 
/// ここで言うワーキングエリアとは、クライアントのアイテム配置等の使用領域を指します。\n
/// ワーキングエリアがクライアント領域(表示領域)を超えると、スクロールバーを表示する。
/// 
/// @param[in] in_rect_of_workarea
///   : ワーキングエリアの矩形座標を指定します。\n
///   : 表示エリアは現在のコンポーネント矩形となります。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void RenderOwner::SetupWorkingArea( _in<float4> in_rect_of_workarea )
{
  auto rect_self = GetRectCoord();
  auto width_frame = GetFrameSize();

  SetupWorkingArea
    //( GetComponent_RenderBox()->GetRectCoord()
    ( float4
      { width_frame, width_frame
      , rect_self.Width()  - width_frame
      , rect_self.Height() - width_frame
      }
    , in_rect_of_workarea
    );
}


// _____________________________________________________________________________
/// @brief ワーキングエリアのサイズのみ指定して再設定（スクロールバーを調整する）
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void RenderOwner::SetupWorkingArea( _in<float2> in_size_of_workarea )
{
  float x = m_sp_scroll_h->GetPos();
  float y = m_sp_scroll_v->GetPos();
  SetupWorkingArea
    ( float4
        ( -x
        , -y
        , -x + in_size_of_workarea.x
        , -y + in_size_of_workarea.y
        )
    );
}


// _____________________________________________________________________________
/// @brief ワーキングエリアの再設定（スクロールバーを調整する）
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void RenderOwner::SetupWorkingArea()
{
  SetupWorkingArea( m_size_work );
}



} // namespace GUC
} // namespace CPL