/*
 * 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 Windows.Graphics.Display;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using SharpDX;
using DXGI = SharpDX.DXGI;
using D2D = SharpDX.Direct2D1;
using D3D = SharpDX.Direct3D;
using D3D11 = SharpDX.Direct3D11;
using FooEditEngine.Metro;
using DotNetTextStore;
using DotNetTextStore.UnmanagedAPI.TSF;
using DotNetTextStore.UnmanagedAPI.WinDef;

namespace FooEditEngine
{
    sealed class D2DRender : D2DRenderBase,IEditorRender
    {
        SurfaceImageSource SurfaceImage;
        DXGI.ISurfaceImageSourceNative SurfaceImageNative;
        D2D.Device D2DDevice;
        D2D.DeviceContext D2DContext;
        D2D.Bitmap1 Bitmap;
        Size Size = new Size();
        DXGI.Surface Surface;
        TextStore2 store;

        public D2DRender(FooTextBox textbox,Windows.UI.Xaml.Shapes.Rectangle rect,TextStore2 store)
            : base()
        {
            this.ConstructDeviceResource(rect.ActualWidth,rect.ActualHeight);
            this.InitTextFormat(textbox.FontFamily, textbox.FontSize);

            this.store = store;

            this.Size.Width = rect.ActualWidth;
            this.Size.Height = rect.ActualHeight;

            this.CreateSurface(rect, rect.ActualWidth, rect.ActualHeight);

            base.PreDrawOneLine = D2DRender_PreDrawOneLine;

            this.Foreground = D2DRenderBase.ToColor4( textbox.Foreground);
            this.Background = D2DRenderBase.ToColor4(textbox.Background);
            this.Hilight = D2DRenderBase.ToColor4(textbox.Hilight);
            this.Keyword1 = D2DRenderBase.ToColor4(textbox.Keyword1);
            this.Keyword2 = D2DRenderBase.ToColor4(textbox.Keyword2);
            this.Literal = D2DRenderBase.ToColor4(textbox.Literal);
            this.Url = D2DRenderBase.ToColor4(textbox.URL);
            this.ControlChar = D2DRenderBase.ToColor4(textbox.ControlChar);
            this.Comment = D2DRenderBase.ToColor4(textbox.Comment);
            this.InsertCaret = D2DRenderBase.ToColor4(textbox.InsertCaret);
            this.OverwriteCaret = D2DRenderBase.ToColor4(textbox.OverwriteCaret);
            this.LineMarker = D2DRenderBase.ToColor4(textbox.LineMarker);
            this.UpdateArea = D2DRenderBase.ToColor4(textbox.UpdateArea);
            this.LineNumber = D2DRenderBase.ToColor4(textbox.LineNumber);
        }

        void D2DRender_PreDrawOneLine(MyTextLayout layout,LineToIndexTable lti, int row, double x, double y)
        {
            using (Unlocker locker = this.store.LockDocument(false))
            {
                int lineIndex = lti.GetIndexFromLineNumber(row);
                int lineLength = lti.GetLengthFromLineNumber(row);
                foreach (TextDisplayAttribute attr in this.store.EnumAttributes(lineIndex, lineIndex + lineLength))
                {
                    if (attr.startIndex == attr.endIndex)
                        continue;
                    int length = attr.endIndex - attr.startIndex;
                    int start = attr.startIndex - lineIndex;

                    HilightType type = HilightType.None;
                    Color4? color = null;
                    switch (attr.attribute.lsStyle)
                    {
                        case TF_DA_LINESTYLE.TF_LS_DOT:
                            type = HilightType.Dot;
                            color = this.Foreground;
                            break;
                        case TF_DA_LINESTYLE.TF_LS_SOLID:
                            type = HilightType.Sold;
                            color = this.Foreground;
                            break;
                        case TF_DA_LINESTYLE.TF_LS_DASH:
                            type = HilightType.Dash;
                            color = this.Foreground;
                            break;
                        case TF_DA_LINESTYLE.TF_LS_SQUIGGLE:
                            type = HilightType.Squiggle;
                            color = this.Foreground;
                            break;
                    }

                    if (attr.attribute.crBk.type != TF_DA_COLORTYPE.TF_CT_NONE)
                    {
                        type = HilightType.Select;
                        color = this.Hilight;
                    }

                    this.DrawMarkerEffect(layout, type, start, length, x, y, attr.attribute.fBoldLine, color);
                }
            }
        }

        public override void GetDpi(out float dpix, out float dpiy)
        {
            dpix = DisplayInformation.GetForCurrentView().LogicalDpi;
            dpiy = DisplayInformation.GetForCurrentView().LogicalDpi;
        }

        public bool Resize(Windows.UI.Xaml.Shapes.Rectangle rect, double width, double height)
        {
            if (this.Size.Width == width && this.Size.Height == height)
                return false;
            this.ReConstructDeviceResource(width, height);
            this.CreateSurface(rect, width, height);
            return true;
        }

        public bool IsCanDraw()
        {
            return this.Size.Height != 0 && this.Size.Width != 0;
        }

        public void DrawContent(EditView view,bool IsEnabled,Rectangle updateRect)
        {
            SharpDX.Point offset;
            this.Surface = this.SurfaceImageNative.BeginDraw(
                new SharpDX.Rectangle(0, 0, (int)this.Size.Width, (int)this.Size.Height), out offset);
            float dpix, dpiy;
            this.GetDpi(out dpix, out dpiy);
            D2D.BitmapProperties1 prop = new D2D.BitmapProperties1(new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Premultiplied),
                    dpix, dpiy, D2D.BitmapOptions.Target | D2D.BitmapOptions.CannotDraw);
            this.Bitmap = new D2D.Bitmap1(this.D2DContext, this.Surface, prop);
            this.D2DContext.Target = this.Bitmap;
            this.D2DContext.Transform = Matrix3x2.Translation(offset.X, offset.Y);
            base.BegineDraw();

            if (IsEnabled)
                view.Draw(updateRect);
            else
                this.FillBackground(updateRect);

            base.EndDraw();
            this.Surface.Dispose();
            this.Bitmap.Dispose();
            this.SurfaceImageNative.EndDraw();
        }

        void CreateSurface(Windows.UI.Xaml.Shapes.Rectangle rect, double width, double height)
        {
            if (this.SurfaceImageNative != null)
                this.SurfaceImageNative.Dispose();
            this.SurfaceImage = new SurfaceImageSource((int)width, (int)height);
            this.SurfaceImageNative = ComObject.As<DXGI.ISurfaceImageSourceNative>(this.SurfaceImage);
            this.SurfaceImageNative.Device = this.DXGIDevice;
            this.Size.Width = width;
            this.Size.Height = height;
            ImageBrush brush = new ImageBrush();
            brush.ImageSource = this.SurfaceImage;
            rect.Fill = brush;
        }

        protected override D2D.RenderTarget ConstructRender(D2D.Factory1 factory, D2D.RenderTargetProperties prop, double width, double height)
        {
            this.D2DDevice = new D2D.Device(factory,this.DXGIDevice);
            this.D2DContext = new D2D.DeviceContext(this.D2DDevice, D2D.DeviceContextOptions.None);
            return this.D2DContext;
        }

        protected override void ConstrctedResource()
        {
        }

        protected override void DestructRender()
        {
            if (this.D2DDevice != null)
                this.D2DDevice.Dispose();
            if (this.D2DContext != null)
                this.D2DContext.Dispose();
        }

        protected override void ReCreateTarget()
        {
        }
    }
}
