/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/11/27
/// 
/// @brief  メニューを簡易的に扱う。
/// 
/// ****************************************************************************



#include "CPL_WIN_Menu.h"



namespace CPL {
namespace WIN {
  

// _____________________________________________________________________________
/// @brief メニュークラス コンストラクタ
/// 
/// @param[in,out] in_ptr_menu_parent : 親メニューのポインタ。
/// @param[in] in_ptr_str    : メニューの文字列。
/// @param[in] in_menu_ID         : メニューID.
/// @param[in] in_flag_popup
///   : ポップアップメニューかどうか。ルートの場合のみfalseでいいはず。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Menu::Menu
( pio<Menu    > in_ptr_menu_parent
, pin<str_type> in_ptr_str
, _in<int     > in_menu_ID
, _in<int     > in_flag_popup
)
{
  // 初期化
  m_index         = -1;
  m_label_string  = in_ptr_str;
  memset( &m_menu_info, 0, sizeof(MENUITEMINFO) );
  m_menu_info.cbSize = sizeof(MENUITEMINFO);
  m_menu_info.fMask  = MIIM_DATA | MIIM_ID | MIIM_SUBMENU | MIIM_TYPE | MIIM_CHECKMARKS | MIIM_STATE;
  if( m_label_string == SwS("-") )
  {
    m_menu_info.fType = MFT_SEPARATOR;
  }
  else
  {
    m_menu_info.fType = MFT_STRING;
  }
//  m_menu_info.fState = MFS_DEFAULT;
  m_menu_info.wID        = in_menu_ID;
  m_menu_info.dwTypeData = (LPTSTR)&m_label_string[ 0 ];
  m_menu_info.cch        = static_cast<UINT>(m_label_string.length());

  if( in_ptr_menu_parent == nullptr  &&  !in_flag_popup )
  {
    m_hdl_menu = ::CreateMenu();
  }
  else
  {
    m_hdl_menu = ::CreatePopupMenu();
  }
  if( m_hdl_menu == nullptr )
  {
    CPL_THROW( CPL_WIN_MSG_NOT_CREATE_MENU );
  }
};



// _____________________________________________________________________________
/// @brief デストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Menu::~Menu()
{
  // 子供メニューを削除
  Menu* pMenuChild = GetNodeChild();
  while( pMenuChild )
  {
    DEL( pMenuChild, pMenuChild->GetNodeNext() );
  }
  Detach(); // 自分をリストからはずす
  if( m_hdl_menu )
  {
    ::DestroyMenu( m_hdl_menu );
  }
}


// _____________________________________________________________________________
/// @brief アイテム（子供）メニューのポインタを取得
/// 
/// @param[in] in_index : 取得したメニューのインデックスを指定。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Menu const* Menu::zzf_getItem_Idx_( int in_index ) const
{
  if( FAILED_INDEX(in_index) )
  {
    return nullptr;
  }

  Menu const* p = GetNodeChild();
  while( p  &&  in_index > 0 )
  {
    p = p->GetNodeNext();
    in_index--;
  }
  return p;
}


// _____________________________________________________________________________
/// @brief アイテム（子供）メニューのポインタを取得
/// 
/// @param[in] in_menu_ID : 取得したいメニューのIDを指定。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Menu const* Menu::zzf_getItem_ID_( int in_menu_ID ) const
{
  Menu const* ptr_menu = GetNodeChild();
  while( ptr_menu )
  {
    if( ptr_menu->GetNodeChild() )
    {
      Menu const* ptr_menu_child = ptr_menu->GetItem_ID( in_menu_ID );
      if( ptr_menu_child )
      {
        return ptr_menu_child;
      }
    }
    if( ptr_menu->GetID() == in_menu_ID )
    {
      return ptr_menu;
    }
    ptr_menu = ptr_menu->GetNodeNext();
  }

  return nullptr;
}



// _____________________________________________________________________________
/// @brief メニュー追加
/// 
/// @param[in] in_ptr_str : 追加するメニューの表示文字列を指定します。
/// @param[in] in_menu_ID : 追加するメニューのIDを指定します。
/// @param[in] in_index   : 挿入位置のインデックスを指定します。
/// 
/// @return 追加されたメニューオブジェクトのポインタが返ります。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Menu*
  Menu::InsertItem
    ( _in<str_cptr> in_ptr_str
    , _in<int     > in_menu_ID
    , _in<int     > in_index
    )
{
  auto index = in_index;
  Menu* ptr_menu_new = CPL_NEW(Menu)( (Menu*)this, in_ptr_str, in_menu_ID ); // 子メニューを作成

  if( FAILED_INDEX(in_index) )
  {
    if( GetNodeChild() )
    {
      index = GetNodeChild()->GetNodeClassNum();
    }
    else
    {
      index = 0;
    }
  }
  Menu* tmp_ptr_menu = GetItem_Idx( index );
  if( auto tmp_ptr_menu = GetItem_Idx( index ) )
  {
    tmp_ptr_menu->AddNodePrev( ptr_menu_new );
  }
  else
  {
    AddNodeChild( ptr_menu_new );
  }

  // パラメータ設定
  ptr_menu_new->m_index = index;

  // インデックス変更
  tmp_ptr_menu = ptr_menu_new;
  while( tmp_ptr_menu->GetNodeNext() )
  {
    tmp_ptr_menu->m_index++;
    tmp_ptr_menu = tmp_ptr_menu->GetNodeNext();
  }
  
  // メニュー追加
  if( !::InsertMenuItem( m_hdl_menu, index, true, &ptr_menu_new->m_menu_info ) )
    return nullptr;

  if( m_ptr_parent )
  {
    ChangeItem( &m_label_string[0], m_menu_info.wID );
  }

  return ptr_menu_new;
}


// _____________________________________________________________________________
/// @brief メニュー追加（メニューオブジェクト指定バージョン）
/// 
/// @param[in] in_ptr_menu : 追加するメニューオブジェクトのポインタを指定します。
/// @param[in] in_index    : 挿入位置のインデックスを指定します。
/// 
/// @return 追加されたメニューオブジェクトのポインタが返ります。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Menu*
  Menu::InsertItem
    ( pio<Menu> in_ptr_menu
    , _in<int > in_index
    )
{
  if( in_ptr_menu == nullptr )
  {
    return nullptr;
  }
//  if( in_ptr_menu->_GetParent() ){
//    in_ptr_menu->_GetParent()->Remove( in_ptr_menu->GetIdx() );
//  }

  Menu* p = GetItem_Idx( in_index );
  if( p )
  {
    p->AddNodePrev( in_ptr_menu );
  }
  else
  {
    AddNodeChild( in_ptr_menu );
  }
  
  // パラメータ設定
  in_ptr_menu->m_index = in_index;

  // インデックス変更
  p = in_ptr_menu;
  while( p )
  {
    p->m_index++;
    p = p->GetNodeNext();
  }
  
  // メニュー追加
  if( !::InsertMenuItem( m_hdl_menu, in_index, true, &in_ptr_menu->m_menu_info ) )
    return nullptr;

  return in_ptr_menu;
}


// _____________________________________________________________________________
/// @brief メニュー内容を変更
/// 
/// @param[in] in_string  : 変更後の文字列を指定。
/// @param[in] in_menu_ID : 変更するメニューのIDを指定します。
/// 
/// @retval true  : 内容が変更された。
/// @retval false : 内容変更に失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Menu::ChangeItem( _in<Core::String> in_string, _in<int> in_menu_ID )
{
  if( m_ptr_parent == nullptr )
    return false;

  m_label_string        = in_string;
  m_menu_info.wID = in_menu_ID;
  if( GetNodeChildNum() > 0 )
  {
    m_menu_info.hSubMenu = m_hdl_menu;
  }
  m_menu_info.dwTypeData = (LPTSTR)&m_label_string[ 0 ];
  m_menu_info.cch        = static_cast<UINT>(m_label_string.length());
  if( !::SetMenuItemInfo( GetNodeParent()->m_hdl_menu, m_index, true, &m_menu_info ) )
    return false;

  return true;
}


// _____________________________________________________________________________
/// @brief 指定したウィンドウへメニューを設定
/// 
/// @param[in] in_hdl_window : 関連付けるウィンドウのハンドルを指定します。
/// 
/// @retval true  : 成功。
/// @retval false : 失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Menu::Set( _in<HWND> in_hdl_window )
{
  if( !::SetMenu( in_hdl_window, m_hdl_menu ) )
    return false;
  return true;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ステート設定
// id == -1 の場合自分、ＩＤを指定していた場合は子から検索
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Menu::zzf_SetState( DWORD in_flag_state, bool sw, int in_menu_ID )
{
  if( in_menu_ID == -1 )
  {
    if( !m_ptr_parent )
    {
      return false;
    }
    if( sw )
    {
      m_menu_info.fState |= in_flag_state;
    }
    else
    {
      m_menu_info.fState &= ~in_flag_state;
    }

    if( ::SetMenuItemInfo( GetNodeParent()->m_hdl_menu, GetID(), false, &m_menu_info ) == 0 )
    {
      return false;
    }
    return true;
  }
  else // IDが指定されている場合
  {
    Menu* p = GetItem_ID( in_menu_ID );
    if( p )
    {
      if( sw )
      {
        p->m_menu_info.fState |= in_flag_state;
      }
      else
      {
        p->m_menu_info.fState &= ~in_flag_state;
      }
      if( ::SetMenuItemInfo( p->GetNodeParent()->m_hdl_menu, in_menu_ID, false, &p->m_menu_info ) == 0 )
      {
        return false;
      }
      return true;
    }
    return false;
  }
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ステート取得
// id == -1 の場合自分、ＩＤが指定していた場合は子から検索
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Menu::zzf_GetState( DWORD in_flag_state, int in_menu_ID ) const
{
  if( in_menu_ID == -1 )
  {
    if( !m_ptr_parent )
    {
      return false;
    }
    if( m_menu_info.fState & in_flag_state )
    {
      return true;
    }
    else
    {
      return false;
    }
  }
  else // IDが指定されている場合
  {
    auto ptr_menu = GetItem_ID( in_menu_ID );
    if( ptr_menu )
    {
      if( ptr_menu->m_menu_info.fState & in_flag_state )
      {
        return true;
      }
      else
      {
        return false;
      }
    }
    return false;
  }
}


// _____________________________________________________________________________
/// @brief メニューのチェック切り替え
/// 
/// @param[in] in_flag_check
///   : true  - チェックON
///   : false - チェックOFF
/// @param[in] in_menu_ID
///   : 変更するメニューのIDを指定します。\n
///   : -1 の場合自分、ＩＤが指定していた場合は子から検索します。
/// 
/// @retval true  : 変更成功。
/// @retval false : 変更失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Menu::SetCheck( _in<BOOL> in_flag_check, _in<int> in_menu_ID )
{
  return zzf_SetState( MFS_CHECKED, in_flag_check != 0, in_menu_ID );
}


// _____________________________________________________________________________
/// @brief メニューのチェック取得
/// 
/// @param[in] in_menu_ID : 取得したいメニューのメニューIDを指定します。
/// 
/// @retval true  : チェックが有効になっています。
/// @retval false : チェックが無効または該当するメニューＩＤがない。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Menu::GetCheck( _in<int> in_menu_ID ) const
{
  return zzf_GetState( MFS_CHECKED, in_menu_ID );
}


// _____________________________________________________________________________
/// @brief メニューの淡色表示設定
/// 
/// @param[in] in_flag_gray : 淡色設定フラグを指定します。
///   : true  - 淡色にします。\n
///   : false - 淡色にしない（通常の状態）。
/// @param[in] in_menu_ID
///   : メニューIDを指定します。
/// 
/// @retval true  : 設定が反映された。
/// @retval false : エラー（メニューIDが該当しないなど）。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

bool Menu::SetGray( _in<bool> in_flag_gray, _in<int> in_menu_ID )
{
  return zzf_SetState( MFS_GRAYED, in_flag_gray, in_menu_ID );
}


// _____________________________________________________________________________
/// @brief メニューの淡色フラグの取得
/// 
/// @param[in] in_menu_ID : 取得したいメニューのメニューIDを指定します。
/// 
/// @retval true  : 淡色状態である。
/// @retval false : 淡色状態ではない、または該当するメニューＩＤがない。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Menu::GetGray( _in<int> in_menu_ID ) const
{
  return zzf_GetState( MFS_GRAYED, in_menu_ID );
}



} // namespace WIN
} // namespace CPL

