/*
 * Copyright (C) 2013 FooProject
 * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Drawing;
using FooEditEngine;
using SharpDX;
using D2D = SharpDX.Direct2D1;
using DW = SharpDX.DirectWrite;
using DXGI = SharpDX.DXGI;
using System.Runtime.InteropServices;

namespace FooEditEngine.Windows
{

    class D2DTextRender : IEditorRender, IDisposable
    {
        D2DRenderCommon common;
        FooTextBox TextBox;
        System.Drawing.Color ForegroundColor = SystemColors.ControlText,
            BackgroundColor = SystemColors.Control,
            HilightColor = System.Drawing.Color.DeepSkyBlue,
            Keyword1Color = System.Drawing.Color.Blue,
            Keyword2Color = System.Drawing.Color.DarkCyan,
            LiteralColor = System.Drawing.Color.Brown,
            UrlColor = System.Drawing.Color.Blue,
            ControlCharColor = System.Drawing.Color.Gray,
            CommentColor = System.Drawing.Color.Green,
            InsertCaretColor = System.Drawing.Color.Black,
            OverwriteCaretColor = System.Drawing.Color.Black,
            LineMarkerColor = System.Drawing.Color.WhiteSmoke;
        string fontName;
        float fontSize;

        public D2DTextRender(FooTextBox textbox)
        {
            this.TextBox = textbox;

            textbox.SizeChanged += new EventHandler(textbox_SizeChanged);
            textbox.FontChanged += new EventHandler(textbox_FontChanged);
            textbox.HandleCreated += new EventHandler(textbox_HandleCreated);
            textbox.HandleDestroyed += new EventHandler(textbox_HandleDestroyed);

            Size size = textbox.Size;
            this.common = new D2DRenderCommon();
            this.common.ConstructRender = ConstructRenderHandler;
            this.common.ConstrctedResource = ConstructedResourceHandler;
            this.common.DestructRender = this.DestructRenderHandler;
            this.common.GetDpi = this.GetDpiHandler;
            this.common.ConstructDeviceResource(size.Width, size.Height);
            this.fontName = textbox.Font.Name;
            this.fontSize = textbox.Font.Size;
            this.common.InitTextFormat(textbox.Font.Name, (float)textbox.Font.Size);
        }

        private void GetDpiHandler(out float dpix, out float dpiy)
        {
            IntPtr hDc = NativeMethods.GetDC(IntPtr.Zero);
            dpix = NativeMethods.GetDeviceCaps(hDc, NativeMethods.LOGPIXELSX);
            dpiy = NativeMethods.GetDeviceCaps(hDc, NativeMethods.LOGPIXELSY);
            NativeMethods.ReleaseDC(IntPtr.Zero, hDc);
        }

        public event EventHandler ChangedRightToLeft;

        void textbox_HandleDestroyed(object sender, EventArgs e)
        {
            this.common.DestructDeviceResource();
        }

        void textbox_HandleCreated(object sender, EventArgs e)
        {
            FooTextBox textbox = (FooTextBox)sender;
            this.TextBox = textbox;
            this.common.ConstructDeviceResource(textbox.Width, textbox.Height);
        }

        void textbox_FontChanged(object sender, EventArgs e)
        {
            FooTextBox textbox = (FooTextBox)sender;
            Font font = textbox.Font;
            this.fontName = font.Name;
            this.fontSize = font.Size;
            DW.FontWeight weigth = font.Bold ? DW.FontWeight.Bold : DW.FontWeight.Normal;
            DW.FontStyle style = font.Italic ? DW.FontStyle.Italic : DW.FontStyle.Normal;
            this.common.InitTextFormat(font.Name, font.Size,weigth,style);
        }

        void textbox_SizeChanged(object sender, EventArgs e)
        {
            FooTextBox textbox = (FooTextBox)sender;
            this.common.ReConstructDeviceResource(this.TextBox.Width, this.TextBox.Height);
        }

        Color4 ToColor4(System.Drawing.Color color)
        {
            return new Color4(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f);
        }

        public bool RightToLeft
        {
            get
            {
                return this.common.RightToLeft;
            }
            set
            {
                this.common.RightToLeft = value;
                this.ChangedRightToLeft(this, null);
            }
        }

        public TextAntialiasMode TextAntialiasMode
        {
            get
            {
                return this.common.TextAntialiasMode;
            }
            set
            {
                this.common.TextAntialiasMode = value;
            }
        }

        public System.Drawing.Color Foreground
        {
            get
            {
                return this.ForegroundColor;
            }
            set
            {
                this.ForegroundColor = value;
                this.common.Foreground = this.ToColor4(value);
            }
        }

        public System.Drawing.Color Background
        {
            get
            {
                return this.BackgroundColor;
            }
            set
            {
                this.BackgroundColor = value;
                this.common.Background = this.ToColor4(value);
            }
        }

        public System.Drawing.Color InsertCaret
        {
            get
            {
                return this.InsertCaretColor;
            }
            set
            {
                this.InsertCaretColor = value;
                this.common.InsertCaret = this.ToColor4(value);
            }
        }

        public System.Drawing.Color OverwriteCaret
        {
            get
            {
                return this.OverwriteCaretColor;
            }
            set
            {
                this.OverwriteCaretColor = value;
                this.common.OverwriteCaret = this.ToColor4(value);
            }
        }

        public System.Drawing.Color LineMarker
        {
            get
            {
                return this.LineMarkerColor;
            }
            set
            {
                this.LineMarkerColor = value;
                this.common.LineMarker = this.ToColor4(value);
            }
        }

        public System.Drawing.Color ControlChar
        {
            get
            {
                return this.ControlCharColor;
            }
            set
            {
                this.ControlCharColor = value;
                this.common.ControlChar = this.ToColor4(value);
            }
        }

        public System.Drawing.Color Url
        {
            get
            {
                return this.UrlColor;
            }
            set
            {
                this.UrlColor = value;
                this.common.Url = this.ToColor4(value);
            }
        }

        public System.Drawing.Color Hilight
        {
            get
            {
                return this.HilightColor;
            }
            set
            {
                this.HilightColor = value;
                this.common.Hilight = this.ToColor4(this.HilightColor);
            }
        }

        public System.Drawing.Color Comment
        {
            get
            {
                return this.CommentColor;
            }
            set
            {
                this.CommentColor = value;
                this.common.Comment = this.ToColor4(value);
            }
        }

        public System.Drawing.Color Literal
        {
            get
            {
                return this.LiteralColor;
            }
            set
            {
                this.LiteralColor = value;
                this.common.Literal = this.ToColor4(value);
            }
        }

        public System.Drawing.Color Keyword1
        {
            get
            {
                return this.Keyword1Color;
            }
            set
            {
                this.Keyword1Color = value;
                this.common.Keyword1 = this.ToColor4(value);
            }
        }

        public System.Drawing.Color Keyword2
        {
            get
            {
                return this.Keyword2Color;
            }
            set
            {
                this.Keyword2Color = value;
                this.common.Keyword2 = this.ToColor4(value);
            }
        }

        public Rectangle TextArea
        {
            get { return this.common.ClipRect; }
            set { this.common.ClipRect = value; }
        }

        public double LineNemberWidth
        {
            get
            {
                return this.common.LineNemberWidth;
            }
        }

        public Size emSize
        {
            get
            {
                return this.common.emSize;
            }
        }

        public int TabWidthChar
        {
            get { return this.common.TabWidthChar; }
            set
            {
                if (value == 0)
                    return;
                this.common.TabWidthChar = value;
            }
        }

        public double FoldingWidth
        {
            get
            {
                return this.common.FoldingWidth;
            }
        }

        public bool ShowFullSpace
        {
            get
            {
                return this.common.ShowFullSpace;
            }
            set
            {
                this.common.ShowFullSpace = value;
            }
        }

        public bool ShowHalfSpace
        {
            get
            {
                return this.common.ShowHalfSpace;
            }
            set
            {
                this.common.ShowHalfSpace = value;
            }
        }

        public bool ShowTab
        {
            get
            {
                return this.common.ShowTab;
            }
            set
            {
                this.common.ShowTab = value;
            }
        }

        public bool ShowLineBreak
        {
            get
            {
                return this.common.ShowLineBreak;
            }
            set
            {
                this.common.ShowLineBreak = value;
            }
        }

        public event ChangedRenderResourceEventHandler ChangedRenderResource
        {
            add
            {
                this.common.ChangedRenderResource += value;
            }
            remove
            {
                this.common.ChangedRenderResource -= value;
            }
        }

        public void DrawLine(Point from, Point to)
        {
            this.common.DrawLine(from, to);
        }

        public void DrawCachedBitmap(Rectangle rect)
        {
            this.common.DrawCachedBitmap(rect);
        }

        public void CacheContent()
        {
            this.common.CacheContent();
        }

        public bool IsVaildCache()
        {
            return this.common.IsVaildCache();
        }

        public void BeginDraw()
        {
            this.common.BegineDraw();
        }

        public void EndDraw()
        {
            this.common.EndDraw();
        }

        public void DrawString(string str, double x, double y, StringAlignment align, Size layoutRect)
        {
            this.common.DrawString(str, x, y,align,layoutRect);
        }

        public void FillRectangle(Rectangle rect, FillRectType type)
        {
            this.common.FillRectangle(rect, type);
        }

        public void DrawFoldingMark(bool expand, double x, double y)
        {
            this.common.DrawFoldingMark(expand, x, y);
        }

        public void FillBackground(Rectangle rect)
        {
            this.common.FillBackground(rect);
        }

        public void DrawOneLine(LineToIndexTable lti, int row, double x, double y, IEnumerable<Selection> SelectRanges)
        {
            this.common.DrawOneLine(lti,
                row,
                x,
                y,
                SelectRanges,
                null);
        }

        public ITextLayout CreateLaytout(string str, SyntaxInfo[] syntaxCollection, IEnumerable<Marker> MarkerRanges)
        {
            return this.common.CreateLaytout(str,syntaxCollection,MarkerRanges);
        }

        public List<LineToIndexTableData> BreakLine(Document doc, LineToIndexTable layoutLineCollection, int startIndex, int endIndex, double wrapwidth)
        {
            return this.common.BreakLine(doc,layoutLineCollection, startIndex, endIndex, wrapwidth);
        }

        public void Dispose()
        {
            this.common.Dispose();
        }

        D2D.WindowRenderTarget WindowRender;

        D2D.RenderTarget ConstructRenderHandler(D2D.Factory factory, D2D.RenderTargetProperties prop, double width, double height)
        {
            D2D.HwndRenderTargetProperties hwndProp = new D2D.HwndRenderTargetProperties();
            hwndProp.Hwnd = this.TextBox.Handle;
            hwndProp.PixelSize = new SharpDX.Size2(this.TextBox.Size.Width,this.TextBox.Size.Height);
            hwndProp.PresentOptions = D2D.PresentOptions.Immediately;
            this.WindowRender = new D2D.WindowRenderTarget(factory,prop,hwndProp);
            return this.WindowRender;
        }

        void ConstructedResourceHandler()
        {
            this.common.Foreground = this.ToColor4(this.ForegroundColor);
            this.common.Background = this.ToColor4(this.BackgroundColor);
            this.common.ControlChar = this.ToColor4(this.ControlCharColor);
            this.common.Url = this.ToColor4(this.UrlColor);
            this.common.Keyword1 = this.ToColor4(this.Keyword1Color);
            this.common.Keyword2 = this.ToColor4(this.Keyword2Color);
            this.common.Literal = this.ToColor4(this.LiteralColor);
            this.common.Comment = this.ToColor4(this.CommentColor);
            this.common.Hilight = this.ToColor4(this.HilightColor);
            this.common.LineMarker = this.ToColor4(this.LineMarkerColor);
            this.common.InsertCaret = this.ToColor4(this.InsertCaretColor);
            this.common.OverwriteCaret = this.ToColor4(this.OverwriteCaretColor);
        }

        void DestructRenderHandler()
        {
            if (this.WindowRender != null)
                this.WindowRender.Dispose();
        }
    }

    internal static class NativeMethods
    {
        public const int LOGPIXELSX = 88;
        public const int LOGPIXELSY = 90;

        [DllImport("gdi32.dll")]
        public static extern int GetDeviceCaps(IntPtr hDc, int nIndex);

        [DllImport("user32.dll")]
        public static extern IntPtr GetDC(IntPtr hWnd);

        [DllImport("user32.dll")]
        public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDc);
    }
}
