/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/11/16
/// 
/// @brief  Win32 MDIウィンドウ管理
/// 
/// ****************************************************************************


#include "CPL_WIN_MdiWindow.h"


namespace CPL {
namespace WIN {


// _____________________________________________________________________________
/// @brief デフォルトコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MdiClientWindow::MdiClientWindow()
  : Window( true )
{
  m_flag_auto_resize = true;
  memset( &m_client_rect, 0, sizeof(m_client_rect) );
}


// _____________________________________________________________________________
/// @brief MDIクライアントウィンドウ作成
/// 
/// @param[in] in_hdl_win_parent
/// 
/// @retval true  : 成功。
/// @retval false : 失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool MdiClientWindow::CreateMdiClient( _in<HWND> in_hdl_win_parent )
{
  m_ccs_client.hWindowMenu  = nullptr;
  m_ccs_client.idFirstChild = static_cast<UINT>(GetBaseObjectID());
  
  m_hdl_window = ::CreateWindow(
    SwS("MDICLIENT"), nullptr,
    WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
    0, 0, 0, 0, in_hdl_win_parent, (HMENU)GetBaseObjectID(),
    GetInstance_(), &m_ccs_client );

  if( !zzf_SetHandle( m_hdl_window ) )
  {
    CPL_THROW( CPL_WIN_MSG_NOT_CREATE_MDICW );
//    return false; // ウィンドウ作成失敗
  }

  if( !zzf_MDICLIENT_PROC() )
  {
    zzf_MDICLIENT_PROC() = zzf_GetProc( m_hdl_window );
  }
  zzf_SetProc( zzf_StaticProcMdiClient );

//  On_Create(); // 一応呼び出し

  return true;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// MDIクライアント用のデフォルトプロシージャイベント関数
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
  MdiClientWindow::zzf_DefaultProcMdiClient
    ( UINT   in_message
    , WPARAM in_wparam
    , LPARAM in_lparam
    )
{
  if( zzf_GetMessageThrow() )
  {
    if( in_message == WM_COMMAND  ||
      in_message == WM_NOTIFY   ||
      in_message == WM_HSCROLL  ||
      in_message == WM_VSCROLL  ||
      in_message >= kID_CPL_USER )
    {
      return (::SendMessage( this->GetParentWindowHandle(), in_message, in_wparam, in_lparam ) != 0);
    }
  }
  return false;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// MDIクライアントウィンドウ用プロシージャ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LRESULT CALLBACK
  MdiClientWindow::zzf_StaticProcMdiClient
    ( HWND   in_hdl_window
    , UINT   in_message
    , WPARAM in_wparam
    , LPARAM in_lparam
    )
{
  MdiClientWindow*
    ptr_mdi_clt_window =
      dynamic_cast<MdiClientWindow*>(GetHandleToPointer(in_hdl_window));
  bool flag = false;
  if( ptr_mdi_clt_window )
  {
    if( ptr_mdi_clt_window->GetEventMode() != eEventMode::AllCut )
    {
      flag = ptr_mdi_clt_window->zzf_DefaultProcMdiClient( in_message, in_wparam, in_lparam );
    }
    else
    {
      return 0;
    }
  }
  if( !flag )
  {
    return
      ::CallWindowProc
        ( zzf_MDICLIENT_PROC()
        , in_hdl_window
        , in_message
        , in_wparam
        , in_lparam
        );
  }
  return true;
}


// _____________________________________________________________________________
/// @copydoc CPL::WIN::Component::SetWindowPos
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  MdiClientWindow::SetWindowPos
    ( _in<td_Coord2> in_pos
    , _in<bool     > in_flag_redraw 
    )
{
  if( m_flag_auto_resize )
  {
    auto par_size = GetLinkParent()->GetClientSize();
    td_Coord2 size
      ( par_size.x - static_cast<int>(m_client_rect.right + m_client_rect.left)
      , par_size.y - static_cast<int>(m_client_rect.bottom + m_client_rect.top)
      );
    m_client_rect.left  = in_pos.x;
    m_client_rect.top   = in_pos.y;
    ::MoveWindow( m_hdl_window, in_pos.x, in_pos.y, size.x, size.y, in_flag_redraw );
  }
  else
  {
    td_Coord2 size
      ( static_cast<int>(m_client_rect.right - m_client_rect.left)
      , static_cast<int>(m_client_rect.bottom - m_client_rect.top)
      );
    m_client_rect.left   = in_pos.x;
    m_client_rect.top    = in_pos.y;
    m_client_rect.right  = in_pos.x + size.x;
    m_client_rect.bottom = in_pos.y + size.y;
    ::MoveWindow( m_hdl_window, in_pos.x, in_pos.y, size.x, size.y, in_flag_redraw );
  }
}


// _____________________________________________________________________________
/// @copydoc CPL::WIN::Component::SetWindowSize
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  MdiClientWindow::SetWindowSize
    ( _in<td_Coord2> in_size
    , _in<bool     > in_flag_redraw
    , _in<bool     > in_flag_client_size
    )
{
  auto size = in_size;
  if( m_flag_auto_resize )
  {
    td_Coord2 par_size = GetLinkParent()->GetClientSize();

    m_client_rect.right  = size.x;
    m_client_rect.bottom = size.y;
    size.x = par_size.x - static_cast<short>(m_client_rect.left + m_client_rect.right);
    size.y = par_size.y - static_cast<short>(m_client_rect.bottom + m_client_rect.top);

    ::MoveWindow( m_hdl_window,
      m_client_rect.left, m_client_rect.top,
      size.x, size.y, in_flag_redraw );
  }
  else
  {
    m_client_rect.right  = m_client_rect.left + size.x;
    m_client_rect.bottom = m_client_rect.top  + size.y;

    ::MoveWindow
      ( m_hdl_window
      , m_client_rect.left, m_client_rect.top
      , size.x, size.y, in_flag_redraw
      );
  }
}


// _____________________________________________________________________________
/// @brief ウィンドウ座標の設定
/// 
/// @copydetails doxycomment::arg_in_pos
/// @copydetails doxycomment::arg_in_size
/// @copydetails doxycomment::arg_in_flag_redraw
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  MdiClientWindow::SetWindowRect
    ( _in<td_Coord2> in_pos
    , _in<td_Coord2> in_size
    , _in<bool     > in_flag_redraw
    )
{
  m_client_rect.left   = in_pos.x;
  m_client_rect.top    = in_pos.y;
  m_client_rect.right  = in_pos.x + in_size.x;
  m_client_rect.bottom = in_pos.y + in_size.y;

  if( m_flag_auto_resize )
  {
    auto par_size = GetLinkParent()->GetClientSize();

    ::MoveWindow
      ( m_hdl_window, in_pos.x, in_pos.y
      , par_size.x-(in_pos.x+in_size.x), par_size.y-(in_pos.y+in_size.y)
      , in_flag_redraw
      );
  }
  else
  {
    ::MoveWindow
      ( m_hdl_window
      , in_pos.x, in_pos.y
      , in_size.x, in_size.y
      , in_flag_redraw
      );
  }
}










// _____________________________________________________________________________
/// @brief MdiWindow デフォルトコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MdiWindow::MdiWindow()
{
  CPL_BLOCK_ONCE
  {
    // MDIクライアント用の設定
    WNDCLASSEX wcex;
    memset( &wcex, 0, sizeof(wcex) );
    wcex.cbSize        = sizeof(WNDCLASSEX);
    wcex.hInstance     = GetInstance_();
    wcex.style         = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc   = (WNDPROC)zzf_StaticProcMdi;
    wcex.lpszClassName = SwS("CPL_WIN_MdiWindow");
    wcex.hCursor       = nullptr;
    wcex.hbrBackground = nullptr;
    wcex.hIcon         = nullptr;

    if( !::RegisterClassEx( &wcex ) )
    {
      throw 0;
    }
  }

  ::memset( &m_ccs_client, 0, sizeof(m_ccs_client) );
  m_flag_mdi_client = false;
}


// _____________________________________________________________________________
/// @brief MdiWindow デストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MdiWindow::~MdiWindow()
{
  // クライアントウィンドウの解放
  size_t num = m_client_window_table.GetNum();
  CPL_LOOP2( num, idx )
  {
    if( m_client_window_table[ idx ] )
    {
      DEL( m_client_window_table[ idx ] );
    }
  }
}



// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// MDIクライアント用プロシージャ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LRESULT CALLBACK
  MdiWindow::zzf_StaticProcMdi
    ( HWND   in_hdl_window
    , UINT   in_message
    , WPARAM in_wparam
    , LPARAM in_lparam
    )
{
  bool flag = false;
  MdiWindow* ptr_mdi_window = nullptr;
//  static HWND hWndClient = nullptr;

  if( in_message == WM_CREATE )
  {
    CREATESTRUCT* cs = (CREATESTRUCT*)in_lparam;
    Window* ptr_window = static_cast<Window*>(cs->lpCreateParams);
    ptr_mdi_window = dynamic_cast<MdiWindow*>(ptr_window);
    if( ptr_mdi_window )
    {
      CallStackMsg csm( ptr_mdi_window, in_message, in_wparam, in_lparam );

      ptr_mdi_window->m_hdl_window = in_hdl_window;

      // クライアントウィンドウ作成
      ptr_mdi_window->m_client_window_table.Add( 0, ptr_mdi_window->On_CreateMdiClient() ); // デフォルトではＩＤ０を割り振る
      MdiClientWindow* ptr_mdi_clt_window = ptr_mdi_window->GetClientWindow( 0 );
      if( ptr_mdi_clt_window )
      {
        ptr_mdi_clt_window->CreateMdiClient( ptr_mdi_window->GetWindowHandle() );
      }

      ptr_mdi_window->zzf_SetHandle( in_hdl_window );
      return ptr_mdi_window->On_Create();
    }
  }
  else
  {
    ptr_mdi_window = dynamic_cast<MdiWindow*>(GetHandleToPointer( in_hdl_window ));
    if( ptr_mdi_window )
    {
      if( ptr_mdi_window->GetEventMode() != eEventMode::AllCut ) // イベントが有効なら
      {
        if( in_message == WM_SIZE )
        {
          // 全てのクライアントウィンドウを検査し、自動リサイズが有効ならば、サイズ調整する
          size_t num = ptr_mdi_window->m_client_window_table.GetNum();
          CPL_LOOP2( num, i )
          {
            MdiClientWindow* ptr_mdi_clt_window = ptr_mdi_window->m_client_window_table[ i ];

            if( ptr_mdi_clt_window->GetAutoResize() )
            {
              Rect rt;
              ptr_mdi_clt_window->GetAutoResizeRect( rt );
              ptr_mdi_clt_window->SetWindowSize
                ( { static_cast<int>(rt.right)
                  , static_cast<int>(rt.bottom) }
                );
            }
          }
        }

        flag = ptr_mdi_window->DefaultProc_( in_message, in_wparam, in_lparam ) != 0;
      }
      else // イベント無効
      {
        return 0;
      }
    }
  }

  if( !flag )
  {
    ptr_mdi_window = dynamic_cast<MdiWindow*>(GetHandleToPointer( in_hdl_window ));
    if( ptr_mdi_window )
    {
      size_t  num = ptr_mdi_window->m_client_window_table.GetNum();
      HWND    hdl_win_client = nullptr;
      LRESULT ret = 0;
      CPL_LOOP2( num, i )
      {
        hdl_win_client = ptr_mdi_window->m_client_window_table[ i ]->GetWindowHandle();
        ret = ::DefFrameProc( in_hdl_window, hdl_win_client, in_message, in_wparam, in_lparam );
      }
      return ret;
    }
    else
    {
      return ::DefFrameProc( in_hdl_window, nullptr, in_message, in_wparam, in_lparam );
    }
  }
  return 0;
}


// _____________________________________________________________________________
/// @brief MDIウィンドウ作成
/// 
/// @param[in] in_pos  : ウィンドウの左上の座標を指定します。
/// @param[in] in_size : ウィンドウのサイズを指定します。
/// @param[in] in_flag_client_size
///   : 指定されたサイズがウィンドウサイズかクライアントサイズか指定します。\n
///   : true  - クライアントサイズ\n
///   : false - ウィンドウサイズ
/// @param[in] in_win_style
///   : ウィンドウスタイルのフラグの組み合わせを指定します。\n
///   : 詳しくはCPL::WIN::STYLEを参照してください。
/// 
/// @return 
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
  MdiWindow::CreateMdi
    ( _in<td_Coord2> in_pos
    , _in<td_Coord2> in_size
    , _in<bool     > in_flag_client_size
    , _in<STYLE    > in_win_style
    )
{
  m_flag_mdi_client = true;
  auto res_create =
    zzf_Create
      ( nullptr, SwS("")
      , in_pos, in_size, 0
      , in_win_style.style
      , in_win_style.ExStyle.style
      , this
      );
  if( !res_create )
  {
    return false;
  }

  if( in_flag_client_size )
  {
    SetWindowSize( in_size, true, true );
  }
  
  return true;
}




} // namespace WIN
} // namespace CPL