/*
 * 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 SharpDX;
using D2D = SharpDX.Direct2D1;
using DW = SharpDX.DirectWrite;

namespace FooEditEngine
{
    sealed class MyTextLayout : ITextLayout
    {
        double _height = double.NaN;
        double _width = double.NaN;

        internal MyTextLayout(DW.TextLayout layout)
        {
            this.layout = layout;
            this.Disposed = false;
        }

        DW.TextLayout layout;

        public bool Disposed
        {
            get;
            private set;
        }

        public bool Invaild
        {
            get;
            set;
        }

        public float MaxWidth
        {
            get
            {
                return this.layout.MaxWidth;
            }
        }

        public float MaxHeight
        {
            get
            {
                return this.layout.MaxHeight;
            }
        }

        public bool RightToLeft
        {
            get
            {
                return this.layout.ReadingDirection == DW.ReadingDirection.RightToLeft;
            }
        }
        
        public int GetIndexFromX(double x)
        {
            SharpDX.Bool isTrailing, isInsed;
            DW.HitTestMetrics metrics;
            metrics = this.layout.HitTestPoint((float)x, 0, out isTrailing, out isInsed);
            if(isTrailing)
                return Util.RoundUp(metrics.TextPosition + metrics.Length);
            else
                return Util.RoundUp(metrics.TextPosition);
        }

        public double GetWidthFromIndex(int index)
        {
            float x, y;
            DW.HitTestMetrics metrics;
            metrics = this.layout.HitTestTextPosition(index, false, out x, out y);
            float x2;
            layout.HitTestTextPosition(index, true, out x2, out y);

            return x2 - x;
        }

        public double GetXFromIndex(int index)
        {
            float x, y;
            DW.HitTestMetrics metrics;
            metrics = this.layout.HitTestTextPosition(index, false, out x, out y);
            return x;
        }

        public int AlignIndexToNearestCluster(int index, AlignDirection flow)
        {
            float x, y;
            DW.HitTestMetrics metrics;
            metrics = this.layout.HitTestTextPosition(index, false, out x, out y);

            if (flow == AlignDirection.Forward)
                return Util.RoundUp(metrics.TextPosition + metrics.Length);
            else if (flow == AlignDirection.Back)
                return Util.RoundUp(metrics.TextPosition);
            throw new ArgumentOutOfRangeException();
        }

        public double Width
        {
            get
            {
                if(double.IsNaN(this._width))
                    this._width = Util.RoundUp(this.layout.Metrics.WidthIncludingTrailingWhitespace);
                return this._width;
            }
        }

        public double Height
        {
            get
            {
                if (!double.IsNaN(this._height))
                    return this._height;
                double height = 0;
                DW.LineMetrics[] metrics = this.layout.GetLineMetrics();
                if (metrics != null && metrics.Length > 0)
                    height = metrics[0].Height;
                this._height = height;
                return height;
            }
        }

        public void Draw(DW.TextRenderer render, float x, float y)
        {
            this.layout.Draw(render, x, y);
        }

        public void Dispose()
        {
            this.layout.Dispose();
            this.Disposed = true;
        }

        public void SetDrawingEffect(ComObject resource, DW.TextRange range)
        {
            this.layout.SetDrawingEffect(resource, range);
        }

        public void SetUnderline(bool underline, DW.TextRange range)
        {
            this.layout.SetUnderline(underline, range);
        }

        public DW.HitTestMetrics[] HitTestTextRange(int start, int length, float x, float y)
        {
            return this.layout.HitTestTextRange(start, length, x, y);
        }

        public void SetInlineObject(DW.InlineObject o, DW.TextRange range)
        {
            this.layout.SetInlineObject(o, range);
        }
    }
}
