/*
 * 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 FooEditEngine;
using SharpDX;
using D2D = SharpDX.Direct2D1;
using DW = SharpDX.DirectWrite;
using DXGI = SharpDX.DXGI;
using System.Runtime.InteropServices;

namespace FooEditEngine
{
    delegate void PreDrawOneLineHandler(MyTextLayout layout);

    delegate void GetDpiHandler(out float dpix,out float dpiy);

    /// <summary>
    /// 文字列のアンチエイリアシングモードを指定する
    /// </summary>
    public enum TextAntialiasMode
    {
        /// <summary>
        /// 最適なものが選択されます
        /// </summary>
        Default = D2D.TextAntialiasMode.Default,
        /// <summary>
        /// ClearTypeでアンチエイリアシングを行います
        /// </summary>
        ClearType = D2D.TextAntialiasMode.Cleartype,
        /// <summary>
        /// グレイスケールモードでアンチエイリアシングを行います
        /// </summary>
        GrayScale = D2D.TextAntialiasMode.Grayscale,
        /// <summary>
        /// アンチエイリアシングを行いません
        /// </summary>
        Aliased = D2D.TextAntialiasMode.Aliased,
    }

    sealed class ColorBrushCollection
    {
        ResourceManager<Color4, D2D.SolidColorBrush> cache = new ResourceManager<Color4, D2D.SolidColorBrush>();
        D2D.RenderTarget _render;

        public event EventHandler RenderChanged;

        public D2D.RenderTarget Render
        {
            get
            {
                return this._render;
            }
            set
            {
                this._render = value;
                if (this.RenderChanged != null)
                    this.RenderChanged(this, null);
            }
        }

        public D2D.SolidColorBrush Get(Color4 key)
        {
            if (this.Render == null || this.Render.IsDisposed)
                throw new InvalidOperationException();
            D2D.SolidColorBrush brush;
            bool result = cache.TryGetValue(key, out brush);
            if (!result)
            {
                brush = new D2D.SolidColorBrush(this.Render, key);
                cache.Add(key, brush);
            }
            
            return brush;
        }

        public void Clear()
        {
            cache.Clear();
        }
    }

    sealed class StrokeCollection
    {
        ResourceManager<HilightType, D2D.StrokeStyle> cache = new ResourceManager<HilightType, D2D.StrokeStyle>();

        public D2D.Factory Factory;

        public D2D.StrokeStyle Get(HilightType type)
        {
            if(this.Factory == null || this.Factory.IsDisposed)
                throw new InvalidOperationException();
            D2D.StrokeStyle stroke;
            if (this.cache.TryGetValue(type, out stroke))
                return stroke;

            D2D.StrokeStyleProperties prop = new D2D.StrokeStyleProperties();
            prop.DashCap = D2D.CapStyle.Flat;
            prop.DashOffset = 0;
            prop.DashStyle = D2D.DashStyle.Solid;
            prop.EndCap = D2D.CapStyle.Flat;
            prop.LineJoin = D2D.LineJoin.Miter;
            prop.MiterLimit = 0;
            prop.StartCap = D2D.CapStyle.Flat;
            switch (type)
            {
                case HilightType.Sold:
                case HilightType.Url:
                case HilightType.Squiggle:
                    prop.DashStyle = D2D.DashStyle.Solid;
                    break;
                case HilightType.Dash:
                    prop.DashStyle = D2D.DashStyle.Dash;
                    break;
                case HilightType.DashDot:
                    prop.DashStyle = D2D.DashStyle.DashDot;
                    break;
                case HilightType.DashDotDot:
                    prop.DashStyle = D2D.DashStyle.DashDotDot;
                    break;
                case HilightType.Dot:
                    prop.DashStyle = D2D.DashStyle.Dot;
                    break;
            }
            stroke = new D2D.StrokeStyle(this.Factory, prop);
            this.cache.Add(type, stroke);
            return stroke;
        }
        public void Clear()
        {
            cache.Clear();
        }
    }

    sealed class EffectCollection
    {
        ResourceManager<Color4, ResourceManager<HilightType, DrawingEffect>> cache = new ResourceManager<Color4, ResourceManager<HilightType, DrawingEffect>>();
        public DrawingEffect Get(Color4 color, HilightType type)
        {
            ResourceManager<HilightType, DrawingEffect> hilights;
            DrawingEffect effect;
            if (this.cache.TryGetValue(color, out hilights))
            {
                if (hilights.TryGetValue(type, out effect))
                    return effect;
                effect = new DrawingEffect(type, color);
                hilights.Add(type, effect);
                return effect;
            }
            effect = new DrawingEffect(type, color);
            hilights = new ResourceManager<HilightType, DrawingEffect>();
            hilights.Add(type, effect);
            this.cache.Add(color, hilights);
            return effect;
        }
        public void Clear()
        {
            foreach (ResourceManager<HilightType, DrawingEffect> hilights in this.cache.Values)
                hilights.Clear();
            cache.Clear();
        }
    }

    class D2DRenderCommon : IDisposable
    {
        ColorBrushCollection Brushes = new ColorBrushCollection();
        StrokeCollection Strokes = new StrokeCollection();
        EffectCollection Effects = new EffectCollection();
        InlineManager HiddenChars;
        TextAntialiasMode _TextAntialiasMode;
        Color4 _ControlChar,_Forground,_URL,_Hilight;
        DW.Factory DWFactory;
#if METRO
        D2D.Factory1 D2DFactory;
#else
        D2D.Factory D2DFactory;
#endif
        DW.TextFormat format;
        D2D.Bitmap bitmap;
        D2D.RenderTarget render;
        CustomTextRenderer textRender;
        int tabLength = 8;
        bool hasCache, _ShowLineBreak;
        Size renderSize = new Size();
        Color4 _Comment, _Literal, _Keyword1, _Keyword2;

        public D2DRenderCommon()
        {
            this.DWFactory = new DW.Factory(DW.FactoryType.Shared);
#if METRO
            this.D2DFactory = new D2D.Factory1(D2D.FactoryType.MultiThreaded);
#else
            this.D2DFactory = new D2D.Factory(D2D.FactoryType.MultiThreaded);
#endif
            this.Strokes.Factory = this.D2DFactory;
            this.ChangedRenderResource += (s, e) => { };
            this.ChangedRightToLeft += (s, e) => { };
        }

        public event ChangedRenderResourceEventHandler ChangedRenderResource;

        public event EventHandler ChangedRightToLeft;

        public const int MiniumeWidth = 40;    //これ以上ないと誤操作が起こる

        public void InitTextFormat(string fontName, float fontSize, DW.FontWeight fontWeigth = DW.FontWeight.Normal,DW.FontStyle fontStyle = DW.FontStyle.Normal)
        {
            if(this.format != null)
                this.format.Dispose();
            
            this.format = new DW.TextFormat(this.DWFactory, fontName,fontWeigth,fontStyle, fontSize / 72.0f * 96.0f);
            this.format.WordWrapping = DW.WordWrapping.NoWrap;

            if (this.HiddenChars == null)
                this.HiddenChars = new InlineManager(this.DWFactory, this.format, this.ControlChar, this.Brushes);
            else
                this.HiddenChars.Format = this.format;

            this.TabWidthChar = this.TabWidthChar;

            this.hasCache = false;

            float dpix, dpiy;
            this.GetDpi(out dpix, out dpiy);
            MyTextLayout layout = new MyTextLayout(this.DWFactory, "0", this.format, float.MaxValue, float.MaxValue, dpix, false);
            layout.RightToLeft = false;
            this.emSize = new Size(layout.Width, layout.Height);
            layout.Dispose();

            layout = new MyTextLayout(this.DWFactory, "+", this.format, float.MaxValue, float.MaxValue, dpix, false);
            layout.RightToLeft = false;
#if METRO
            this.FoldingWidth = Math.Max(D2DRenderCommon.MiniumeWidth, layout.Width);
#else
            this.FoldingWidth = layout.Width;
#endif
            layout.Dispose();

            this.ChangedRenderResource(this,new ChangedRenderRsourceEventArgs(ResourceType.Font));
        }

        public bool RightToLeft
        {
            get
            {
                return this.format.ReadingDirection == DW.ReadingDirection.RightToLeft;
            }
            set
            {
                this.format.ReadingDirection = value ? DW.ReadingDirection.RightToLeft : DW.ReadingDirection.LeftToRight;
                this.ChangedRightToLeft(this, null);                
            }
        }

        public TextAntialiasMode TextAntialiasMode
        {
            get
            {
                return this._TextAntialiasMode;
            }
            set
            {
                if (this.render == null)
                    throw new InvalidOperationException();
                this._TextAntialiasMode = value;
                this.render.TextAntialiasMode = (D2D.TextAntialiasMode)value;
                this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Antialias));
            }
        }

        public bool ShowFullSpace
        {
            get
            {
                if (this.HiddenChars == null)
                    return false;
                else
                    return this.HiddenChars.ContainsSymbol('　');
            }
            set
            {
                if (this.HiddenChars == null)
                    throw new InvalidOperationException();
                if (value)
                    this.HiddenChars.AddSymbol('　', '□');
                else
                    this.HiddenChars.RemoveSymbol('　');
                this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.InlineChar));
            }
        }

        public bool ShowHalfSpace
        {
            get
            {
                if (this.HiddenChars == null)
                    return false;
                else
                    return this.HiddenChars.ContainsSymbol(' ');
            }
            set
            {
                if (this.HiddenChars == null)
                    throw new InvalidOperationException();
                if (value)
                    this.HiddenChars.AddSymbol(' ', 'ﾛ');
                else
                    this.HiddenChars.RemoveSymbol(' ');
                this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.InlineChar));
            }
        }

        public bool ShowTab
        {
            get
            {
                if (this.HiddenChars == null)
                    return false;
                else
                    return this.HiddenChars.ContainsSymbol('\t');
            }
            set
            {
                if (this.HiddenChars == null)
                    throw new InvalidOperationException();
                if (value)
                    this.HiddenChars.AddSymbol('\t', '>');
                else
                    this.HiddenChars.RemoveSymbol('\t');
                this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.InlineChar));
            }
        }

        public bool ShowLineBreak
        {
            get
            {
                return this._ShowLineBreak;
            }
            set
            {
                this._ShowLineBreak = value;
                this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.InlineChar));
            }
        }

        public Color4 Foreground
        {
            get
            {
                return this._Forground;
            }
            set
            {
                if (this.render == null)
                    return;
                this._Forground = value;
                if (this.textRender != null)
                    this.textRender.DefaultFore = value;
                this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
            }
        }

        public Color4 Background
        {
            get;
            set;
        }

        public Color4 InsertCaret
        {
            get;
            set;
        }

        public Color4 OverwriteCaret
        {
            get;
            set;
        }

        public Color4 LineMarker
        {
            get;
            set;
        }

        public Color4 UpdateArea
        {
            get;
            set;
        }

        public Color4 ControlChar
        {
            get
            {
                return this._ControlChar;
            }
            set
            {
                this._ControlChar = value;
                if (this.HiddenChars != null)
                    this.HiddenChars.Fore = value;
                this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
            }
        }

        public Color4 Url
        {
            get
            {
                return this._URL;
            }
            set
            {
                this._URL = value;
                this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
            }
        }

        public Color4 Hilight
        {
            get
            {
                return this._Hilight;
            }
            set
            {
                this._Hilight = value;
            }
        }

        public Color4 Comment
        {
            get
            {
                return this._Comment;
            }
            set
            {
                this._Comment = value;
                this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
            }
        }

        public Color4 Literal
        {
            get
            {
                return this._Literal;
            }
            set
            {
                this._Literal = value;
                this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
            }
        }

        public Color4 Keyword1
        {
            get
            {
                return this._Keyword1;
            }
            set
            {
                this._Keyword1 = value;
                this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
            }
        }

        public Color4 Keyword2
        {
            get
            {
                return this._Keyword2;
            }
            set
            {
                this._Keyword2 = value;
                this.ChangedRenderResource(this, new ChangedRenderRsourceEventArgs(ResourceType.Brush));
            }
        }

        public Rectangle TextArea
        {
            get;
            set;
        }

        public double LineNemberWidth
        {
            get
            {
                return this.emSize.Width * (EditView.LineNumberLength + 1);
            }
        }

        public double FoldingWidth
        {
            get;
            private set;
        }

        public int TabWidthChar
        {
            get { return this.tabLength; }
            set
            {
                if (value == 0)
                    return;
                this.tabLength = value;
                DW.TextLayout layout = new DW.TextLayout(this.DWFactory, "0", this.format, float.MaxValue, float.MaxValue);
                float width = (float)(layout.Metrics.Width * value);
                this.HiddenChars.TabWidth = width;
                this.format.IncrementalTabStop = width;
            }
        }

        public Size emSize
        {
            get;
            private set;
        }

        public void DrawGripper(Point p, double radius)
        {
            D2D.Ellipse ellipse = new D2D.Ellipse();
            ellipse.Point = p;
            ellipse.RadiusX = (float)radius;
            ellipse.RadiusY = (float)radius;
            this.render.FillEllipse(ellipse, this.Brushes.Get(this.Background));
            this.render.DrawEllipse(ellipse, this.Brushes.Get(this.Foreground));
        }

        public void ReConstructDeviceResource()
        {
            this.ReConstructDeviceResource(this.renderSize.Width, this.renderSize.Height);
        }

        public void ReConstructDeviceResource(double width, double height)
        {
            this.DestructDeviceResource();
            this.ConstructDeviceResource(width, height);
        }

        public virtual void DrawCachedBitmap(Rectangle rect)
        {
            if (this.render == null || this.render.IsDisposed)
                return;
            render.DrawBitmap(this.bitmap, rect, 1.0f, D2D.BitmapInterpolationMode.Linear, rect);
        }

        public virtual void CacheContent()
        {
            if (this.render == null || this.bitmap == null || this.bitmap.IsDisposed || this.render.IsDisposed)
                return;
            render.Flush();
            this.bitmap.CopyFromRenderTarget(this.render, new SharpDX.Point(), new SharpDX.Rectangle(0, 0, (int)this.renderSize.Width, (int)this.renderSize.Height));
            this.hasCache = true;
        }

        public virtual bool IsVaildCache()
        {
            return this.hasCache;
        }

        public virtual void BegineDraw()
        {
            if (this.render == null || this.render.IsDisposed)
                return;
            this.render.BeginDraw();
            this.render.AntialiasMode = D2D.AntialiasMode.Aliased;
        }

        public virtual void EndDraw()
        {
            if (this.render == null || this.render.IsDisposed)
                return;
            this.render.AntialiasMode = D2D.AntialiasMode.PerPrimitive;
            this.render.EndDraw();
        }

        public void DrawString(string str, double x, double y, StringAlignment align, Size layoutRect)
        {
            if (this.render == null || this.render.IsDisposed)
                return;
            float dpix, dpiy;
            this.GetDpi(out dpix,out dpiy);
            MyTextLayout layout = new MyTextLayout(this.DWFactory, str, this.format, (float)layoutRect.Width, (float)layoutRect.Height,dpix,false);
            layout.StringAlignment = align;
            D2D.SolidColorBrush brush = this.Brushes.Get(this.Foreground);
            layout.Draw(this.render, (float)x, (float)y, brush);
            layout.Dispose();
        }

        public void DrawFoldingMark(bool expand, double x, double y)
        {
            string mark = expand ? "-" : "+";
            this.DrawString(mark, x, y,StringAlignment.Left, new Size(this.FoldingWidth, this.emSize.Height));
        }

        public void FillRectangle(Rectangle rect,FillRectType type)
        {
            if (this.render == null || this.render.IsDisposed)
                return;
            D2D.SolidColorBrush brush = null;
            switch(type)
            {
                case FillRectType.OverwriteCaret:
                    brush = this.Brushes.Get(this.OverwriteCaret);
                    this.render.FillRectangle(rect, brush);
                    break;
                case FillRectType.InsertCaret:
                    brush = this.Brushes.Get(this.InsertCaret);
                    this.render.FillRectangle(rect, brush);
                    break;
                case FillRectType.InsertPoint:
                    brush = this.Brushes.Get(this.Hilight);
                    this.render.FillRectangle(rect, brush);
                    break;
                case FillRectType.LineMarker:
                    brush = this.Brushes.Get(this.LineMarker);
                    this.render.DrawRectangle(rect, brush, EditView.LineMarkerThickness);
                    break;
                case FillRectType.UpdateArea:
                    brush = this.Brushes.Get(this.UpdateArea);
                    this.render.FillRectangle(rect, brush);
                    break;
            }
        }

        public void FillBackground(Rectangle rect)
        {
            if (this.render == null || this.render.IsDisposed)
                return;
            this.render.Clear(this.Background);
        }

        public void DrawOneLine(LineToIndexTable lti, int row, double x, double y, IEnumerable<Selection> SelectRanges,PreDrawOneLineHandler PreDrawOneLine)
        {
            int lineLength = lti.GetLengthFromLineNumber(row);

            if (lineLength == 0 || this.render == null || this.render.IsDisposed)
                return;

            MyTextLayout layout = (MyTextLayout)lti.GetLayout(row);

            this.render.PushAxisAlignedClip(this.TextArea, D2D.AntialiasMode.Aliased);

            if(PreDrawOneLine != null)
                PreDrawOneLine(layout);

            if (SelectRanges != null)
            {
                foreach (Selection sel in SelectRanges)
                {
                    if (sel.length == 0 || sel.start == -1)
                        continue;

                    this.DrawMarkerEffect(layout, HilightType.Select, sel.start, sel.length, x, y, false);
                }
            }

            layout.Draw(textRender, (float)x, (float)y);

            this.render.PopAxisAlignedClip();
        }

        public void SetTextColor(MyTextLayout layout,int start, int length, Color4? color)
        {
            if (color == null)
                return;
            layout.SetDrawingEffect(this.Brushes.Get((Color4)color), new DW.TextRange(start, length));
        }

        public void DrawLine(Point from, Point to)
        {
            D2D.Brush brush = this.Brushes.Get(this.Foreground);
            D2D.StrokeStyle stroke = this.Strokes.Get(HilightType.Sold);
            this.render.DrawLine(from, to, brush, 1.0f, stroke);
        }

        public void DrawMarkerEffect(MyTextLayout layout, HilightType type, int start, int length, double x, double y, bool isBold, Color4? effectColor = null)
        {
            if (type == HilightType.None)
                return;

            float thickness = isBold ? 2 : 1;

            Color4 color;
            if (effectColor != null)
                color = (Color4)effectColor;
            else if (type == HilightType.Select)
                color = this.Hilight;
            else
                color = this.Foreground;

            IMarkerEffecter effecter = null;
            D2D.SolidColorBrush brush = this.Brushes.Get(color);

            if (type == HilightType.Squiggle)
                effecter = new D2DSquilleLineMarker(this.render, brush, this.Strokes.Get(HilightType.Squiggle), thickness);
            else if (type == HilightType.Select)
                effecter = new HilightMarker(this.render, brush);
            else if (type == HilightType.None)
                effecter = null;
            else
                effecter = new LineMarker(this.render, brush, this.Strokes.Get(type), thickness);

            if (effecter != null)
            {
                bool isUnderline = type != HilightType.Select;

                DW.HitTestMetrics[] metrics = layout.HitTestTextRange(start, length, (float)x, (float)y);
                foreach (DW.HitTestMetrics metric in metrics)
                {
                    float offset = isUnderline ? metric.Height : 0;
                    effecter.Draw(metric.Left, metric.Top + offset, metric.Width, metric.Height);
                }
            }
        }

        public ITextLayout CreateLaytout(string str, SyntaxInfo[] syntaxCollection, IEnumerable<Marker> MarkerRanges)
        {
            float dpix,dpiy;
            this.GetDpi(out dpix,out dpiy);

            bool hasNewLine = str.Length > 0 && str[str.Length - 1] == Document.NewLine;
            MyTextLayout newLayout = new MyTextLayout(this.DWFactory,
                str,
                this.format,
                this.TextArea.Width,
                this.TextArea.Height,
                dpiy,
                hasNewLine && this._ShowLineBreak);
            ParseLayout(newLayout, str);
            if (syntaxCollection != null)
            {
                foreach (SyntaxInfo s in syntaxCollection)
                {
                    D2D.SolidColorBrush brush = this.Brushes.Get(this.Foreground);
                    switch (s.type)
                    {
                        case TokenType.Comment:
                            brush = this.Brushes.Get(this.Comment);
                            break;
                        case TokenType.Keyword1:
                            brush = this.Brushes.Get(this.Keyword1);
                            break;
                        case TokenType.Keyword2:
                            brush = this.Brushes.Get(this.Keyword2);
                            break;
                        case TokenType.Literal:
                            brush = this.Brushes.Get(this.Literal);
                            break;
                    }
                    newLayout.SetDrawingEffect(brush, new DW.TextRange(s.index, s.length));
                }
            }

            if (MarkerRanges != null)
            {
                foreach (Marker m in MarkerRanges)
                {
                    if (m.start == -1 || m.length == 0)
                        continue;
                    Color4 color = new Color4(m.color.R / 255.0f, m.color.G / 255.0f, m.color.B / 255.0f, m.color.A / 255.0f);
                    if (m.hilight == HilightType.Url)
                        color = this.Url;
                    newLayout.SetDrawingEffect(this.Effects.Get(color, m.hilight), new DW.TextRange(m.start, m.length));
                }
            }

            return newLayout;
       }

        public List<LineToIndexTableData> BreakLine(Document doc, LineToIndexTable layoutLineCollection, int startIndex, int endIndex, double wrapwidth)
        {
            List<LineToIndexTableData> output = new List<LineToIndexTableData>();

            this.format.WordWrapping = DW.WordWrapping.Wrap;

            foreach (string str in doc.GetLines(startIndex, endIndex))
            {
                DW.TextLayout layout = new DW.TextLayout(this.DWFactory, str, this.format, (float)wrapwidth, float.MaxValue);

                int i = startIndex;
                foreach (DW.LineMetrics metrics in layout.GetLineMetrics())
                {
                    if (metrics.Length == 0 && metrics.NewlineLength == 0)
                        continue;

                    bool lineend = false;
                    if (metrics.NewlineLength > 0)
                        lineend = true;

                    output.Add(layoutLineCollection.CreateLineToIndexTableData(i, (int)metrics.Length, lineend, null));
                    i += Util.RoundUp(metrics.Length);
                }

                layout.Dispose();

                startIndex += str.Length;
            }

            this.format.WordWrapping = DW.WordWrapping.NoWrap;

            if (output.Count > 0)
                output.Last().LineEnd = true;

            return output;
        }

        public virtual void Dispose()
        {
            this.DestructDeviceResource();
            this.HiddenChars.Clear();
            if (this.format != null)
                this.format.Dispose();
            if(this.DWFactory != null)
                this.DWFactory.Dispose();
            if(this.D2DFactory != null)
                this.D2DFactory.Dispose();
        }

        public void ConstructDeviceResource(double width, double height)
        {
            float dpiX, dpiY;
            this.GetDpi(out dpiX, out dpiY);
            D2D.RenderTargetProperties prop = new D2D.RenderTargetProperties(
                D2D.RenderTargetType.Default,
                new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Ignore),
                dpiX,
                dpiY,
                D2D.RenderTargetUsage.None,
                D2D.FeatureLevel.Level_DEFAULT);

            this.render = this.ConstructRender(this.D2DFactory,prop,width,height);

            this.Brushes.Render = this.render;

            D2D.BitmapProperties bmpProp = new D2D.BitmapProperties();
            bmpProp.DpiX = dpiX;
            bmpProp.DpiY = dpiY;
            bmpProp.PixelFormat = new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Ignore);
            this.bitmap = new D2D.Bitmap(this.render, new SharpDX.Size2((int)width, (int)height), bmpProp);
            this.hasCache = false;

            this.ConstrctedResource();

            this.textRender = new CustomTextRenderer(this.render, this.Brushes,this.Strokes, this.Foreground);

            this.renderSize.Width = width;
            this.renderSize.Height = height;

            this.TextAntialiasMode = this._TextAntialiasMode;
        }

#if METRO
        protected virtual D2D.RenderTarget ConstructRender(D2D.Factory1 factory, D2D.RenderTargetProperties prop, double width, double height)
#else
        protected virtual D2D.RenderTarget ConstructRender(D2D.Factory factory, D2D.RenderTargetProperties prop, double width, double height)
#endif
        {
            throw new NotImplementedException();
        }

        protected virtual void ConstrctedResource()
        {
            throw new NotImplementedException();
        }

        public virtual void GetDpi(out float dpix,out float dpiy)
        {
            throw new NotImplementedException();
        }

        protected virtual void DestructRender()
        {
            throw new NotImplementedException();

        }

        protected virtual void ReCreateTarget()
        {
            throw new NotImplementedException();
        }

        public void DestructDeviceResource()
        {
            this.hasCache = false;
            if (this.bitmap != null)
                this.bitmap.Dispose();
            this.Brushes.Clear();
            this.Strokes.Clear();
            this.Effects.Clear();
            if (this.textRender != null)
                this.textRender.Dispose();
            this.DestructRender();
        }

        void ParseLayout(MyTextLayout layout, string str)
        {
            for (int i = 0; i < str.Length; i++)
            {
                DW.InlineObject inlineObject = this.HiddenChars.Get(layout,i, str);
                if (inlineObject != null)
                {
                    layout.SetInlineObject(inlineObject, new DW.TextRange(i, 1));
                    layout.SetDrawingEffect(this.Brushes.Get(this.ControlChar), new DW.TextRange(i, 1));
                }
            }
            layout.SetLineBreakBrush(this.Brushes.Get(this.ControlChar));
        }
    }
}
