/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2015/11/11
/// 
/// @brief  DirectX フォント周り。
/// 
/// ****************************************************************************

#pragma once

#include "CPL_DX_Sprite.h"
#include "../Win/GObj/CPL_WIN_Graphics.h"


namespace CPL {
namespace DX {


// フォント描画時に指定するフラグ
enum eFDM_Flag
{ kFDM_X_LEFT     = 0x00000001 ///< 横方向フラグ、左。
, kFDM_X_RIGHT    = 0x00000002 ///< 横方向フラグ、右。
, kFDM_X_MIDDLE   = 0x00000003 ///< 横方向フラグ、中央。
, kFDM_X_MASK     = 0x0000000F ///< 横方向フラグのマスク。
, kFDM_Y_TOP      = 0x00000010 ///< 縦方向フラグ、上。
, kFDM_Y_BOTTOM   = 0x00000020 ///< 縦方向フラグ、下。
, kFDM_Y_MIDDLE   = 0x00000030 ///< 縦方向フラグ、中央。
, kFDM_Y_MASK     = 0x000000F0 ///< 縦方向フラグのマスク。
//, FDm_ID_ROTATE_ENABLE  = 0x00000100 ///< 文字毎の回転を有効にする場合に指定する。
//, FDm_ID_COLOR_ENABLE   = 0x00000200 ///< 文字毎のカラーを有効にする場合に指定する。
//, FDm_ID_SCALE_ENABLE   = 0x00000400 ///< 文字毎のスケールを有効にする場合に指定する。
//, FDm_ID_BGFILL_ENABLE  = 0x00000800 ///< 文字毎の背景塗りつぶしを有効にする
};

// _____________________________________________________________________________
/// @brief フォント描画マネージャクラス
///
// *****************************************************************************
class FontDrawManager
{
private:
  //
  //using type_char          = wchar_t; // UTF-8仕様に変更（2015/10/03）
  //using type_char          = char;
  //using type_char_ptr      = ptr<type_char>;
  //using type_char_cptr     = cptr<type_char>;
  using Array_CharsRotate  = std::vector<float>;
  using Array_CharsColor   = std::vector<U32x4>;
  using Array_CharsScale   = std::vector<float2>;
  using Array_CpSdeResView = std::vector<CpDXShaderResourceView>;
  using Array_CharIndexs   = std::vector<U16>;
public:
  using td_DefChar     = wchar_t;
  using td_DefCharPtr  = td_DefChar*;
  using td_DefCharCPtr = td_DefChar const*;
  using td_DefString   = Core::String_X_<td_DefChar>;
  using td_DefCharCPtrArr = std::vector<td_DefCharCPtr>;

  // １つのキャラクタのテクスチャ情報
  struct RectInfo {
    U8     tex_idx;
    S8     char_w; // 
    S8     offs_x; // gmptGlyphOrigin.x
    S8     offs_y; // tm.tmAscent - gmptGlyphOrigin.y
    float4 rect;   // uv

    RectInfo(){}
    RectInfo
    ( _in<int   > in_tex_idx
    , _in<int   > in_char_w
    , _in<int   > in_offs_x
    , _in<int   > in_offs_y
    , _in<float4> in_rect
    ) : tex_idx(in_tex_idx)
      , char_w(in_char_w)
      , offs_x(in_offs_x)
      , offs_y(in_offs_y)
      , rect(in_rect) 
    {}
  };

  // メッセージ描画情報
  struct MsgInfo {
    WIN::Rect     rect;           ///< 描画範囲。
    float         depth_z;        ///< ｚ深度。
    U32x4         default_color;  ///< デフォルトのカラー。
    float         rotate_z;       ///< 角度（rad単位）
    float2        rotate_center;  ///< 回転中心座標。
    int           line_h;         ///< ライン送り幅
    DWORD         flags;          ///< オプションフラグ。eFDM_Flag参照。
  private:
    td_DefCharCPtr    message;
    Array_CharsRotate rotate_array;
    Array_CharsColor  color_array;
    Array_CharsScale  scale_array;

    template<class T_Param, class T_Default>
    size_t zzz_updateTableSize_
      ( std::vector<T_Param>& io_params
      , cref<T_Default> in_data_default )
    {
      const size_t length_msg = td_DefString::str_len_( message );
      if( io_params.size() < length_msg )
      {
        auto size_old = io_params.size();
        io_params.resize( length_msg );
        for( size_t idx = size_old; idx < length_msg; ++idx )
        {
          io_params[ idx ] = in_data_default;
        }
      }
      return length_msg;
    }

  public:
    MsgInfo
    ( pin<td_DefChar> in_message       = nullptr
    , _in<RECT      > in_rect_for_msg  = WIN::Rect(0,0,1,1)
    , _in<float     > in_depth_z       = 0.0f
    , _in<U32x4     > in_default_color = U32x4(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff)
    , _in<float     > in_rotate_z      = 0.0f
    , _in<float2    > in_rotate_center = float2(0,0)
    , _in<int       > in_line_h        = -1
    , _in<DWORD     > in_flags         = kFDM_X_LEFT | kFDM_Y_TOP/* | FDM_ID_WIN_X_LEFT | FDM_ID_WIN_Y_TOP*/
    );

