using System;
using System.Collections.Generic;
using SkiaSharp;
using TheGameExtreme.model.card.cardType;
using TheGameExtreme.viewmodel;
using TouchTracking;
using Xamarin.Essentials;

namespace TheGameExtreme.view
{
    /**
     * Classe permettant de gérer un objet dessiné dans un canvas
     */
    public class TouchManipulationCard
    {
        public SKPoint InitialPoint { get; set; }
        SKPaint textPaint;
        SKPaint textPaint1;
        SKPaint textPaint2;
        Dictionary<long, TouchManipulationInfo> touchDictionary = new Dictionary<long, TouchManipulationInfo>();
        public CardVM Value;
        public string display;
        private float width;
        private float height;
        public SKColor Color;


        /**
         * <param name="textPaint">Objet dessiné</param>
         * <param name="value">Carte qui est dessiné par le textPaint</param>
         */
        public TouchManipulationCard(SKPaint textPaint, CardVM value, float width)
        {
            this.textPaint = textPaint;
            Value = value;
            this.width = width;
            height = (0.2f * (float)DeviceDisplay.MainDisplayInfo.Height - textPaint.TextSize) * 0.5f;

            display = Value.ToString();

            if (Value.View.GetType() == typeof(FractionCard))
            {
                textPaint1 = new SKPaint();
                textPaint2 = new SKPaint();
                textPaint1.TextSize = textPaint.TextSize;
                textPaint2.TextSize = textPaint.TextSize;
            }
            else
            {
                if (!display.Contains(",") && !display.Contains(".") && !display.Contains("/"))
                {
                    if (Value.Value.CompareTo(-10m) <= 0)
                    {
                        this.width -= textPaint.MeasureText("0") * 0.25f;
                    }
                    if (Value.Value.CompareTo(0m) >= 0 && Value.Value.CompareTo(10) < 0)
                    {
                        this.width += textPaint.MeasureText("0") * 0.5f;
                    }
                }
            }

            Matrix = SKMatrix.MakeIdentity();

            Mode = TouchManipulationMode.PanOnly;
        }

        public TouchManipulationMode Mode { set; get; }

        public SKMatrix Matrix { set; get; }

        public SKMatrix InitialMatrix { set; get; }

        /**
         * Fonction permettant de dessiner l'objet que contient l'instance dans un canvas donné avec une couleur donnée
         * <param name="canvas">Canvas où l'on souhaite dessiner l'objet contenu dans l'instance</param>
         * <param name="color">Couleur qui sera prise par l'objet</param>
         */
        public void Paint(SKCanvas canvas)
        {
            canvas.Save();

            SKMatrix matrix = Matrix;
            canvas.Concat(ref matrix);

            textPaint.Color = Color;
            textPaint.StrokeWidth = 5;
            textPaint.Style = SKPaintStyle.StrokeAndFill;

            SKRect card;

            if (Value.View.GetType() == typeof(FractionCard))
            {
                card = new SKRect(-width, -height - textPaint.TextSize, width + textPaint.MeasureText(Math.Pow(10, ((FractionCard)(Value.View)).Fraction.SizeMax-1).ToString()), height);
                textPaint1.Color = Color;
                textPaint1.StrokeWidth = 5;
                textPaint1.Style = SKPaintStyle.StrokeAndFill;
                textPaint2.Color = Color;
                textPaint2.StrokeWidth = 5;
                textPaint2.Style = SKPaintStyle.StrokeAndFill;
            }
            else
            {
                card = new SKRect(-width, -height - textPaint.TextSize, width + textPaint.MeasureText(display), height);
            }

            if (Value.View.GetType() == typeof(FractionCard))
            {
                int sizeMax = ((FractionCard)(Value.View)).Fraction.SizeMax;
                float xNum = textPaint.MeasureText(Math.Pow(10, sizeMax-2).ToString());
                float xDen = textPaint.MeasureText(Math.Pow(10, sizeMax-2).ToString());
                int tailleNum = Math.Abs(((FractionCard)(Value.View)).Fraction.Numerateur);
                int tailleDen = Math.Abs(((FractionCard)(Value.View)).Fraction.Denominateur);

                if (tailleNum == 0)
                {
                    tailleNum += 1;
                }
                if (tailleDen == 0)
                {
                    tailleDen += 1;
                }

                string fractionBarre = "___";
                for (int i = 2; i < sizeMax; i++){
                    fractionBarre += "_";
                }
                while (tailleNum > 9)
                {
                    xNum -= textPaint.MeasureText("0");
                    tailleNum /= 10;
                }
                while (tailleDen > 9)
                {
                    xDen -= textPaint2.MeasureText("0");
                    tailleDen /= 10;
                }
                if (((FractionCard)(Value.View)).Fraction.Numerateur < 0)
                {
                    xNum -= textPaint.MeasureText("-");
                }
                if (((FractionCard)(Value.View)).Fraction.Denominateur < 0)
                {
                    xDen -= textPaint2.MeasureText("-");
                }

                SKPaint paint = textPaint1;
                paint.Style = SKPaintStyle.Stroke;
                canvas.DrawRect(card, paint);
                canvas.DrawText(((FractionCard)Value.View).Fraction.Numerateur.ToString(), xNum, -height * 0.5f, textPaint);
                canvas.DrawText(fractionBarre, -textPaint1.MeasureText("_") * 0.5f, -textPaint1.TextSize * 0.5f, textPaint1);
                canvas.DrawText(((FractionCard)Value.View).Fraction.Denominateur.ToString(), xDen, height * 0.5f, textPaint2);
            }
            else
            {
                SKPaint paint = textPaint;
                paint.Style = SKPaintStyle.Stroke;
                canvas.DrawRect(card, paint);
                canvas.DrawText(display, 0, 0, textPaint);
            }

            canvas.Restore();

        }

