/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/12/03
/// 
/// @brief  フォント周り。
/// 
/// ****************************************************************************


#include <bitset>
#include "CPL_DX_Font.h"


namespace CPL {
namespace DX {
//namespace Graph {


const int FONT_TEXTURE_WIDTH  = 512;
const int FONT_TEXTURE_HEIGHT = 512;


// _____________________________________________________________________________
/// @brief 設定付きコンストラクタ
/// 
/// メッセージ描画情報の設定。
/// 
/// @param[in] in_message
///   : 描画する文字列を指定します。
/// @param[in] in_rect_for_msg
///   : 描画範囲をRECT型で指定します。
/// @param[in] in_depth_z
///   : 描画深度を0.0～1.0の範囲で指定します。
/// @param[in] in_default_color
///   : デフォルトカラーを指定します。\n
///   : フォーマットはRGBA(32bit)×4の４点座標形式になります。
/// @param[in] in_rotate_z
///   : 描画文字列の角度をrad単位で指定します。
/// @param[in] in_rotate_center
///   : 回転中心座標（左上座標からの相対値）を指定します。
/// @param[in] in_line_h
///   : 行の幅（ラインの高さ）を指定します。\n
///   : 改行時に使用されます。
/// @param[in] in_flags
///   : 描画フラグ(CPL::DX::eFDM_Flag)の組み合わせを指定します。\n
///   : 詳しくはCPL::DX::eFDM_Flagを参照してください。
/// 
/// @return 
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FontDrawManager::MsgInfo::MsgInfo
( pin<td_DefChar> in_message
, _in<RECT      > in_rect_for_msg
, _in<float     > in_depth_z
, _in<U32x4     > in_default_color
, _in<float     > in_rotate_z
, _in<float2    > in_rotate_center
, _in<int       > in_line_h
, _in<DWORD     > in_flags
) : rect         ( in_rect_for_msg )
  , depth_z      ( in_depth_z )
  , default_color( in_default_color )
  , rotate_z     ( in_rotate_z )
  , rotate_center( in_rotate_center )
  , line_h       ( in_line_h )
  , flags        ( in_flags )
{
  SetMessage( in_message );
}


// _____________________________________________________________________________
/// @brief メッセージ設定
/// 
/// @param[in] in_message : メッセージ文字列を指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void FontDrawManager::MsgInfo::SetMessage( pin<wchar_t> in_message )
{
  if( in_message )
  {
    message = in_message;

  //  //
  //  const size_t length = Core::StringW::str_len_( message );

  //  // rotate
  //  if( flags & FDm_ID_ROTATE_ENABLE )
  //  {
  //    rotate_array.resize( length );
  //    for( auto& r : rotate_array )
  //    {
  //      r = 0;
  //    }
  //  }

  //  // color
  //  if( flags & FDm_ID_COLOR_ENABLE )
  //  {
  //    color_array.resize( length );
  //    for( auto& c : color_array )
  //    {
  //      c = this->default_color;
  //    }
  //  }

  //  // scale
  //  if( flags & FDm_ID_SCALE_ENABLE )
  //  {
  //    scale_array.resize( length );
  //    for( auto& s : scale_array )
  //    {
  //      s = float2(1.0f, 1.0f);
  //    }
  //  }
  }
}


// _____________________________________________________________________________
/// @brief 指定した文字列の角度を設定
/// 
/// @param[in] in_str : 角度を設定する文字列を指定します。
/// @param[in] in_rad_char : 文字毎の回転角度をラジアン単位で指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  FontDrawManager::MsgInfo::SetRotate_String
    ( pin<td_DefChar> in_str
    , _in<float     > in_rad_char
    )
{
  zzz_updateTableSize_( rotate_array, 0.0f );

  const size_t length_check = td_DefString::str_len_( in_str );
  // 文字列検索
  auto check_str = td_DefString::str_str_( this->message, in_str );

  while( check_str )
  {
    const size_t idx_base = check_str - this->message;
    for( size_t i = 0; i < length_check; ++i )
    {
      rotate_array[ idx_base + i ] = in_rad_char;
    }

    check_str = td_DefString::str_str_( check_str + length_check, in_str );
  }
}


// _____________________________________________________________________________
/// @brief in_index番目の要素の角度設定
/// 
/// @param[in] in_index : メッセージの設定する文字のインデックスを指定します。
/// @param[in] in_rad_char : 文字毎の回転角度をラジアン単位で指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  FontDrawManager::MsgInfo::SetRotate_Index
    ( _in<int  > in_index
    , _in<float> in_rad_char
    )
{
  zzz_updateTableSize_( rotate_array, 0.0f );

  if( (int)rotate_array.size() > in_index )
  {
    rotate_array[ in_index ] = in_rad_char;
  }
}


// _____________________________________________________________________________
/// @brief in_char_listに含まれる文字の角度設定
/// 
/// @param[in] in_char_list : 回転する文字のリストを指定します。
/// @param[in] in_rad_char : 指定文字列の回転角度をラジアン単位で指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  FontDrawManager::MsgInfo::SetRotate_Char
    ( pin<td_DefChar> in_char_list
    , _in<float     > in_rad_char
    )
{
  zzz_updateTableSize_( rotate_array, 0.0f );

  // フラグテーブル
  // ※ 大きな領域をスタックで確保するのは好ましくないため、ヒープに確保。
  //    この関数（SetRotate_Char）は静的デストラクタから呼び出されるような
  //    使い方はしないはずなので、ヒープに確保して問題ないはず。
  static std::bitset<Core::kStrMaxSize> flags;
  flags.reset();

  auto tmp_char = in_char_list;

  while( *tmp_char )
  {
    flags[ *tmp_char ] = true;
    ++tmp_char;
  }

  // メッセージ処理
  tmp_char = this->message;
  while( *tmp_char )
  {
    int n;
    U32 idx = Core::GetCharIndex_UTF16( tmp_char, n );
    if( flags[ idx ] )
    {
      rotate_array[ tmp_char - this->message ] = in_rad_char;
    }

    tmp_char += n;
  }
}


// _____________________________________________________________________________
/// @brief 指定した文字列の角度を設定
/// 
/// @param[in] in_str : 角度を設定する文字列を指定します。
/// @param[in] in_color : 文字毎の４点カラーを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  FontDrawManager::MsgInfo::SetColor_String
    ( pin<td_DefChar> in_str
    , _in<U32x4     > in_color
    )
{
  zzz_updateTableSize_( color_array, this->default_color );

  // 文字列検索
  const size_t length_check = td_DefString::str_len_( in_str );
  auto check_str = td_DefString::str_str_( this->message, in_str );

  while( check_str )
  {
    const size_t idx_base = check_str - this->message;
    //CPL_LOOP2( length_check, i )
    for( size_t i = 0; i < length_check; ++i )
    {
      color_array[ idx_base + i ] = in_color;
    }

    check_str = td_DefString::str_str_( check_str + length_check, in_str );
  }
}


// _____________________________________________________________________________
/// @brief in_index番目の要素の４点カラー設定
/// 
/// @param[in] in_index : メッセージの設定する文字のインデックスを指定します。
/// @param[in] in_color : 文字毎の４点カラーを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  FontDrawManager::MsgInfo::SetColor_Index
    ( _in<int  > in_index
    , _in<U32x4> in_color
    )
{
  zzz_updateTableSize_( color_array, this->default_color );

  if( (int)color_array.size() > in_index )
  {
    color_array[ in_index ] = in_color;
  }
}


// _____________________________________________________________________________
/// @brief in_char_listに含まれる文字の４点カラー設定
/// 
/// @param[in] in_char_list : 回転する文字のリストを指定します。
/// @param[in] in_color : 指定文字列の回転角度を４点カラーを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  FontDrawManager::MsgInfo::SetColor_Char
    ( pin<td_DefChar> in_char_list
    , _in<U32x4     > in_color
    )
{
  zzz_updateTableSize_( color_array, this->default_color );

  // フラグテーブル
  // ※ 大きな領域をスタックで確保するのは好ましくないため、ヒープに確保。
  //    この関数（SetColor_Char）は静的デストラクタから呼び出されるような
  //    使い方はしないはずなので、ヒープに確保して問題ないはず。
  static bool flag[ 0xffff ];
  memset( flag, 0, sizeof(flag) );

  auto tmp_char = in_char_list;

  while( *tmp_char )
  {
    flag[ *tmp_char ] = true;
    ++tmp_char;
  }

  // メッセージ処理
  tmp_char = this->message;
  while( *tmp_char )
  {
    if( flag[ *tmp_char ] )
    {
      color_array[ tmp_char - this->message ] = in_color;
    }

    ++tmp_char;
  }
}


// _____________________________________________________________________________
/// @brief 指定した文字列のスケール設定
/// 
/// @param[in] in_str : 角度を設定する文字列を指定します。
/// @param[in] in_scale : 指定文字列のスケールを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  FontDrawManager::MsgInfo::SetScale_String
    ( pin<td_DefChar> in_str
    , _in<float2    > in_scale
    )
{
  zzz_updateTableSize_( scale_array, float2(1.0f,1.0f) );

  // 文字列検索
  const size_t length_check = td_DefString::str_len_( in_str );
  auto check_str = td_DefString::str_str_( this->message, in_str );

  while( check_str )
  {
    const size_t idx_base = check_str - this->message;
    //CPL_LOOP2( length_check, i )
    for( size_t i = 0; i < length_check; ++i )
    {
      scale_array[ idx_base + i ] = in_scale;
    }

    check_str = td_DefString::str_str_( check_str + length_check, in_str );
  }
}


// _____________________________________________________________________________
/// @brief in_index番目の要素のスケール設定
/// 
/// @param[in] in_index : メッセージの設定する文字のインデックスを指定します。
/// @param[in] in_scale : 指定文字列のスケールを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  FontDrawManager::MsgInfo::SetScale_Index
    ( _in<int   > in_index
    , _in<float2> in_scale
    )
{
  zzz_updateTableSize_( scale_array, float2(1.0f,1.0f) );

  if( (int)scale_array.size() > in_index )
  {
    scale_array[ in_index ] = in_scale;
  }
}


// _____________________________________________________________________________
/// @brief in_char_listに含まれる文字のスケール設定
/// 
/// @param[in] in_char_list : 回転する文字のリストを指定します。
/// @param[in] in_scale : 指定文字列のスケールを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  FontDrawManager::MsgInfo::SetScale_Char
    ( pin<td_DefChar> in_char_list
    , _in<float2    > in_scale
    )
{
  zzz_updateTableSize_( scale_array, float2(1.0f,1.0f) );

  // フラグテーブル
  // ※ 大きな領域をスタックで確保するのは好ましくないため、ヒープに確保。
  //    この関数（SetScale_Char）は静的デストラクタから呼び出されるような
  //    使い方はしないはずなので、ヒープに確保して問題ないはず。
  static bool flag[ 0xffff ];
  memset( flag, 0, sizeof(flag) );

  auto tmp_char = in_char_list;

  while( *tmp_char )
  {
    flag[ *tmp_char ] = true;
    ++tmp_char;
  }

  // メッセージ処理
  tmp_char = this->message;
  while( *tmp_char )
  {
    int n;
    U32 idx = Core::GetCharIndex_UTF16( tmp_char, n );
    if( flag[ idx ] )
    {
      scale_array[ tmp_char - this->message ] = in_scale;
    }

    tmp_char += n;
  }
}


// _____________________________________________________________________________
/// @brief フォント生成付きコンストラクタ（パラメタ指定version）
/// 
/// @param[in] in_ptr_d3d_dev : DirectXデバイスのポインタを指定します。
/// @param[in] in_font_name   : フォント名を指定します。
/// @param[in] in_height      : フォントの高さを指定します。
/// @param[in] in_italic      : 斜体フォントにするかどうかを指定します。
/// @param[in] in_underline   : 下線を入れるかどうかを指定します。
/// @param[in] in_strikeout   : 取り消し線を入れるかどうかを指定します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FontDrawManager::FontDrawManager
( _in<DXDevice*> in_ptr_d3d_dev
, pin<str_type > in_font_name
, _in<int      > in_height
, _in<bool     > in_italic
, _in<bool     > in_underline
, _in<bool     > in_strikeout
) : /* m_cp_d3d_dev( in_ptr_d3d_dev ) ← これ禁止（AddRefは呼ばれないから）
  ,*/m_charIdx_to_rectIdx( Core::kStrMaxSize, 0xffff )
  , m_texture_width( FONT_TEXTURE_WIDTH )
  , m_texture_height( FONT_TEXTURE_HEIGHT )
{
  m_cp_d3d_dev = in_ptr_d3d_dev;

  m_sp_hdc  =
    CPL_MAKE_SHARED( WIN::HdcSurface, (HWND)nullptr );
  m_sp_font =
    CPL_MAKE_SHARED
      ( WIN::DrawFont
      , m_sp_hdc->GetDC()
      , in_height, in_font_name
      , in_italic, in_underline, in_strikeout
      );
  m_sp_sprite_draw_manager =
    CPL_MAKE_SHARED( DX::SpriteDrawManager );
}


// _____________________________________________________________________________
/// @brief フォント生成付きコンストラクタ（LOGFONT version）
/// 
/// @param[in] in_ptr_d3d_dev
///   : DirectXデバイスのポインタを指定します。
/// @param[in] in_ptr_log_font
///   : LOGFONT構造体へのポインタを指定します。\n
///   : ※ LOGFONTのHDCは無視されます。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FontDrawManager::FontDrawManager
( _in<DXDevice*> in_ptr_d3d_dev
, pin<LOGFONT  > in_ptr_log_font
) : m_charIdx_to_rectIdx( Core::kStrMaxSize, 0xffff )
  , m_texture_width( FONT_TEXTURE_WIDTH )
  , m_texture_height( FONT_TEXTURE_HEIGHT )
{
  m_cp_d3d_dev = in_ptr_d3d_dev;

  m_sp_hdc  = CPL_MAKE_SHARED( WIN::HdcSurface, (HWND)nullptr );
  m_sp_font = CPL_MAKE_SHARED( WIN::DrawFont,  m_sp_hdc->GetDC(), in_ptr_log_font );
  m_sp_sprite_draw_manager
            = CPL_MAKE_SHARED( DX::SpriteDrawManager );
}


// _____________________________________________________________________________
/// @brief フォント生成付きコンストラクタ（DrawFont version）
///
/// @param[in] in_ptr_d3d_dev : DirectXデバイスのポインタを指定します。
/// @param[in] in_sp_font     : CPL::WIN::DrawFontクラスへのスマートポインタを指定します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FontDrawManager::FontDrawManager
( _in<DXDevice*           > in_ptr_d3d_dev
, _in<sdptr<WIN::DrawFont>> in_sp_font
) : m_charIdx_to_rectIdx( Core::kStrMaxSize, 0xffff )
  , m_texture_width( FONT_TEXTURE_WIDTH )
  , m_texture_height( FONT_TEXTURE_HEIGHT )
{
  m_cp_d3d_dev = in_ptr_d3d_dev;
  m_sp_font    = in_sp_font;
  m_sp_sprite_draw_manager
               = CPL_MAKE_SHARED(DX::SpriteDrawManager);
}


// _____________________________________________________________________________
/// @brief デストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FontDrawManager::~FontDrawManager()
{
}


// _____________________________________________________________________________
/// @brief 文字登録
/// 
/// @param[in] in_char_code : １文字の文字コードを指定します。
/// @param[in] in_text_mtc
///   : 文字情報(TEXTMETRIC)を指定します。\n
///   : 詳しくはTEXTMETRICで検索してください。
/// @param[in] in_line_h_min
///   : テクスチャマッピング時の１行の最低限の高さを指定します。
/// 
/// @return
/// キャラクタの高さを返します。\n
/// 既に登録済みの場合はin_line_h_minをそのまま返します。\n
/// テクスチャの作成に失敗した場合、-1を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int
  FontDrawManager::EntryChar
    ( _in<U32       > in_char_code
    , _in<TEXTMETRIC> in_text_mtc
    , _in<int       > in_line_h_min
    )
{
  //int n;
  //U32 char_idx = Core::GetCharIndex_UTF8( in_char_code, n );
  if( IsEntryChar( in_char_code ) )
  {
    // 既に登録済みなら抜ける
    return in_line_h_min;
  }
//  if( code == L'\n' )
//  {
//    // 改行なら抜ける
//    return in_line_h_min;
//  }
  
  /*** １文字分をテクスチャに追加 ***/
  static Core::Array::Dynamic<U8> tmp_buf;
  CONST MAT2 Mat = { {0,1},{0,0},{0,0},{0,1} };
  GLYPHMETRICS gm;

  U16 idx_rect_new = static_cast<U16>(m_rect_table.GetNum());
  int n;
  U32 char_idx = Core::GetCharIndex_UTF16( (td_DefCharCPtr)&in_char_code, n );
  m_charIdx_to_rectIdx[ char_idx ] = idx_rect_new;

  // フォント情報取得
  DWORD size =
    GetGlyphOutlineW
      ( m_sp_hdc->GetDC()
      , in_char_code
      , GGO_GRAY8_BITMAP
      , &gm
      , 0
      , nullptr
      , &Mat
      );
  if( size == GDI_ERROR )
  {
    return false;
  }
  int bmp_w  = ((gm.gmBlackBoxX + 3) >> 2) << 2; // （4byteアライメント：どうやら１ピクセルあたり１バイトらしいので）
  int bmp_h  = gm.gmBlackBoxY;
  int offs_x = gm.gmptGlyphOrigin.x;
  int offs_y = in_text_mtc.tmAscent - gm.gmptGlyphOrigin.y;
  int max_h  = Max<int>( in_line_h_min, bmp_h );
  if( size > 0 )
  {
    tmp_buf.Resize( size );
    GetGlyphOutlineW
      ( m_sp_hdc->GetDC()
      , in_char_code
      , GGO_GRAY8_BITMAP
      , &gm
      , size
      , &tmp_buf[0]
      , &Mat
      );
  }
  else
  {
    tmp_buf.ResizeInit( bmp_w * bmp_h * 4, 0, 0 );
    ::memset( tmp_buf.GetTop(), 0, tmp_buf.GetSize() );
  }

  CpDXShaderResourceView cp_tex_view;

  if( idx_rect_new == 0 ) // 一番最初
  {
    cp_tex_view = zzf_EntryChar_CreateTexture();
    if( !cp_tex_view )
    {
      return -1;
    }
    // 矩形情報を追加
    float4 rt_next( 0,0, static_cast<float>(bmp_w), static_cast<float>(bmp_h) );
    m_rect_table.AddEnd(
      RectInfo(
        static_cast<int>(m_tex_table.size()) - 1,
        gm.gmCellIncX, offs_x, offs_y,
        rt_next ) );
  }
  else
  {
    int prev_right  = static_cast<int>(m_rect_table[ idx_rect_new-1 ].rect.z) + 1;
    int prev_top    = static_cast<int>(m_rect_table[ idx_rect_new-1 ].rect.y);
    //int prev_bottom = static_cast<int>(m_rect_table[ idx_rect_new-1 ].rect.w);
    int tex_idx     = static_cast<int>(m_tex_table.size()) - 1;
    short4 rt_next( 0,0,0,0 );
    // 現在のテクスチャに文字を追加できるかどうか判定
    if( prev_right + bmp_w <= m_texture_width ) // 横に追加できる場合
    {
      rt_next.x = prev_right;
      rt_next.z = prev_right + bmp_w;
      rt_next.y = prev_top;
      rt_next.w = prev_top + bmp_h;
      cp_tex_view = m_tex_table.back();
    }
    else
    if( prev_top + max_h + bmp_h <= m_texture_height ) // 次の列に追加できる場合
    {
      rt_next.x = 0;
      rt_next.z = bmp_w;
      rt_next.y = prev_top + max_h;
      rt_next.w = rt_next.y + bmp_h;
      cp_tex_view = m_tex_table.back();
      max_h = bmp_h;
    }
    else // 現在のテクスチャに追加できない場合、新しいテクスチャを作成して追加
    {
      rt_next.x = 0;
      rt_next.z = bmp_w;
      rt_next.y = 0;
      rt_next.w = bmp_h;
      //
      cp_tex_view = zzf_EntryChar_CreateTexture();
    }
    // 矩形情報を追加
    RectInfo ri( tex_idx, gm.gmCellIncX, offs_x, offs_y, rt_next );
    m_rect_table.AddEnd( ri );
  }
  
  // ***** データ書き込み ***** //
  if( cp_tex_view )
  {
    // 
    float4 const& rt = m_rect_table.GetEnd()->rect;
    // 書き込むデータ形式に変換（最大サイズを確保）
    static u_char4 temp_texture[ FONT_TEXTURE_HEIGHT ][ FONT_TEXTURE_WIDTH ];
    // データ書き込み
    for( int y = 0; y < bmp_h; ++y )
    {
      for( int x = 0; x < bmp_w; ++x )
      {
        BYTE a = (tmp_buf[ bmp_w * y + x ] * 255) >> 6; // 0-64を0-255に補正
        temp_texture[ y ][ x ] = u_char4(255,255,255,a);
      }
    }
    // テクスチャリソースを取得
    CpDXResource cp_texture;
    cp_tex_view->GetResource( &cp_texture );
    // テクスチャリソースへコピー
    CD3D11_BOX
      box
        ( (long)rt.left,  (long)rt.top,    0
        , (long)rt.right, (long)rt.bottom, 1
        );
    Graphics_i_::GetImmediateContext()->UpdateSubresource
      ( cp_texture
      , D3D10CalcSubresource(0,0,1)
      , &box
      , temp_texture
      , sizeof(u_char4) * FONT_TEXTURE_WIDTH
      , sizeof(temp_texture)
      );
  }

  return max_h;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// テクスチャの新規作成
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CpDXShaderResourceView FontDrawManager::zzf_EntryChar_CreateTexture()
{
  //*** テクスチャ作成 ***//
  // テクスチャ情報
  D3D11_TEXTURE2D_DESC tex_desc;
  ZeroMemory( &tex_desc, sizeof(tex_desc) );
  tex_desc.Width = m_texture_width;
  tex_desc.Height = m_texture_height;
  tex_desc.MipLevels = 1;
  tex_desc.ArraySize = 1;
  tex_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
  tex_desc.Usage  = D3D11_USAGE_DEFAULT;
  tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
  tex_desc.CPUAccessFlags = 0;//D3D11_CPU_ACCESS_WRITE;
  tex_desc.SampleDesc.Count = 1;
  // 初期化情報
  //Core::Array::C_Char clear_data( m_texture_width * m_texture_height * 4, 0 );
  //D3D11_SUBRESOURCE_DATA init_data;
  //init_data.pSysMem = &clear_data[0];
  //init_data.SysMemPitch = m_texture_width * 4;
  //
  CpDXTexture2D cp_texture;
  HRESULT hr =
    Graphics_i_::GetDevice()->CreateTexture2D( &tex_desc, nullptr, &cp_texture );
  if( FAILED(hr) )
  {
    //CPL_THROW_CODE( CEC_NOT_CREATE_TEXTURE, "テクスチャの作成に失敗（共通テクスチャ）\n" );
    return CpDXShaderResourceView(nullptr);
  }
  
  // シェーダーリソースビューの作成
  CpDXShaderResourceView cp_shader_RV;
  hr =
    Graphics_i_::GetDevice()->CreateShaderResourceView(
      cp_texture, nullptr, &cp_shader_RV );
  if( FAILED(hr) )
  {
    return cp_shader_RV;
  }

  m_tex_table.push_back( cp_shader_RV );

  return cp_shader_RV;
}


// _____________________________________________________________________________
/// @brief 文字列登録
/// 
/// @param[in] in_str
///   : 登録文字のリストを指定します。\n
///   : null終端文字列として指定します。
/// 
/// @retval true  : 登録成功。
/// @retval false : 文字コードに不正あり。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int FontDrawManager::EntryString( pin<td_DefChar> in_str )
{
  if( !m_sp_font )
  {
    return false;
  }

  TEXTMETRIC tm;
  GetTextMetrics( m_sp_hdc->GetDC(), &tm );
  static int max_h = 1;

  auto ptr_str = in_str;
  int n;
  static U32 code_LF = Core::GetCharCode_UTF16( L"\n", n );
  while( *ptr_str )
  {
    U32 code = Core::GetCharCode_UTF16( ptr_str, n );
    if( code == 0xffffffff )
    {
      return false;
    }
    if( code == code_LF )
    {
      ptr_str += n;
      continue;
    }

    max_h = EntryChar( code, tm, max_h );

    ptr_str += n;
  }

  return true;
}


/*
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 文字列描画
// 戻り値：描画幅
// ※ in_colorを０にすると描画しない（描画したサイズを返す）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
short2 FontDrawManager::DrawString(
        float          in_X,
        float          in_Y,
        float          in_Z,
        const wchar_t* in_str,
        DWORD          in_color,
        int            in_max_width,
        int            in_length,
        int            in_flag_entry )
{
  // nullptr判定
  if( *in_str == 0 )
  {
    return short2(0,0);
  }
  // 最大幅調整
  if( in_max_width <= 0 )
  {
    in_max_width = 0x3fffffff;
  }

  // 登録処理を行うかどうか
  if( in_flag_entry )
  {
    if( !EntryString( in_str ) )
    {
      return short2(0,0);
    }
  }

  float base_x = in_X;
  int length = (in_length <= 0) ? Core::StringW::str_len_( in_str ) : in_length;
  for( int i = 0; i < length; ++i )
  {
    wchar_t code = in_str[ i ];
    wchar_t idx  = this->m_charIdx_to_rectIdx[ code ];
    if( idx == 0xffff )
    {
      continue;
    }

    if( m_rect_table.GetNum() <= idx )
    {
      return short2(0,0);
    }

    const int tex_idx = m_rect_table[ idx ].tex_idx;
    const int char_w  = m_rect_table[ idx ].char_w;
    const int offs_x  = m_rect_table[ idx ].offs_x;
    const int offs_y  = m_rect_table[ idx ].offs_y;

    if( in_X + char_w >= base_x + in_max_width )
    {
      break;
    }

    if( in_color )
    {
      RECT const& rt = m_rect_table[ idx ].rect;
      m_sp_sprite_draw_manager->DrawEx(
        &*m_tex_table[ tex_idx ],
        float2(in_X + offs_x, in_Y + offs_y), rt,
        in_Z, in_color, float2(0,0), float2(1,1) );
    }
    in_X += char_w;
  }

  RECT const& rt = m_rect_table[ m_charIdx_to_rectIdx[ in_str[0] ] ].rect;

  return short2( short(in_X - base_x), short(rt.bottom-rt.top) );
}
*/

// _____________________________________________________________________________
/// @brief メッセージ描画
/// 
/// @note
/// この関数では実際に描画されるわけではなく、内部バッファにスプライト情報として
/// 保持されます。\n
/// 実際の描画はTranslate関数で行います。
/// 
/// @param[in] in_MI : メッセージ描画情報(MsgInfo)オブジェクトを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void FontDrawManager::DrawMessage( _in<MsgInfo> in_MI )
{
  td_DefChar const* msg_top = in_MI.GetMessage();

  if( msg_top == nullptr )
  {
    return;
  }

  //const size_t msg_length  = Core::StringW::str_len_( msg_top );
  const float4
    rect_clip // クリップ用矩形。
      ( static_cast<float>(in_MI.rect.left  )
      , static_cast<float>(in_MI.rect.top   )
      , static_cast<float>(in_MI.rect.right )
      , static_cast<float>(in_MI.rect.bottom)
      );
  const int    width       = in_MI.rect.right  - in_MI.rect.left;
  const int    height      = in_MI.rect.bottom - in_MI.rect.top;
  //const int    line_height = m_sp_font->GetHeight();
  const DWORD  flag_x      = in_MI.flags & kFDM_X_MASK;
  const DWORD  flag_y      = in_MI.flags & kFDM_Y_MASK;

  // 文字使用準備
  EntryString( msg_top );

  // 行数取得
  int line_num = zzf_getLineNum_( msg_top );

  // 文字列のライン毎の先頭ポインタを取得
  td_DefCharCPtrArr str_head_ptr_tbl;//( ReserveSize(line_num) );
  zzf_getLineStringList_( msg_top, str_head_ptr_tbl );

  // ライン毎のサイズを取得
  Core::Array::Dynamic<short2> str_line_size_tbl;
  int msg_height = 0;
  for( int i = 0; i < line_num; ++i )
  {
    short2 size = 
      GetStringLineSize
        ( str_head_ptr_tbl[ i ]
        , static_cast<int>(str_head_ptr_tbl[ i ] - msg_top)
        , &in_MI.GetScaleTable()
        );
    str_line_size_tbl.AddEnd( size );
    msg_height += size.y;
  }

  int offset_y = 0;
  // 縦方向フラグ検査
  if( flag_y == kFDM_Y_MIDDLE ) // 縦中央揃えの場合
  {
    offset_y = (height - msg_height) / 2;
  }

  // メッセージ全体のマトリクスを生成
  Math::D2<float>::Matrix mtx_msg;
  mtx_msg.Identity(); // 初期化
  mtx_msg.Translate( -in_MI.rotate_center.x, -in_MI.rotate_center.y );
  mtx_msg.RotZrad( in_MI.rotate_z );
  mtx_msg.Translate
    ( in_MI.rotate_center.x + in_MI.rect.left
    , in_MI.rotate_center.y + in_MI.rect.top
    );

  //
  for( int i = 0; i < line_num; ++i )
  {
    //auto         tmp_msg = str_head_ptr_tbl[ i ];
    const short2 line_size = str_line_size_tbl[ i ];
    int          offset_x = 0;

    // 中央揃えの場合
    if( flag_x == kFDM_X_MIDDLE )
    {
      offset_x = (width - str_line_size_tbl[ i ].x) / 2;
    }
    // 右揃えの場合
    else
    if( flag_x == kFDM_X_RIGHT )
    {
      offset_x = width - str_line_size_tbl[ i ].x;
    }

    auto check_char = str_head_ptr_tbl[ i ];
    auto end_char   = str_head_ptr_tbl[ i + 1 ];
    while( check_char < end_char )
    {
      //
      int n;
      U32 idx_char = Core::GetCharIndex_UTF16( check_char, n );
      if( idx_char == 0xffff )
      {
        ++check_char;
        continue;
      }
      U16 idx_rect  = this->m_charIdx_to_rectIdx[ idx_char ];
      if( idx_rect == 0xffff )
      {
        ++check_char;
        continue;
      }

      const int    tex_idx = m_rect_table[ idx_rect ].tex_idx;
      const int    char_w  = m_rect_table[ idx_rect ].char_w;
      const int    offs_x  = m_rect_table[ idx_rect ].offs_x;
      const int    offs_y  = m_rect_table[ idx_rect ].offs_y;
      const size_t msg_idx = check_char - msg_top;
      float2       scale(1,1);
      const RectColor4P
        color4p
          ( in_MI.GetColorTable().empty()
              ? in_MI.default_color
              : in_MI.GetColorTable()[ msg_idx ] );

      //
      if( color4p.IsVisible() )
      {
        float4 const& rt = m_rect_table[ idx_rect ].rect;
        if( in_MI.GetScaleTable().size() > msg_idx )
        {
          scale = in_MI.GetScaleTable()[ msg_idx ];
        }
        // マトリクス設定
        // イメージ的に、(0,0,1,1)のポリゴンに適用する感じ。
        Math::D2<float>::Matrix mtx_chr;
        mtx_chr.Identity(); // 初期化
        mtx_chr.Scaling( float2( rt.Width(), rt.Height() ) );
        mtx_chr.Translate
          ( static_cast<float>(offs_x)
          , static_cast<float>(offs_y)
          );
        mtx_chr.Scaling( scale );
        if( in_MI.GetRotateTable().size() > msg_idx )
        {
          float2 offs_center( char_w * 0.5f, line_size.y * 0.5f );
          mtx_chr.Translate( -offs_center.x, -offs_center.y );
          mtx_chr.RotZrad( in_MI.GetRotateTable()[ msg_idx ] );
          mtx_chr.Translate( offs_center.x, offs_center.y );
        }
        mtx_chr.Translate
          ( static_cast<float>(offset_x)
          , static_cast<float>(offset_y)
          );
        mtx_chr *= mtx_msg;

        //
        m_sp_sprite_draw_manager->Draw
          ( m_tex_table[ tex_idx ]
          , mtx_chr
          , rt
          , in_MI.depth_z
          , color4p
          , &rect_clip
          );
      }

      offset_x += static_cast<int>(char_w * scale.x + 0.999999f);

      check_char += n;
    }

    //
    offset_y += line_size.y;
  }

}


// _____________________________________________________________________________
/// @brief キャッシュ済みスプライトの描画
/// 
/// @param[in] in_ptr_d3d_dev : DirectXデバイスのアドレスを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void FontDrawManager::Translate( DXDevice* in_ptr_d3d_dev )
{
  this->m_sp_sprite_draw_manager->Transrate();
}


// _____________________________________________________________________________
/// @brief カレット情報を取得
/// 
/// 指定した文字列とｘ座標からカレット情報を計算します。
/// 
/// @param[in] in_MI : メッセージ描画情報(MsgInfo)オブジェクトを指定します。
/// @param[in] io_CD : カレット情報(CaretData)取得用オブジェクトを指定します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  FontDrawManager::GetCaretData
    ( _in<MsgInfo  > in_MI
    , rio<CaretData> io_CD
    )
{
  auto msg = in_MI.GetMessage();
  const int width       = in_MI.rect.right - in_MI.rect.left;
  const int height      = in_MI.rect.bottom - in_MI.rect.top;
  const int msg_length  = static_cast<int>(td_DefString::str_len_( msg ));
  const int line_height = m_sp_font->GetHeight();

  //const size_t  str_length = Core::StringW::str_len_( msg ); // 文字数
  const int     line_num   = zzf_getLineNum_( msg );  // 行数取得

  const DWORD flag_x = in_MI.flags & kFDM_X_MASK;
  const DWORD flag_y = in_MI.flags & kFDM_Y_MASK;

  // 縦方向フラグ検査
  int offset_y = 0;
  if( flag_y == kFDM_Y_MIDDLE )
  {
    offset_y = (height - line_height * line_num) / 2;
  }

  //
  EntryString( msg );

  // 文字列幅取得
  td_DefCharCPtrArr str_width_tbl;//( ReserveSize(line_num) );
  str_width_tbl.reserve( line_num + 1 );
  zzf_getLineStringList_( msg, str_width_tbl );

  // 各文字の座標オフセット保持用
  Core::Array::Dynamic<short2> str_offs_tbl;
  str_offs_tbl.Resize( msg_length + 1 );

  // オフセット設定
  for( int i = 0, offs_y = 0; i < line_num; ++i )
  {
    auto check_char = str_width_tbl[ i ];
    auto end_char   = str_width_tbl[ i + 1 ];
    int offs_x = 0;
    while( check_char <= end_char )
    {
      wchar_t code = *check_char;
      U16 idx  = this->m_charIdx_to_rectIdx[ code ];
      // ｘオフセット(左端)を保存
      str_offs_tbl[ check_char - msg ] = int2( offs_x, offs_y );

      if( idx == 0xffff )
      {
        ++check_char;
        continue;
      }

      offs_x += m_rect_table[ idx ].char_w;

      ++check_char;
    }

    offs_y += line_height;
  }

  // ｘオフセットの考慮
  if( flag_x == kFDM_X_MIDDLE ) // 中央揃えの場合
  {
    for( int line_idx = 0; line_idx < line_num; ++line_idx )
    {
      // 行の幅を取得
      size_t last_idx = str_width_tbl[ line_idx + 1 ] - msg;
      const int msg_width = str_offs_tbl[ last_idx - 1 ].x;
      const int msg_offs_x = (width - msg_width) / 2;

      for( size_t idx = str_width_tbl[ line_idx ] - msg; idx <= last_idx; ++idx )
      {
        str_offs_tbl[ idx ].x += msg_offs_x;
      }
    }
  }
  else
  if( flag_x == kFDM_X_RIGHT ) // 右揃えの場合
  {
    for( int line_idx = 0; line_idx < line_num; ++line_idx )
    {
      // 行の幅を取得
      size_t last_idx = str_width_tbl[ line_idx + 1 ] - msg;
      const int msg_width = str_offs_tbl[ last_idx - 1 ].x;
      const int msg_offs_x = width - msg_width;

      for( size_t idx = str_width_tbl[ line_idx ] - msg; idx <= last_idx; ++idx )
      {
        str_offs_tbl[ idx ].x += msg_offs_x;
      }
    }
  }

  // ｙオフセットの考慮
  if( flag_y == kFDM_Y_MIDDLE )
  {
  }
  else
  if( flag_y == kFDM_Y_BOTTOM )
  {
  }

  io_CD.height = line_height;

  const int offs_x = in_MI.rect.left;
  const int offs_y = in_MI.rect.top;

  if( io_CD.get_type == CaretData::kXY_TO_INDEX ) // 座標からインデックスを返す場合
  {
    auto mx = io_CD.x;
    auto my = io_CD.y;
    io_CD.x   = offs_x;
    io_CD.y   = offs_y;
    io_CD.idx = 0;
    if( mx <= 0 )
    {
      return;
    }
    // 当たり判定
    //CPL_LOOP2( msg_length+1, index )
    for
      ( int index = 0, idx_end = msg_length + 1
      ; index < idx_end
      ; ++index
      )
    {
      int4
        rt( str_offs_tbl[index].x + offs_x
          , str_offs_tbl[index].y + offs_y
          , (index >= msg_length) ? width + offs_x : str_offs_tbl[index+1].x + offs_x
          , str_offs_tbl[index].y + offs_y + line_height
          );
      const int harf = (rt.right - rt.left) / 2 + 1;

      //int2 p0 = str_offs_tbl[ index ];
      //int2 p1 = (index>=msg_length) ?
      //  int2(width, line_height) :
      //  int2(str_offs_tbl[ index + 1 ].x, line_height);
      //p0 += offs;
      //p1 += offs;
      //io_CD.x   = p0.x;
      //io_CD.y   = p0.y;
      //io_CD.idx = index;
      //const int harf = (p1.x - p0.x) / 2;
      //WIN::Rect rt( p0.x-harf, p0.y, p1.x-harf, p0.y + line_height );
      
      io_CD.x   = rt.x;
      io_CD.y   = rt.y;
      io_CD.idx = index;

      rt.x -= harf;
      rt.z -= harf;

      if( rt.IsHit( mx, my ) )
      {
        return;
      }
    }
  }
  else
  if( io_CD.get_type == CaretData::kINDEX_TO_XY ) // インデックスから座標を返す場合
  {
    int2 p = str_offs_tbl[ io_CD.idx ];
    io_CD.x = p.x + offs_x;
    io_CD.y = p.y + offs_y;
  }

}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 文字列のライン数を取得
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int FontDrawManager::zzf_getLineNum_( pin<td_DefChar> in_msg ) const
{
  // 行数検査
  int line_num = 1;
  for(
    auto check_char = in_msg;
    *check_char;
    ++check_char )
  {
    if( *check_char == SwS('\n') )
    {
      ++line_num;
    }
  }

  return line_num;
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 文字列の改行後の先頭アドレスのリストを取得
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  FontDrawManager::zzf_getLineStringList_
    ( pin <td_DefChar       > in_msg
    , rout<td_DefCharCPtrArr> out_char_tbl
    ) const
{
  // テーブル初期化
  //out_char_tbl.Release();
  out_char_tbl.clear();
  // 先頭追加
  out_char_tbl.push_back( in_msg );

  // 
  cptr<td_DefChar> check_char = in_msg;
  for(; *check_char; ++check_char )
  {
    if( *check_char == SwS('\n') )
    {
      if( *(check_char + 1) )
      {
        out_char_tbl.push_back( check_char + 1 );
      }
    }
  }

  out_char_tbl.push_back( check_char );
}


// _____________________________________________________________________________
/// @brief 文字列の描画サイズの取得
/// 
/// 指定した文字列を描画した際の最低限必要な矩形サイズを計算します。
/// 文字列中に改行が含まれる場合、最初の改行まで求めます。
/// 
/// @param[in] in_msg : メッセージの文字列を指定します。
/// @param[in] in_offs_idx
///   : 指定した文字列の先頭からのオフセットインデックスを指定します。
/// @param[in] in_ptr_scale_array
///   : 文字毎の描画倍率を指定します。\n
///   : 使用しない場合nullptrを指定できます。
/// 
/// @return 描画時の矩形サイズを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int2
  FontDrawManager::GetStringLineSize
    ( pin<td_DefChar      > in_msg
    , _in<int             > in_offs_idx
    , pin<Array_CharsScale> in_ptr_scale_array
    )
{
  const int line_height = m_sp_font->GetHeight();
  auto      check_char = in_msg;
  short2    size(0,0);

  // 文字列の使用登録
  // ※ 登録しないと、サイズを求めることができない。
  EntryString( in_msg );

  // サイズ計算ループ
  while( *check_char  &&  *check_char != L'\n' )
  {
    int n;
    wchar_t idx_char = Core::GetCharIndex_UTF16( check_char, n );
    wchar_t idx_rect = this->m_charIdx_to_rectIdx[ idx_char ];
    if( idx_rect == 0xffff )
    {
      ++check_char;
      continue;
    }

    int char_w = m_rect_table[ idx_rect ].char_w;
    int char_h = line_height;
    if( in_ptr_scale_array  &&  !in_ptr_scale_array->empty() )
    {
      const size_t table_idx = check_char - in_msg + in_offs_idx;
      char_w = static_cast<int>(char_w * (*in_ptr_scale_array)[ table_idx ].x + 0.99999f);
      char_h = static_cast<int>(char_h * (*in_ptr_scale_array)[ table_idx ].y + 0.99999f);
    }

    size.x += char_w;
    size.y = Max<int>( size.y, char_h );

    check_char += n;
  }

  return size;
}


//} // namespace Graph
} // namespace DX
} // namespace CPL

