#define WINVER 0x0500                                  /* バージョン定義 Windows2000以上 */
#define _WIN32_WINNT WINVER

#include "ruby.h"
#ifndef RUBY_ST_H
#include "st.h"
#endif

#define DXRUBY_EXTERN 1
#include "dxruby.h"
#include "font.h"

static VALUE cFont;         /* フォントクラス       */
static VALUE symbol_italic  = Qundef;
static VALUE symbol_weight  = Qundef;

/*********************************************************************
 * Fontクラス
 *
 * D3DXFontインターフェースを使用してフォントを描画する。
 * スプライト描画に混ぜ込むことでとりあえず実装。
 *********************************************************************/
/*--------------------------------------------------------------------
   参照されなくなったときにGCから呼ばれる関数
 ---------------------------------------------------------------------*/
static void Font_free( struct DXRubyFont *font )
{
    RELEASE( font->pD3DXFont );
    DeleteObject( font->hFont );
}
void Font_release( struct DXRubyFont *font )
{
    /* フォントオブジェクトの開放 */
    if( font->pD3DXFont )
    {
        Font_free( font );
    }
    free( font );
    font = NULL;

    g_iRefAll--;
    if( g_iRefAll == 0 )
    {
        CoUninitialize();
    }
}

/*--------------------------------------------------------------------
   Fontクラスのdispose。
 ---------------------------------------------------------------------*/
static VALUE Font_dispose( VALUE self )
{
    struct DXRubyFont *font = DXRUBY_GET_STRUCT( Font, self );
    DXRUBY_CHECK_DISPOSE( font, pD3DXFont );
    Font_free( font );
    return self;
}

/*--------------------------------------------------------------------
   Fontクラスのdisposed?。
 ---------------------------------------------------------------------*/
static VALUE Font_check_disposed( VALUE self )
{
    if( DXRUBY_GET_STRUCT( Font, self )->pD3DXFont == NULL )
    {
        return Qtrue;
    }

    return Qfalse;
}

/*--------------------------------------------------------------------
   Fontクラスのmark
 ---------------------------------------------------------------------*/
static void Font_mark( struct DXRubyFont *font )
{
    rb_gc_mark( font->fontname );
    rb_gc_mark( font->italic );
    rb_gc_mark( font->weight );
}

/*--------------------------------------------------------------------
   Fontクラスのallocate。メモリを確保する為にinitialize前に呼ばれる。
 ---------------------------------------------------------------------*/
static VALUE Font_allocate( VALUE klass )
{
    VALUE obj;
    struct DXRubyFont *font;

    /* DXRubyFontのメモリ取得＆Fontオブジェクト生成 */
    font = malloc( sizeof(struct DXRubyFont) );
    if( font == NULL ) rb_raise( eDXRubyError, "メモリの取得に失敗しました - Font_allocate" );
    obj = Data_Wrap_Struct(klass, Font_mark, Font_release, font);
    font->fontname = Qnil;
    font->italic = Qnil;
    font->weight = Qnil;
    return obj;
}


/*--------------------------------------------------------------------
   FontクラスのInitialize
 ---------------------------------------------------------------------*/
