using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using Ruby;
using VALUE = System.Int32;
using StarEngine.Core;

namespace StarEngine.Ruby
{
    public partial class RubyInterpreter
    {
        private ITexture GetTexture(VALUE rbTexture)
        {
            return this.GetCachedObject<ITexture>(rbTexture);
        }

        private void InitializeTextureClass()
        {
            VALUE cTexture = SERubyConsts.ForName("Texture");

            RUBY.rb_define_method(cTexture, "__cs_initialize__",
                delegate(VALUE self)
                {
                    RUBY.Send(self, "__cs_set_finalizer__");
                    return RUBY.Qnil;
                });

            RUBY.rb_define_private_method(cTexture, "initialize_copy",
                delegate(VALUE self, VALUE rbTexture)
                {
                    ITexture csClonedTexture = (ITexture)this.GetTexture(rbTexture).Clone();
                    this.AddCachedObject(self, csClonedTexture);
                    return self;
                });

            RUBY.rb_define_singleton_method(cTexture, "new",
                delegate(VALUE self, VALUE rbWidth, VALUE rbHeight)
                {
                    Size csSize = new Size(RUBY.NUM2INT(rbWidth), RUBY.NUM2INT(rbHeight));
                    ITexture csTexture = this.GameEnvironment.TextureFactory.Create(csSize);
                    VALUE rbTexture = RUBY.Send(self, "_orig_new");
                    this.AddCachedObject(rbTexture, csTexture);
                    RUBY.rb_iv_set(rbTexture, "@width", RUBY.INT2NUM(csTexture.Width));
                    RUBY.rb_iv_set(rbTexture, "@height", RUBY.INT2NUM(csTexture.Height));
                    return rbTexture;
                });

            RUBY.rb_define_singleton_method(cTexture, "load",
                delegate(VALUE self, VALUE path)
                {
                    string csPath = PathExtensions.GetFullPathAndExtension(RUBY.StringValuePtr(path));
                    ITexture csTexture;
                    try
                    {
                        csTexture = this.GameEnvironment.TextureFactory.Load(csPath);
                    }
                    catch (FileNotFoundException e)
                    {
                        RUBY.rb_raise(SERubyConsts.ForName("::Errno::ENOENT"), e.FileName);
                        return RUBY.Qnil;
                    }
                    catch (ArgumentException)
                    {
                        RUBY.rb_raise(SERubyConsts.ForName("StarEngineError"), "invalid image file");
                        return RUBY.Qnil;
                    }
                    VALUE rbTexture = RUBY.Send(self, "_orig_new");
                    this.AddCachedObject(rbTexture, csTexture);
                    RUBY.rb_iv_set(rbTexture, "@width", RUBY.INT2NUM(csTexture.Width));
                    RUBY.rb_iv_set(rbTexture, "@height", RUBY.INT2NUM(csTexture.Height));
                    return rbTexture;
                });

            RUBY.rb_define_method(cTexture, "dispose",
                delegate(VALUE self)
                {
                    RUBY.rb_check_frozen(self);
                    ITexture csTexture = this.GetTexture(self);
                    if (csTexture == null)
                    {
                        RUBY.rb_raise(SERubyConsts.ForName("StarEngineError"), "already disposed");
                        return RUBY.Qnil;
                    }
                    csTexture.Dispose();
                    this.RemoveCachedObject(self);
                    return RUBY.Qnil;
                });

            RUBY.rb_define_method(cTexture, "disposed?",
                delegate(VALUE self)
                {
                    return !this.ContainsCachedObject(self) ? RUBY.Qtrue : RUBY.Qfalse;
                });

            RUBY.rb_define_method(cTexture, "get_pixel",
                delegate(VALUE self, VALUE rbX, VALUE rbY)
                {
                    ITexture csTexture = this.GetTexture(self);
                    if (csTexture == null)
                    {
                        RUBY.rb_raise(SERubyConsts.ForName("StarEngineError"), "disposed texture");
                        return RUBY.Qnil;
                    }
                    int csX = RUBY.NUM2INT(rbX);
                    int csY = RUBY.NUM2INT(rbY);
                    try
                    {
                        return ColorClass.ToRubyColor(csTexture.GetPixel(csX, csY));
                    }
                    catch (IndexOutOfRangeException)
                    {
                        RUBY.rb_raise(SERubyConsts.ForName("::IndexError"),
                            string.Format("index out of range: ({0},{1})", csX, csY));
                        return RUBY.Qnil;
                    }
                });

            RUBY.rb_define_method(cTexture, "set_pixel",
                delegate(VALUE self, VALUE rbX, VALUE rbY, VALUE rbColor)
                {
                    RUBY.rb_check_frozen(self);
                    ITexture csTexture = this.GetTexture(self);
                    if (csTexture == null)
                    {
                        RUBY.rb_raise(SERubyConsts.ForName("StarEngineError"), "disposed texture");
                        return RUBY.Qnil;
                    }
                    int csX = RUBY.NUM2INT(rbX);
                    int csY = RUBY.NUM2INT(rbY);
                    Color csColor = ColorClass.ToCSharpColor(rbColor);
                    try
                    {
                        csTexture.SetPixel(csX, csY, csColor);
                    }
                    catch (IndexOutOfRangeException)
                    {
                        RUBY.rb_raise(SERubyConsts.ForName("::IndexError"),
                            string.Format("index out of range: ({0},{1})", csX, csY));
                    }
                    return RUBY.Qnil;
                });

            RUBY.rb_define_method(cTexture, "change_hue",
                delegate(VALUE self, VALUE rbDHue)
                {
                    RUBY.rb_check_frozen(self);
                    ITexture csTexture = this.GetTexture(self);
                    if (csTexture == null)
                    {
                        RUBY.rb_raise(SERubyConsts.ForName("StarEngineError"), "disposed texture");
                        return RUBY.Qnil;
                    }
                    csTexture.ChangeHue(RUBY.NUM2INT(rbDHue));
                    return RUBY.Qnil;
                });

            RUBY.rb_define_method(cTexture, "clear",
                delegate(VALUE self)
                {
                    RUBY.rb_check_frozen(self);
                    ITexture csTexture = this.GetTexture(self);
                    if (csTexture == null)
                    {
                        RUBY.rb_raise(SERubyConsts.ForName("StarEngineError"), "disposed texture");
                        return RUBY.Qnil;
                    }
                    csTexture.Clear();
                    return RUBY.Qnil;
                });

            RUBY.rb_define_method(cTexture, "fill",
                delegate(VALUE self, VALUE rbColor)
                {
                    RUBY.rb_check_frozen(self);
                    ITexture csTexture = this.GetTexture(self);
                    if (csTexture == null)
                    {
                        RUBY.rb_raise(SERubyConsts.ForName("StarEngineError"), "disposed texture");
                        return RUBY.Qnil;
                    }
                    csTexture.Fill(ColorClass.ToCSharpColor(rbColor));
                    return RUBY.Qnil;
                });

            RUBY.rb_define_method(cTexture, "fill_rect",
                delegate(VALUE self, VALUE rbX, VALUE rbY, VALUE rbWidth, VALUE rbHeight, VALUE rbColor)
                {
                    RUBY.rb_check_frozen(self);
                    ITexture csTexture = this.GetTexture(self);
                    if (csTexture == null)
                    {
                        RUBY.rb_raise(SERubyConsts.ForName("StarEngineError"), "disposed texture");
                        return RUBY.Qnil;
                    }

                    int csX = RUBY.NUM2INT(rbX);
                    int csY = RUBY.NUM2INT(rbY);
                    int csWidth = RUBY.NUM2INT(rbWidth);
                    int csHeight = RUBY.NUM2INT(rbHeight);
                    Color csColor = ColorClass.ToCSharpColor(rbColor);
                    csTexture.FillRectangle(new Rectangle(csX, csY, csWidth, csHeight), csColor);
                    return RUBY.Qnil;
                });

            RUBY.rb_define_method(cTexture, "render_text",
                delegate(VALUE self, VALUE rbText, VALUE rbX, VALUE rbY, VALUE rbFont, VALUE rbColor)
                {
                    RUBY.rb_check_frozen(self);
                    ITexture csTexture = this.GetTexture(self);
                    if (csTexture == null)
                    {
                        RUBY.rb_raise(SERubyConsts.ForName("StarEngineError"), "disposed texture");
                        return RUBY.Qnil;
                    }
                    string csText = RUBY.StringValuePtr(rbText);
                    Point csLocation = new Point(RUBY.NUM2INT(rbX), RUBY.NUM2INT(rbY));
                    Font csFont = this.GetCachedObject<Font>(rbFont);
                    Color csColor = ColorClass.ToCSharpColor(rbColor);
                    csTexture.RenderText(csText, csLocation, csFont, csColor);
                    return RUBY.Qnil;
                });
            RUBY.rb_define_private_method(cTexture, "__cs_render_texture__",
                delegate(VALUE self, VALUE rbTexture, VALUE rbX, VALUE rbY, VALUE rbSrcX, VALUE rbSrcY, VALUE rbSrcWidth, VALUE rbSrcHeight, VALUE rbScaleX, VALUE rbScaleY, VALUE rbAngle, VALUE rbCenterX, VALUE rbCenterY, VALUE rbAlpha, VALUE rbBlendType, VALUE rbTone)
                {
                    RUBY.rb_check_frozen(self);
                    ITexture csTexture = this.GetTexture(self);
                    if (csTexture == null)
                    {
                        RUBY.rb_raise(SERubyConsts.ForName("StarEngineError"), "disposed texture");
                        return RUBY.Qnil;
                    }
                    ITexture csTexture2 = this.GetTexture(rbTexture);
                    if (csTexture2 == null)
                    {
                        RUBY.rb_raise(SERubyConsts.ForName("StarEngineError"), "disposed texture");
                        return RUBY.Qnil;
                    }
                    
                    int csX = RUBY.NUM2INT(rbX);
                    int csY = RUBY.NUM2INT(rbY);

                    int csSrcX = (rbSrcX == RUBY.Qnil) ? 0 : RUBY.NUM2INT(rbSrcX);
                    int csSrcY = (rbSrcY == RUBY.Qnil) ? 0 : RUBY.NUM2INT(rbSrcY);
                    int csSrcWidth = (rbSrcWidth == RUBY.Qnil) ? csTexture.Width : RUBY.NUM2INT(rbSrcWidth);
                    int csSrcHeight = (rbSrcHeight == RUBY.Qnil) ? csTexture.Height : RUBY.NUM2INT(rbSrcHeight);
                    Rectangle csSrcRect =
                        new Rectangle(csSrcX, csSrcY, csSrcWidth, csSrcHeight);
                    double csScaleX = (rbScaleX == RUBY.Qnil) ? 1.0 : RUBY.NUM2DBL(rbScaleX);
                    double csScaleY = (rbScaleY == RUBY.Qnil) ? 1.0 : RUBY.NUM2DBL(rbScaleY);
                    int csAngle = (rbAngle == RUBY.Qnil) ? 0 : RUBY.NUM2INT(rbAngle);
                    double csCenterX = (rbCenterX == RUBY.Qnil) ? 0 : RUBY.NUM2DBL(rbCenterX);
                    double csCenterY = (rbCenterY == RUBY.Qnil) ? 0 : RUBY.NUM2DBL(rbCenterY);
                    byte csAlpha = (byte)((rbAlpha == RUBY.Qnil) ? 255 : Math.Min(255, Math.Max(0, RUBY.NUM2INT(rbAlpha))));
                    BlendType csBlendType = (rbBlendType == RUBY.Qnil) ? BlendType.Alpha : BlendTypeEnum.ToCSharpBlendType(rbBlendType);
                    Tone csTone = (rbTone == RUBY.Qnil) ? Tone.Empty : ToneClass.ToCSharpTone(rbTone);

                    AffineMatrix mat = AffineMatrix.I;
                    if (csScaleX != 1.0 || csScaleY != 1.0 || csAngle % Math4096.MaxAngle != 0)
                    {
                        mat.Concat(new AffineMatrix(1, 0, 0, 1, -csCenterX, -csCenterY));
                        if (csScaleX != 1.0 || csScaleY != 1.0)
                            mat.Concat(new AffineMatrix(csScaleX, 0, 0, csScaleY, 0, 0));
                        if (csAngle % Math4096.MaxAngle != 0)
                        {
                            double cos = Math4096.Cos(csAngle);
                            double sin = Math4096.Sin(csAngle);
                            mat.Concat(new AffineMatrix(cos, -sin, sin, cos, 0, 0));
                        }
                        mat.Concat(new AffineMatrix(1, 0, 0, 1, csCenterX, csCenterY));
                    }
                    mat.Concat(new AffineMatrix(1, 0, 0, 1, csX, csY));

                    try
                    {
                        csTexture.RenderTexture(csTexture2, csSrcRect, mat, csAlpha, csBlendType, csTone);
                    }
                    catch (ArgumentException e)
                    {
                        RUBY.rb_raise(SERubyConsts.ForName("::ArgumentError"), e.Message);
                    }

                    return RUBY.Qnil;
                });
        }
    }
}
