using System;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using HandyControl.Controls;

namespace HandyControl.Tools
{
    public class GithubGravatarGenerator : IGravatarGenerator
    {
        private const int RenderDataMaxLength = 15;

        public object GetGravatar(string id)
        {
            var hashcode = GetHashCode(id);
            var renderData = GetRenderData(hashcode);
            var renderBrush = GetRenderBrush(hashcode);
            var geometryGroup = new GeometryGroup();

            void AddRec(int i, int j, bool hidden = false)
            {
                var rec = new RectangleGeometry(new Rect(new Point(i, j), hidden ? new Size() : new Size(1, 1)));
                geometryGroup.Children.Add(rec);
            }

            var index = 0;
            for (var i = 0; i < 2; i++)
            {
                for (var j = 0; j < 5; j++, index++)
                {
                    AddRec(i, j, renderData[index] == 0);
                }
            }

            for (var j = 0; j < 5; j++, index++)
            {
                AddRec(2, j, renderData[index] == 0);
            }

            index -= 10;
            for (var i = 3; i < 5; i++)
            {
                for (var j = 0; j < 5; j++, index++)
                {
                    AddRec(i, j, renderData[index] == 0);
                }
                index -= 10;
            }

            var path = new Path
            {
                Data = geometryGroup,
                Fill = renderBrush,
                VerticalAlignment = VerticalAlignment.Top
            };

            RenderOptions.SetEdgeMode(path, EdgeMode.Aliased);

            return path;
        }

        private string GetHashCode(string id)
        {
            var md5 = MD5.Create();
            var bytes = Encoding.ASCII.GetBytes(id);
            var hash = md5.ComputeHash(bytes);
            var stringBuilder = new StringBuilder();
            foreach (var item in hash)
            {
                stringBuilder.Append(item.ToString("X2"));
            }
            return stringBuilder.ToString();
        }

        private int[] GetRenderData(string hashcode)
        {
            var arr = new int[RenderDataMaxLength];
            for (var i = 0; i < RenderDataMaxLength; i++)
            {
                var c = hashcode[i];
                var v = (int) c;
                arr[i] = v % 2;
            }

            return arr;
        }

        private Brush GetRenderBrush(string hashcode)
        {
            var v = (double)int.Parse(hashcode.Substring(hashcode.Length - 7), NumberStyles.HexNumber);
            var scale = v / 0xfffffff;
            var color = Hsl2Rgb(scale);
            return new SolidColorBrush(color);
        }

        //adapted from https://github.com/stewartlord/identicon.js/blob/4fad7cafec1b7a4d896015e084e861625ef5d64f/identicon.js#L110
        private Color Hsl2Rgb(double h, double s = 0.7, double b = 0.5)
        {
            h *= 6;
            var arr = new[]
            {
                b += s *= b < .5 ? b : 1 - b,
                b - h % 1 * s * 2,
                b -= s *= 2,
                b,
                b + h % 1 * s,
                b + s
            };

            var hValue = (int) Math.Floor(h);

            return Color.FromRgb((byte) (arr[hValue % 6] * 255), (byte) (arr[(hValue | 16) % 6] * 255),
                (byte) (arr[(hValue | 8) % 6] * 255));
        }
    }
}