/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2010/X/X - 2017/02/01
/// 
/// @brief  アクティブフォーム関連。
/// 
/// ****************************************************************************

#ifdef _MSC_VER
  #pragma comment(lib, "msimg32.lib")
#endif


//#include "CPL_GUC_Window.h"
#include "CPL_GUC_ActiveForm.h"
#include "CPL_GUC_MasterLayer.h"


namespace CPL {
namespace GUC {


// _____________________________________________________________________________
/// @brief コンストラクタ
/// 
/// @copydoc doxycomment::arg_in_sp_component_for_parent
/// @param[in] in_pos_mode
///   : 配置モードを指定します。\n
///   : 詳しくはePosModeを参照してください。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ActiveForm::ActiveForm
( _in<sdptr<Component>> in_sp_component_for_parent
, _in<ePosMode        > in_pos_mode
) : Container
    ( in_sp_component_for_parent
    , kPOS_DEFAULT
    , kSIZE_DEFAULT
    )
  , m_pos_mode( in_pos_mode )
  , m_hide_count( 0 )
{
  auto sp_comp_master =
    std::dynamic_pointer_cast<MasterLayer>( in_sp_component_for_parent->GetMasterLayer() );
  this->SetBelongComponent
    ( sp_comp_master->GetComponent_for_OverLayer()
    , eParentType::Render
    );

  // ActiveFormのルートチェック。
  // ルートの場合はコンテナテーブルオブジェクトを生成する。
  for
    ( auto sp_parent = in_sp_component_for_parent
    ; sp_parent
    ; sp_parent = sp_parent->GetLinkParent()
    )
  {
    if( auto sp_acform_parent = std::dynamic_pointer_cast<ActiveForm>(sp_parent) )
    {
      m_sp_view_form_table = sp_acform_parent->m_sp_view_form_table;
      break;
    }
  }
  if( !m_sp_view_form_table )
  {
    m_sp_view_form_table = CPL_MAKE_SHARED( ContainerArray );
  }

  // 初期設定
  this->SetStateVisible( false ); // 念のため非表示（デフォルトでは非表示）
  this->zzf_setupDrawState( true, true, false ); // 描画ステートのデフォルト設定。
  // 
  this->zzf_SetInitializedLevel( zzf_GetClassLevel() );
}


// _____________________________________________________________________________
/// @brief コピーコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ActiveForm::ActiveForm( cref<ActiveForm> in_active_form )
  : Container( in_active_form )
  , m_pos_mode( in_active_form.m_pos_mode )
  , m_hide_count( 0 )
{
  // ActiveFormのルートチェック。
  // ルートの場合はコンテナテーブルオブジェクトを生成する。
  for
    ( auto sp_parent = GetLinkParent()
    ; sp_parent
    ; sp_parent = sp_parent->GetLinkParent()
    )
  {
    if( auto sp_acform_parent = std::dynamic_pointer_cast<ActiveForm>(sp_parent) )
    {
      m_sp_view_form_table = sp_acform_parent->m_sp_view_form_table;
      break;
    }
  }
  if( !m_sp_view_form_table )
  {
    m_sp_view_form_table = CPL_MAKE_SHARED( ContainerArray );
  }
}


// _____________________________________________________________________________
/// @brief ヒット対象のコンポーネントの追加
/// 
/// @param[in] in_add_sp_window
///   : ヒット対象にするコンポーネントを指すスマートポインタを指定します。
/// 
/// @retval true  : 追加成功
/// @retval false : 追加失敗
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int ActiveForm::AddHitComponent( _in<sdptr<Component>> in_add_sp_window )
{
  auto itr_hit =
    std::find
      ( m_sp_view_form_table->begin()
      , m_sp_view_form_table->end()
      , in_add_sp_window
      );

  if( itr_hit == m_sp_view_form_table->end() )
  {
    m_sp_view_form_table->push_back( in_add_sp_window );
    return true;
  }

  return false;
}


// _____________________________________________________________________________
/// @brief ヒット対象のコンポーネントの削除
/// 
/// @param[in] in_del_sp_window
///   : ヒット対象から除外するコンポーネントのスマートポインタ指定します。
/// 
/// @retval true  : 削除成功
/// @retval false : 削除失敗
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int ActiveForm::DelHitComponent( _in<sdptr<Component>> in_del_sp_window )
{
  auto itr_hit =
    std::find
      ( m_sp_view_form_table->begin()
      , m_sp_view_form_table->end()
      , in_del_sp_window
      );

  if( itr_hit != m_sp_view_form_table->end() )
  {
    m_sp_view_form_table->erase( itr_hit );
    return true;
  }
  return false;
}


// _____________________________________________________________________________
/// @brief 指定座標のエリア内判定
/// 
/// @param[in] in_top_mx : 比較するｘ座標を指定します。
/// @param[in] in_top_my : 比較するｙ座標を指定します。
/// 
/// @retval true  : エリア内
/// @retval false : エリア外
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ActiveForm::HitArea( _in<float> in_top_mx, _in<float> in_top_my ) const
{
  float4 rt_win;
  size_t num = m_sp_view_form_table->size();

  //CPL_LOOP2( num, idx )
  for( auto& sp_view_form : *m_sp_view_form_table )
  {
    rt_win = sp_view_form->GetRectCoord();
    //
    MappingPosition
      ( sp_view_form->GetBelongComponent( eParentType::Position )
      , nullptr
      , (float2_ptr)&rt_win
      , 2
      );

    if( rt_win.IsHit( in_top_mx, in_top_my ) )
    {
      return true;
    }
  }

//  ::GetWindowRect( GetWindowHandle(), &rt_win );
  rt_win = GetRectCoord();

  MappingPosition
    ( GetBelongComponent( eParentType::Position )
    , nullptr
    , (float2_ptr)&rt_win
    , 2
    );

  return rt_win.IsHit( in_top_mx, in_top_my );
}


// _____________________________________________________________________________
/// @brief 自身が属する矩形を取得
/// 
/// @param[out] out_rt_subject
///   : 取得する座標を格納するためのfloat4型への参照を渡します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ActiveForm::GetSubjectRect( rout<float4> out_rt_subject ) const
{
  if( auto sp_comp_belong = GetBelongComponent( eParentType::Position ) )
  {
    sp_comp_belong->GetRectCoord( out_rt_subject );
  }
}

// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::CreateClone
/// @note in_offset_position_from_originalは無視されます。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<Component>
  ActiveForm::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,out] io_sp_comp_belong
/////   : 属したいコンポーネントのスマートポインタを渡します。
///// 
///// @return なし
//// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//void ActiveForm::SetupBelongComponent_for_Position( _in<sdptr<Component>> io_sp_comp_belong )
//{
//  if( io_sp_comp_belong == this->GetLinkMyself() )
//  {
//    return;
//  }
//
//  sdptr<Owner> sp_owner =
//    std::dynamic_pointer_cast<Owner>(io_sp_comp_belong->GetOwnerWindow());
//  sdptr<Component> sp_parent =
//    sp_owner ? sp_owner->GetComponent_ArrangeLayer()
//             : io_sp_comp_belong;
//  //
//  sp_parent->AddLinkChild( GetLinkMyself() );
//  //
//  CPL_GUC_ACTIVE_FORM_BASE::SetupBelongComponent_for_Position( io_sp_comp_belong, false );
//}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::WindowEventInterface::On_Show
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  ActiveForm::On_Show
    ( _in<sdptr<Component>> in_sp_component
    , _in<BOOL            > in_show_flag
    )
{
  if( in_sp_component != GetLinkMyself() )
  {
    return eEventReturnID::LastCall;
  }

  if( in_show_flag )
  {
//    SetTimer( GetWindowHandle(), 0, 100, nullptr );
    SetTimer( eID::Tid_ActiveFormShow, 100 );

    m_hide_count = 0;

    // 画面外に出ないように座標補正。
    auto sp_comp_parent = GetBelongComponent( eParentType::Position );
    auto top_win_size   = GetSubjectWindow()->GetClientSize();
    float4
      rt_top_win
        ( 0, 0
        , static_cast<float>(top_win_size.x)
        , static_cast<float>(top_win_size.y)
        );
    float4 rt_parent = sp_comp_parent->GetRectCoord();
    MappingPosition
      ( nullptr
      , sp_comp_parent
      , (float2_ptr)&rt_top_win
      , 2
      );
    //rt_parent.z = rt_parent.Width();
    //rt_parent.w = rt_parent.Height();
    //rt_parent.x = 0;
    //rt_parent.y = 0;
    //MappingPosition
    //  ( sp_comp_parent->GetLinkParent()
    //  , nullptr
    //  , (float2_ptr)&rt_parent
    //  , 2
    //  );
    // 自身を最前面（最後尾）へ
    //MoveLinkEnd();
    ActivateTopOrder();
    sp_comp_parent->ActivateTopOrder();

    auto size_self = this->GetSize();
    float2 pos;
    float const x_left           = static_cast<short>(-size_self.x); // 左配置の場合のｘ座標。
    float const x_right          = rt_parent.z; // 右配置の場合のｘ座標。
    float const x_vertical_left  = rt_parent.z - size_self.x;
    float const x_vertical_right = 0;
    float const y_upper          = rt_parent.Height() - size_self.y; // 上配置の場合のｙ座標。
    float const y_lower          = 0; // 下配置の場合のｙ座標。
    float const y_top            = static_cast<short>(-size_self.y);
    float const y_bottom         = rt_parent.Height();

    // 初期位置補正
    switch( m_pos_mode )
    {
    case ePosMode::LeftLower:       pos = { x_left, y_lower }; break;
    case ePosMode::LeftUpper:       pos = { x_left, y_upper }; break;
    case ePosMode::RightLower:      pos = { x_right, y_lower }; break;
    case ePosMode::RightUpper:      pos = { x_right, y_upper }; break;
    case ePosMode::TopLeftSide:     pos = { x_vertical_left,  y_top }; break;
    case ePosMode::TopRightSide:    pos = { x_vertical_right, y_top }; break;
    case ePosMode::BottomLeftSide:  pos = { x_vertical_left,  y_bottom }; break;
    case ePosMode::BottomRightSide: pos = { x_vertical_right, y_bottom }; break;
    default:
      pos = this->GetPosition();
      break;
    }
    this->SetPosition( pos );
    auto rt_this = GetRectCoord(); // 現在の座標を取得
    // 横配置の場合true
    bool const is_pos_horizontal =
      ePosMode::LeftLower <= m_pos_mode  &&
                             m_pos_mode <= ePosMode::RightUpper;
    // 縦配置の場合true
    bool const is_pos_vertical =
      ePosMode::TopLeftSide <= m_pos_mode  &&
                               m_pos_mode <= ePosMode::BottomRightSide;

    // 横方向補正
    if( rt_this.x < rt_top_win.x ) // 左辺オーバーチェック
    {
      if( is_pos_horizontal )
      {
        pos.x = x_right; // 右側に移動させる。
      }
    }
    else
    if( rt_top_win.z < rt_this.z ) // 右辺オーバーチェック
    {
      if( is_pos_horizontal )
      {
        pos.x -= rt_this.z; // 左側に移動させる。
      }
      else
      if( is_pos_vertical )
      {
        pos.x += rt_top_win.z - rt_this.z; // 右辺に揃える。
      }
    }

    // 縦方向補正
    if( rt_this.y < rt_top_win.y ) // 上辺オーバーチェック
    {
      if( is_pos_vertical ) // 縦配置の場合
      {
        pos.y += rt_top_win.y - rt_this.y; // 下側に移動させる
      }
    }
    else
    if( rt_top_win.w < rt_this.w ) // 下辺オーバーチェック
    {
      if( is_pos_vertical ) // 縦配置の場合
      {
        pos.y += rt_parent.Height() - rt_this.w; // 上側に移動させる
      }
      else
      if( is_pos_horizontal ) // 横配置の場合
      {
        pos.y += rt_top_win.w - rt_this.w; // 下揃え
      }
    }

    //
    if( pos.x != 0  ||  pos.y != 0 )
    {
      this->SetPosition( pos );
      rt_this = GetRectCoord();
    }

    //
    //MappingPosition( nullptr, GetLinkMyself(), (float2_ptr)&rt_this, 2 );

    //// 表示メッセージを通知
    //SendParentMessage
    //  ( GetLinkMyself()
    //  , eID::ActiveViewItemShow
    //  , 0
    //  , (LPARAM)this
    //  , eParentType::Position
    //  );
    AddHitComponent( GetLinkMyself() );
  }
  else // 非表示の場合
  {
    // タイマー削除
    KillTimer( eID::Tid_ActiveFormShow );
    
    // 自身を最背面（先頭）へ
    MoveLinkTop();

    // 非表示メッセージを通知（親を非表示にしていく）
    SendParentMessage
      ( this->GetLinkMyself()
      , eID::ActiveViewItemHide
      , 0
      , (LPARAM)this
      , eParentType::Position
      );

    //// 下位のアクティブフォームは非表示とする。
    //for( auto& afm : m_view_form_table )
    //{
    //  afm->SetStateVisible( false );
    //}
    DelHitComponent( GetLinkMyself() );
  }

  return eEventReturnID::LastCall;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::WindowEventInterface::On_Timer
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID ActiveForm::On_Timer( _in<int> in_timer_ID )
{
  // マウス座標（ルート座標系）
  float2 mpos
    ( static_cast<float>(WIN::Mouse::GetX())
    , static_cast<float>(WIN::Mouse::GetY())
    );

  //
  auto sp_comp = GetBelongComponent( eParentType::Position );
  // 自身と属するコンポーネントの座標をグローバル座標に変換
  auto rt_this   = GetRectCoord();
  auto rt_belong = sp_comp->GetRectCoord();
  MappingPosition( sp_comp,    nullptr, (float2_ptr)&rt_this, 2 );
  MappingPosition( sp_comp->GetLinkParent(), nullptr, (float2_ptr)&rt_belong, 2 );

  //
  int flag_hit = rt_belong.IsHit( mpos.x, mpos.y );
  if( !flag_hit )
  {
    flag_hit = HitArea( mpos.x, mpos.y );
  }

  //
  if( !flag_hit )
  {
    ++m_hide_count;
    if( m_hide_count >= 5 )
    {
      SetStateVisible( false );
      Update();
    }
  }
  else
  {
    m_hide_count = 0;
  }

  return eEventReturnID::LastCall;
}


//// _____________________________________________________________________________
///// @copydoc CPL::GUC::Component::On_UserProc
//// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//eEventReturnID
//  ActiveForm::On_UserProc
//    ( _in<sdptr<Component>> in_sp_component
//    , _in<long            > in_message
//    , _in<WPARAM          > in_wparam
//    , _in<LPARAM          > in_lparam
//    )
//{
//  switch( in_message )
//  {
//  case eID::ActiveViewItemShow:
//    if( in_lparam )
//    {
//      AddHitComponent( in_sp_component );
//      return eEventReturnID::SendParent_Position;
//    }
//    break;
//
//  case eID::ActiveViewItemHide:
//    if( in_lparam )
//    {
//      DelHitComponent( in_sp_component );
//      return eEventReturnID::SendParent_Position;
//    }
//    break;
//    
//  default:
//    SendParentMessage
//      ( in_sp_component, in_message, in_wparam, in_lparam
//      , eParentType::Position
//      );
//    break;
//  }
//
//  return eEventReturnID::LastCall;
//}

/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// アクティブフォーム　キーダウンイベント　※ 全て親に渡す
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
long ActiveForm::On_KeyDown( SpComponent in_sp_component, int in_virtual_key_code )
{
//  MSG_PARAM* ptr_msg = this->m_message_stack.GetActive();
  
//  SendParentMessage( ptr_msg->msg, ptr_msg->wp, ptr_msg->lp );

  return false;
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// アクティブフォーム　キーアップイベント　※ 全て親に渡す
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
long ActiveForm::On_KeyUp( SpComponent in_sp_component, int in_virtual_key_code )
{
//  MSG_PARAM* ptr_msg = this->m_message_stack.GetActive();
  
//  SendParentMessage( ptr_msg->msg, ptr_msg->wp, ptr_msg->lp );

  return false;
}
*/


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::On_DrawBackground
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ActiveForm::On_DrawBackground( _in<float4> in_rect )
{
  td_base::On_DrawBackground( in_rect );
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::On_DrawFrame
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ActiveForm::On_DrawFrame( _in<float4> in_rect )
{
  td_base::On_DrawFrame( in_rect );
}




} // namespace GUC
} // namespace CPL

