/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/11/27
/// 
/// @brief  Win32イメージ関連
/// 
/// ****************************************************************************


#include "CPL_WIN_ImageBox.h"


namespace CPL {
namespace WIN {


// _____________________________________________________________________________
/// @brief コンストラクタ　イメージボックスコンポーネントの作成。
/// 
/// ※ 親ウィンドウにはなれない。
/// 
/// @param[in] in_hdl_win_parent
///   : 親ウィンドウハンドル。
/// @param[in] in_pos
///   : コンポーネントの左上の座標を指定します。
/// @param[in] in_size
///   : コンポーネントのサイズを指定します。
/// @param[in] in_back_color
///   : 背景カラー。\n
///   : 32bit整数、またはRGB(r,g,b)の形で表現する。
/// @param[in] in_flag_client_size
///   : サイズがクライアントサイズか、ウィンドウサイズかを指定する。\n
///   : true  - クライアントサイズ。\n
///   : false - ウィンドウサイズ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ImageBox::ImageBox
( _in<HWND     > in_hdl_win_parent
, _in<td_Coord2> in_pos
, _in<td_Coord2> in_size
, _in<COLORREF > in_back_color
, _in<bool     > in_flag_client_size
)
{
  // 初期化
  m_ptr_back_surface = nullptr;
  m_hdl_DC           = nullptr;

  // 親ウィンドウハンドル検査
  if( in_hdl_win_parent == nullptr )
  {
    CPL_THROW( CPL_WIN_MSG_NOT_CREATE_IMGWINDOW );
  }

  // イメージボックス設定
//  SetBackColor( in_back_color ); // 背景色設定
  // イメージボックス作成
  if( !zzf_Create( in_hdl_win_parent, SwS(""), in_pos, in_size, -1, WS_CHILD | WS_HSCROLL | WS_VSCROLL, 0, this ) )
  {
    CPL_THROW( CPL_WIN_MSG_NOT_CREATE_IMGBOX );
  }
  if( in_flag_client_size )
  {
    if( in_size.x > 0  &&  in_size.y > 0 )
    {
      SetWindowSize( in_size, true, true );
    }
    else
    {
      SetWindowSize( GetWindowSize(), true, true );
    }
  }
  SetBgColor( in_back_color );

  // 作成されたウィンドウのクライアントサイズを取得
  m_canval_size  = GetClientSize();
  // デフォルトスケール
  m_canvas_scale = 1.0f;
  //
  m_alpha = 1.0f;

  // スクロールバー設定
  SCROLLINFO sc;
  sc.cbSize    = sizeof(sc);
  sc.fMask     = SIF_ALL;
  sc.nMax      = m_canval_size.x;
  sc.nMin      = 0;
  sc.nPage     = sc.nMax + 1;
  sc.nPos      = 0;
  sc.nTrackPos = 0;
  ::SetScrollInfo( m_hdl_window, SB_HORZ, &sc, true );
  sc.nMax      = m_canval_size.y;
  sc.nPage     = sc.nMax + 1;
  ::SetScrollInfo( m_hdl_window, SB_VERT, &sc, true );

  this->m_hdl_DC = ::GetDC( this->m_hdl_window );
  // バックサーフェイス
  m_ptr_back_surface = CPL_NEW(HdcSurface)( this->m_hdl_window );
  if( m_ptr_back_surface == nullptr )
  {
    CPL_THROW( CPL_WIN_MSG_NOT_CREATE_IMGBOX );
  }
  m_back_color = in_back_color;
  ::SetBkColor( m_ptr_back_surface->GetDC(), in_back_color );
  // バックバッファ用イメージ？
  m_ptr_back_bmp =
    CPL_NEW(HdcBitmap)( m_hdl_DC, m_canval_size.x, m_canval_size.y );
  if( m_ptr_back_bmp == nullptr )
  {
    CPL_THROW( CPL_WIN_MSG_NOT_CREATE_IMGBOX );
  }

  // 関連付け
  ::SelectObject( m_ptr_back_surface->GetDC(), m_ptr_back_bmp->GetBmp() );

  Clear();
}


// _____________________________________________________________________________
/// @brief デストラクタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ImageBox::~ImageBox()
{
  DEL( m_ptr_back_surface );
  DEL( m_ptr_back_bmp );
  if( m_hdl_DC )
  {
    ReleaseDC( m_hdl_window, m_hdl_DC );
  }
}




// _____________________________________________________________________________
/// @brief イメージボックス描画イベント
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF ImageBox::On_Paint()
{
  Start();
  
  On_DrawCanvas( m_ptr_back_surface->GetDC() ); // 描画関数へ

  Renew();

  On_DrawFront( m_hdl_DC );

  End();
  return true;
}



// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// イメージボックスサイズ変更処理
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF ImageBox::On_Size( _in<eSizeMode> in_size_mode )
{
  if( m_ptr_back_surface  &&  m_ptr_back_bmp )
  {
    SetCanvasSize( GetClientSize() - td_Coord2(1,1) );
  }
  zzf_AdjustScrollBar();
  return true;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// イメージボックス スクロールバー調整
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ImageBox::zzf_AdjustScrollBar()
{
  auto size_client = GetClientSize();
  // スクロールバー調整
  SCROLLINFO sc;
  sc.cbSize = sizeof(sc);
  sc.fMask  = SIF_PAGE | SIF_RANGE;
  // 横スクロールバー
  sc.nMin   = 0;
  sc.nMax   = static_cast<int>(m_canval_size.x * m_canvas_scale);
  sc.nPage  = size_client.x;
  ::SetScrollInfo( m_hdl_window, SB_HORZ, &sc, true );
  if( m_canvas_top.x > sc.nMax - (int)sc.nPage )
  {
    m_canvas_top.x = Max( sc.nMax - (int)sc.nPage, sc.nMin );
  }
  else
  {
    m_canvas_top.x = 0;
  }

  // 縦スクロールバー
  sc.nMax   = static_cast<int>(m_canval_size.y * m_canvas_scale);
  sc.nPage  = size_client.y;
  ::SetScrollInfo( m_hdl_window, SB_VERT, &sc, true );
  if( m_canvas_top.y > sc.nMax - (int)sc.nPage )
  {
    m_canvas_top.y = Max( sc.nMax - (int)sc.nPage, sc.nMin );
  }
  else
  {
    m_canvas_top.y = 0;
  }
}


// _____________________________________________________________________________
/// @brief 描画開始処理
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ImageBox::Start()
{
  m_hdl_DC = ::BeginPaint( m_hdl_window, &m_PS );
}


// _____________________________________________________________________________
/// @brief 背景塗りつぶし
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ImageBox::Clear()
{
  // 背景塗りつぶし
  COLORREF back_color = ::GetBkColor( m_ptr_back_surface->GetDC() );
  DrawBrush brush( m_ptr_back_surface->GetDC(), back_color );
  ::PatBlt( m_ptr_back_surface->GetDC(), 0, 0, m_canval_size.x, m_canval_size.y, PATCOPY );
}


// _____________________________________________________________________________
/// @brief イメージ転送
/// 
/// バックバッファイメージを関連付けられているデバイスコンテキストに描画します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ImageBox::Renew()
{
  auto size_client = GetClientSize();
  // 転送
  if( m_canvas_scale == 1.0f )
  {
    ::BitBlt( m_hdl_DC, 0, 0,
      Min<short>(size_client.x, m_canval_size.x-m_canvas_top.x), // 転送幅
      Min<short>(size_client.y, m_canval_size.y-m_canvas_top.y), // 転送高さ
      m_ptr_back_surface->GetDC(),
      m_canvas_top.x, m_canvas_top.y, SRCCOPY );
  }
  else
  {
    int img_w     = static_cast<int>(m_canval_size.x * m_canvas_scale);
    int img_h     = static_cast<int>(m_canval_size.y * m_canvas_scale);
    int source_x  = static_cast<int>(m_canvas_top.x / m_canvas_scale);
    int source_y  = static_cast<int>(m_canvas_top.y / m_canvas_scale);
    int dest_w    = Min<int>(size_client.x, img_w-static_cast<int>(source_x*m_canvas_scale));
    int dest_h    = Min<int>(size_client.y, img_h-static_cast<int>(source_y*m_canvas_scale));
    int source_w  = static_cast<int>(dest_w / m_canvas_scale);
    int source_h  = static_cast<int>(dest_h / m_canvas_scale);
    ::StretchBlt( m_hdl_DC,
      0, 0, dest_w, dest_h,
      m_ptr_back_surface->GetDC(),
      source_x, source_y, source_w, source_h,
      SRCCOPY );
  }
}


// _____________________________________________________________________________
/// @brief 描画終了処理
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ImageBox::End()
{
  ::EndPaint( m_hdl_window, &m_PS );
}


// _____________________________________________________________________________
/// @brief ビットマップ(DIBitmapオブジェクト)の描画
/// 
/// @param[in] in_ptr_image : 描画するDIBitmapオブジェクトへのポインタを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ImageBox::DrawImage( pin<DIBitmap> in_ptr_image )
{
  if( in_ptr_image == nullptr  ||  m_ptr_back_surface == nullptr )
  {
    return;
  }

  DIBitmap::DrawInfo di;
  di.alpha = m_alpha;
  in_ptr_image->Draw( m_ptr_back_surface->GetDC(), di );
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF ImageBox::DefaultProc_( _in<UINT> in_message, _in<WPARAM> in_wparam, _in<LPARAM> in_lparam )
{
  switch( in_message )
  {
  case WM_HSCROLL:
    {
      SCROLLINFO sc;
      sc.cbSize = sizeof(sc);
      sc.fMask  = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
      ::GetScrollInfo( m_hdl_window, SB_HORZ, &sc );
      switch( LOWORD(in_wparam) )
      {
      case SB_LINELEFT:   sc.nPos--; break;
      case SB_LINERIGHT:  sc.nPos++; break;
      case SB_PAGELEFT:   sc.nPos -= sc.nPage; break;
      case SB_PAGERIGHT:  sc.nPos += sc.nPage; break;
      case SB_THUMBTRACK:
      case SB_THUMBPOSITION: sc.nPos = sc.nTrackPos; break;
      }
      ::SetScrollInfo( m_hdl_window, SB_HORZ, &sc, true );
      m_canvas_top.x = sc.nPos;
      Update( true );
    }
    break;
  case WM_VSCROLL:
    {
      SCROLLINFO sc;
      sc.cbSize = sizeof(sc);
      sc.fMask  = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
      ::GetScrollInfo( m_hdl_window, SB_VERT, &sc );
      switch( LOWORD(in_wparam) )
      {
      case SB_LINEDOWN:  sc.nPos--; break;
      case SB_LINEUP:    sc.nPos++; break;
      case SB_PAGEDOWN:  sc.nPos -= sc.nPage; break;
      case SB_PAGEUP:    sc.nPos += sc.nPage; break;
      case SB_THUMBTRACK:
      case SB_THUMBPOSITION: sc.nPos = sc.nTrackPos; break;
      }
      ::SetScrollInfo( m_hdl_window, SB_VERT, &sc, true );
      m_canvas_top.y = sc.nPos;
      Update( true );
    }
    break;

  default:
    return Component::DefaultProc_( in_message, in_wparam, in_lparam );
  }
  return 0;
}


// _____________________________________________________________________________
/// @brief 水平スクロールバーの情報を取得
/// 
/// @param[in] in_ptr_scroll_info
///   : スクロールバーの情報を取得するためのSCROLLINFOオブジェクトのポインタを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ImageBox::GetScrollInfoH( SCROLLINFO* in_ptr_scroll_info ) const
{
  in_ptr_scroll_info->cbSize = sizeof(SCROLLINFO);
  in_ptr_scroll_info->fMask  = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
  ::GetScrollInfo( m_hdl_window, SB_HORZ, in_ptr_scroll_info );
}


// _____________________________________________________________________________
/// @brief 垂直スクロールバーの情報を取得
/// 
/// @param[in] in_ptr_scroll_info
///   : スクロールバーの情報を取得するためのSCROLLINFOオブジェクトのポインタを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ImageBox::GetScrollInfoV( SCROLLINFO* in_ptr_scroll_info ) const
{
  in_ptr_scroll_info->cbSize = sizeof(SCROLLINFO);
  in_ptr_scroll_info->fMask  = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
  ::GetScrollInfo( m_hdl_window, SB_VERT, in_ptr_scroll_info );
}


// _____________________________________________________________________________
/// @brief キャンバスサイズ設定
/// 
/// @param[in] in_size : キャンバスサイズを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ImageBox::SetCanvasSize( _in<td_Coord2> in_size )
{
  m_canval_size = in_size;
  HDC hdc = m_ptr_back_surface->GetDC();
  m_ptr_back_bmp->Resize( hdc, in_size.x, in_size.y );
  ::SetBkColor( hdc, m_back_color );
  ::SelectObject( hdc, m_ptr_back_bmp->GetBmp() ); // 再関連付け

  // スクロールバー調整
  zzf_AdjustScrollBar();
}


// _____________________________________________________________________________
/// @brief キャンバススケール設定
/// 
/// @param[in] in_scale : キャンバススケールを指定します。
/// 
/// @return 
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ImageBox::SetCanvasScale( _in<float> in_scale )
{
  m_canvas_scale = in_scale;
  zzf_AdjustScrollBar();
}



} // namespace WIN
} // namespace CPL