        /**
         * Fonction permettant de changer la couleur de l'objet contenu dans l'instance
         * <param name="color">Nouvelle couleur que l'on souhaite attribué à l'objet contenu dans l'instance</param>
         */
        public void ChangeColor(SKColor color)
        {
            textPaint.Color = color;
        }

        /**
         * Fonction permettant de savoir si le point qu'on lui passe est dans la zone où l'objet est dessiné
         * <param name="location">Point que l'on souhaite tester</param>
         * <returns>Booléen qui indique si le point donné est dans la zone où l'objet en instance est dessiné</returns>
         */
        public bool HitTest(SKPoint location)
        {
            // Invert the matrix
            SKMatrix inverseMatrix;

            if (Matrix.TryInvert(out inverseMatrix))
            {
                // Transform the point using the inverted matrix
                SKPoint transformedPoint = inverseMatrix.MapPoint(location);

                // Check if it's in the untransformed bitmap rectangle
                SKRect rect;
                if (Value.View.GetType() == typeof(FractionCard))
                {
                    rect = new SKRect(-width, -height - textPaint.TextSize, width + textPaint.MeasureText(Math.Pow(10, ((FractionCard)(Value.View)).Fraction.SizeMax - 1).ToString()), height);
                }
                else
                {
                    rect = new SKRect(-width, -height - textPaint.TextSize, width + textPaint.MeasureText(display), height);
                }
                return rect.Contains(transformedPoint);
            }
            return false;
        }

        /**
         * Evénement qui permet de gérer le Drag and Drop.
         * Il permet de traiter le clic, le mouvement, le relachement du clic et la fin de traitement d'un objet.
         * <param name="id">Index de la position que l'on souhaite traiter</param>
         * <param name="type">Type d'évenement lancé</param>
         * <param name="location">Nouveau point que l'on souhaite traiter</param>
         */
        public void ProcessTouchEvent(long id, TouchActionType type, SKPoint location)
        {
            switch (type)
            {
                case TouchActionType.Pressed:
                    touchDictionary.Add(id, new TouchManipulationInfo
                    {
                        PreviousPoint = location,
                        NewPoint = location
                    });
                    break;

                case TouchActionType.Moved:
                    TouchManipulationInfo info = touchDictionary[id];
                    info.NewPoint = location;
                    Manipulate();
                    info.PreviousPoint = info.NewPoint;
                    break;

                case TouchActionType.Released:

                case TouchActionType.Cancelled:
                    touchDictionary.Remove(id);
                    break;
            }
        }

        /**
         * Fonction permettant de traiter un déplacement de l'objet en instance
         */
        private void Manipulate()
        {
            TouchManipulationInfo[] infos = new TouchManipulationInfo[touchDictionary.Count];
            touchDictionary.Values.CopyTo(infos, 0);

            if (Mode == TouchManipulationMode.PanOnly)
            {
                Matrix = SKMatrix.MakeTranslation(infos[0].NewPoint.X, infos[0].NewPoint.Y);
            }
        }
    }
}