    void  SetMessage( pin<wchar_t> in_message );
    td_DefCharCPtr
          GetMessage() const { return message; }
    Array_CharsRotate&
          GetRotateTable()       { return rotate_array; }
    Array_CharsRotate const&
          GetRotateTable() const { return rotate_array; }
    Array_CharsColor&
          GetColorTable()        { return color_array; }
    Array_CharsColor const&
          GetColorTable()  const { return color_array; }
    Array_CharsScale&
          GetScaleTable()        { return scale_array; }
    Array_CharsScale const&
          GetScaleTable()  const { return scale_array; }

    void SetRotate_String( pin<td_DefChar> in_str,       _in<float> in_rad_char );
    void SetRotate_Index ( _in<int       > in_index,     _in<float> in_rad_char );
    void SetRotate_Char  ( pin<td_DefChar> in_char_list, _in<float> in_rad_char );
    void SetColor_String ( pin<td_DefChar> in_str,       _in<U32x4> in_color );
    void SetColor_Index  ( _in<int       > in_index,     _in<U32x4> in_color );
    void SetColor_Char   ( pin<td_DefChar> in_char_list, _in<U32x4> in_color );
    void SetScale_String ( pin<td_DefChar> in_str,       _in<float2> in_scale );
    void SetScale_Index  ( _in<int       > in_index,     _in<float2> in_scale );
    void SetScale_Char   ( pin<td_DefChar> in_char_list, _in<float2> in_scale );
  };

  // キャレットデータ取得用オブジェクト
  struct CaretData {
    enum eGetType
    { kXY_TO_INDEX
    , kINDEX_TO_XY
    };

    eGetType get_type;
    int      idx;
    int      x;
    int      y;
    int      height;

    CaretData()
      : get_type(kXY_TO_INDEX)
      , idx(0)
      , x(0)
      , y(0)
      , height(10)
    {}
  };
private:
  using RectInfoArray = Core::Array::Dynamic< RectInfo >;

  sdptr<SpriteDrawManager> m_sp_sprite_draw_manager;
  CpDXDevice               m_cp_d3d_dev; // デストラクタの順序の問題でm_tex_tableより前で定義すること
  sdptr<WIN::DrawFont>     m_sp_font;
  sdptr<WIN::HdcSurface>   m_sp_hdc;
  Array_CpSdeResView       m_tex_table;
  Array_CharIndexs         m_charIdx_to_rectIdx;
  RectInfoArray            m_rect_table;
  int                      m_texture_width;  // 作成するテクスチャの幅を保持
  int                      m_texture_height; // 作成するテクスチャの高さを保持

  static int staticInitialize();

  int   zzf_getLineNum_( pin<td_DefChar> in_msg ) const;
  void  zzf_getLineStringList_
          ( pin <td_DefChar       > in_msg
          , rout<td_DefCharCPtrArr> out_char_tbl
          ) const;
  CpDXShaderResourceView
        zzf_EntryChar_CreateTexture();

public:
  // _____________________________________________________________________________
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  FontDrawManager
  ( _in<DXDevice*> in_ptr_d3d_dev
  , pin<str_type > in_font_name
  , _in<int      > in_height
  , _in<bool     > in_italic    = false
  , _in<bool     > in_underline = false
  , _in<bool     > in_strikeout = false
  );

  // _____________________________________________________________________________
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  FontDrawManager
  ( _in<DXDevice*> in_ptr_d3d_dev
  , pin<LOGFONT  > in_ptr_log_font
  );

  // _____________________________________________________________________________
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  FontDrawManager
  ( _in<DXDevice*           > in_ptr_d3d_dev
  , _in<sdptr<WIN::DrawFont>> in_sp_font
  );

  // _____________________________________________________________________________
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ~FontDrawManager();

  // _____________________________________________________________________________
  /// @brief フォントクラスのスマートポインタを取得。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  sdptr<WIN::DrawFont>
         GetFont() const { return m_sp_font; }

  // _____________________________________________________________________________
  /// @brief 文字コード登録。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  bool   IsEntryChar( _in<U32> in_char_code ) const
  {
    int n;
    U32 idx = Core::GetCharIndex_UTF16( (td_DefCharCPtr)&in_char_code, n );
    return (m_charIdx_to_rectIdx[ idx ] != 0xffff);
  }
  int  EntryChar
         ( _in<U32       > in_char_code
         , _in<TEXTMETRIC> in_text_mtc
         , _in<int       > in_max_h
         );
  int  EntryString( pin<td_DefChar> in_str );
  void DrawMessage( _in<MsgInfo> in_MI );
  void GetCaretData( _in<MsgInfo> in_MI, rio<CaretData> io_CD );
  void Translate( DXDevice*  in_ptr_d3d_dev );
  int2 GetStringLineSize
           ( pin<td_DefChar      > in_msg
           , _in<int             > in_offs_idx
           , pin<Array_CharsScale> in_ptr_scale_array
           );
};


} // namespace DX
} // namespace CPL