static VALUE Font_initialize( int argc, VALUE *argv, VALUE obj )
{
    struct DXRubyFont *font;
    HRESULT hr;
    D3DXFONT_DESC desc;
    VALUE size, vfontname, voption, hash, vweight, vitalic;
    LOGFONT logfont;
    int weight, italic;

    g_iRefAll++;

    rb_scan_args( argc, argv, "12", &size, &vfontname, &hash );

    if( hash == Qnil )
    {
        voption = rb_hash_new();
    }
    else
    {
        Check_Type( hash, T_HASH );
        voption = hash;
    }

    vweight = hash_lookup( voption, symbol_weight );
    vitalic = hash_lookup( voption, symbol_italic );

    if( vweight == Qnil || vweight == Qfalse )
    {
        weight = 400;
    }
    else if( vweight == Qtrue )
    {
        weight = 1000;
    }
    else
    {
        weight = NUM2INT( vweight );
    }

    if( vitalic == Qnil || vitalic == Qfalse )
    {
        italic = FALSE;
    }
    else
    {
        italic = TRUE;
    }

    desc.Height          = NUM2INT( size );
    desc.Width           = 0;
    desc.Weight          = weight;
    desc.MipLevels       = 0;
    desc.Italic          = italic;
    desc.CharSet         = DEFAULT_CHARSET;
    desc.OutputPrecision = 0;
    desc.Quality         = DEFAULT_QUALITY;
    desc.PitchAndFamily  = DEFAULT_PITCH || FF_DONTCARE;

    ZeroMemory( &logfont, sizeof(logfont) );
    logfont.lfHeight          = NUM2INT( size );
    logfont.lfWidth           = 0;
    logfont.lfWeight          = weight;
    logfont.lfItalic          = italic;
    logfont.lfCharSet         = DEFAULT_CHARSET;
    logfont.lfQuality         = ANTIALIASED_QUALITY;

    if( vfontname == Qnil )
    {
        lstrcpy(desc.FaceName, "");
        lstrcpy(logfont.lfFaceName, "");
    }
    else
    {
        VALUE vsjisstr;
        Check_Type(vfontname, T_STRING);
#ifdef HAVE_RB_ENC_STR_NEW
        if( rb_enc_get_index( vfontname ) != 0 )
        {
            vsjisstr = rb_funcall( vfontname, rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
        }
        else
        {
            vsjisstr = vfontname;
        }
#else
        vsjisstr = vfontname;
#endif
        lstrcpy(desc.FaceName, RSTRING_PTR( vsjisstr ));
        lstrcpy(logfont.lfFaceName, RSTRING_PTR( vsjisstr ));
    }

    /* フォントオブジェクト取り出し */
    Data_Get_Struct( obj, struct DXRubyFont, font );

    hr = D3DXCreateFontIndirect( g_pD3DDevice, &desc, &font->pD3DXFont );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "フォントの作成に失敗しました - D3DXCreateFontIndirect" );
    }

    font->hFont = CreateFontIndirect( &logfont );

    if( font->hFont == NULL )
    {
        rb_raise( eDXRubyError, "フォントの作成に失敗しました - CreateFontIndirect" );
    }

    font->size = NUM2INT( size );
    font->italic = vitalic;
    font->weight = vweight;
#ifdef HAVE_RB_ENC_STR_NEW
    font->fontname = vfontname == Qnil ? Qnil : rb_str_new_shared( vfontname );
#else
    font->fontname = vfontname == Qnil ? Qnil : rb_str_new2( RSTRING_PTR(vfontname) );
#endif

    return obj;
}


/*--------------------------------------------------------------------
   Fontクラスのinstall
 ---------------------------------------------------------------------*/
static VALUE Font_install( VALUE klass, VALUE vstr )
{
    int result;
    VALUE vsjisstr;
    Check_Type( vstr, T_STRING );

#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( vstr ) != 0 )
    {
        vsjisstr = rb_funcall( vstr, rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
    }
    else
    {
        vsjisstr = vstr;
    }
#else
    vsjisstr = vstr;
#endif
    result = AddFontResourceEx( RSTRING_PTR( vsjisstr ), 0x10, 0 );
    if( result == 0 )
    {
        rb_raise( eDXRubyError, "フォントのインストールに失敗しました - Font_install" );
    }

    return INT2FIX( result );
}


/*--------------------------------------------------------------------
   フォントの幅を取得する
 ---------------------------------------------------------------------*/
VALUE Font_getWidth( VALUE obj, VALUE vstr )
{
    HDC hDC;
    struct DXRubyFont *font;
    RECT rc;
    VALUE vsjisstr;

    Check_Type( vstr, T_STRING );

    /* フォントオブジェクト取り出し */
    Data_Get_Struct( obj, struct DXRubyFont, font );
    DXRUBY_CHECK_DISPOSE( font, pD3DXFont );

    hDC = GetDC( g_hWnd );
    if( hDC == NULL )
    {
        rb_raise( eDXRubyError, "DCの取得に失敗しました - GetDC" );
    }

    SelectObject( hDC, font->hFont );
    SetRect( &rc, 0, 0, 1, 1 );
#ifdef HAVE_RB_ENC_STR_NEW
    if( rb_enc_get_index( vstr ) != 0 )
    {
        vsjisstr = rb_funcall( vstr, rb_intern( "encode" ), 1, rb_str_new2( sys_encode ) );
    }
    else
    {
        vsjisstr = vstr;
    }
#else
    vsjisstr = vstr;
#endif
    DrawText( hDC, RSTRING_PTR( vsjisstr ), -1, &rc, DT_LEFT | DT_NOCLIP | DT_NOPREFIX | DT_CALCRECT | DT_SINGLELINE);
    ReleaseDC( g_hWnd, hDC );

    return INT2FIX( rc.right );
}


/*--------------------------------------------------------------------
   フォントのサイズを取得する
 ---------------------------------------------------------------------*/
VALUE Font_getSize( VALUE obj )
{
    struct DXRubyFont *font;

    /* フォントオブジェクト取り出し */
    Data_Get_Struct( obj, struct DXRubyFont, font );
    DXRUBY_CHECK_DISPOSE( font, pD3DXFont );

    return INT2FIX( font->size );
}


/*--------------------------------------------------------------------
   フォント名称を取得する
 ---------------------------------------------------------------------*/
static VALUE Font_getFontname( VALUE obj )
{
    struct DXRubyFont *font;

    /* フォントオブジェクト取り出し */
    Data_Get_Struct( obj, struct DXRubyFont, font );
    DXRUBY_CHECK_DISPOSE( font, pD3DXFont );

    return font->fontname;
}


/*--------------------------------------------------------------------
   イタリックフラグを取得する
 ---------------------------------------------------------------------*/
static VALUE Font_getItalic( VALUE obj )
{
    struct DXRubyFont *font;

    /* フォントオブジェクト取り出し */
    Data_Get_Struct( obj, struct DXRubyFont, font );
    DXRUBY_CHECK_DISPOSE( font, pD3DXFont );

    return font->italic;
}


/*--------------------------------------------------------------------
   フォントの太さを取得する
 ---------------------------------------------------------------------*/
static VALUE Font_getWeight( VALUE obj )
{
    struct DXRubyFont *font;

    /* フォントオブジェクト取り出し */
    Data_Get_Struct( obj, struct DXRubyFont, font );
    DXRUBY_CHECK_DISPOSE( font, pD3DXFont );

    return font->weight;
}


void Init_dxruby_Font()
{
    /* Fontクラス登録 */
    cFont = rb_define_class_under( mDXRuby, "Font", rb_cObject );

    /* Fontクラスにクラスメソッド登録*/
    rb_define_singleton_method(cFont, "install", Font_install, 1);

    /* Fontクラスにメソッド登録*/
    rb_define_private_method( cFont, "initialize", Font_initialize, -1 );
    rb_define_method( cFont, "dispose"   , Font_dispose   , 0 );
    rb_define_method( cFont, "disposed?" , Font_check_disposed, 0 );
    rb_define_method( cFont, "get_width" , Font_getWidth  , 1 );
    rb_define_method( cFont, "getWidth"  , Font_getWidth  , 1 );
    rb_define_method( cFont, "fontname", Font_getFontname  , 0 );
    rb_define_method( cFont, "italic", Font_getItalic  , 0 );
    rb_define_method( cFont, "weight" , Font_getWeight  , 0 );
    rb_define_method( cFont, "size"      , Font_getSize  , 0 );

    /* Fontオブジェクトを生成した時にinitializeの前に呼ばれるメモリ割り当て関数登録 */
    rb_define_alloc_func( cFont, Font_allocate );

    symbol_italic         = ID2SYM(rb_intern("italic"));
    symbol_weight         = ID2SYM(rb_intern("weight"));
}

