using System;
using System.Collections.Generic;
using System.Linq;
using GGJ.Models;
using UnityEngine;
using Vectrosity;

namespace GGJ.LightRenderer
{
    public class RendererGroup
    {
        public VectorLine spikeLine;
        public GameObject curveMesh;
    }

    public class VectorLight : MonoBehaviour, ILightRenderer
    {
        public int segments;
        private List<RendererGroup> renderers = new List<RendererGroup>();
        private List<RendererGroup> inuse = new List<RendererGroup>();
        public Material material;
        private float distance = 0f;

        public void Draw(LightSegment segment)
        {
            if (segment.moveType == LightMoveType.Photon)
            {
                DrawSegment(segment);
            }
            else
            {
                if (Utils.IsColorSimilar(segment.color, Color.white, 0.1f))
                {
                    DrawSinSegment(segment, new Color32(88, 181, 115, 255), 0);
                    DrawSinSegment(segment, new Color32(105, 202, 254, 255), 1);
                    DrawSinSegment(segment, new Color32(231, 77, 72, 255), 2);
                }
                else
                {
                    DrawSinSegment(segment, segment.color, 0);
                }
            }
        }

        public void DrawSinSegment(LightSegment segment, Color color, float timeDelta)
        {
            var lineRenderer = Next();
            var l = lineRenderer.spikeLine;
            l.SetColor(color);
            l.SetWidth(segment.startsWith);
            var spline = new List<Vector3>();

            var length = this.distance - segment.dist;
            if (length <= 0)
            {
                lineRenderer.spikeLine.active = false;
                return;
            }
            if (segment.completionSegment)
            {
                GameManager.instance.logic.ComplationSegment();
            }

            var dist = (segment.end - segment.start).magnitude;
            var dir = (segment.end - segment.start).normalized;
            var crt = segment.start;

            if (length < dist)
            {
                dist = length;
            }
            dist *= 10.0f;
            for (int i = 0; i < dist; i++)
            {
                crt += dir / 10f;
                var m = 0.1f * Mathf.Sin(i * 10 + Time.time * 10 + timeDelta);
                var p = new Vector3(-dir.y, dir.x);
                crt += p * m;
                spline.Add(crt);
            }

            if (spline.Count == 0)
            {
                spline.Add(segment.start);
            }
            if (spline.Count < 2)
            {
                if (length > dist)
                    spline.Add(segment.end);
                else
                    spline.Add(segment.start + dir * length);
            }

            l.MakeSpline(spline.ToArray(), segments, false);
            l.Draw3D();
        }

        public void DrawSegment(LightSegment segment)
        {
            var lineRenderer = Next();
            var l = lineRenderer.spikeLine;
            l.SetColor(segment.color);
            l.SetWidth(segment.startsWith);

            var length = this.distance - segment.dist;
            if (length <= 0)
            {
                lineRenderer.spikeLine.active = false;
                return;
            }
            if (segment.completionSegment)
            {
                GameManager.instance.logic.ComplationSegment();
            }

            var segmentLength = (segment.end - segment.start).magnitude;
            var spline = new List<Vector3> {segment.start};
            if (length > segmentLength)
            {
                spline.Add(segment.end);
            }
            else
            {
                var dir = (segment.end - segment.start).normalized;
                var end = segment.start + dir * length;
                spline.Add(end);
            }

            l.MakeSpline(spline.ToArray(), segments, false);
            l.Draw3D();
        }

        public void Clear()
        {
            foreach (var r in renderers)
            {
                r.spikeLine.active = false;
            }
            foreach (var lr in inuse)
            {
                renderers.Add(lr);
                lr.spikeLine.active = false;
            }
            inuse.Clear();
        }

        public void SetDistanceTravelled(float dist)
        {
            distance = dist;
        }

        private RendererGroup Next()
        {
            if (renderers.Count == 0)
            {
                var rr = NewVectorRenderer();
                inuse.Add(rr);
                return rr;
            }
            var r = renderers[0];
            renderers.RemoveAt(0);
            inuse.Add(r);
            r.spikeLine.active = true;
            return r;
        }

        private RendererGroup NewVectorRenderer()
        {
            var c = new List<Vector3>(segments + 1);
            var rn = "lightrenderer" + RandomString(4);
            var spikeLine = new VectorLine(rn, c, 10f, LineType.Continuous);
            spikeLine.material = this.material;
            spikeLine.Draw3D();
            var curveMesh = GameObject.Find(rn);
            curveMesh.transform.position = this.transform.position;
            return new RendererGroup() {spikeLine = spikeLine, curveMesh = curveMesh};
        }

        private static System.Random random = new System.Random();

        public static string RandomString(int length)
        {
            const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            return new string(Enumerable.Repeat(chars, length)
                .Select(s => s[random.Next(s.Length)])
                .ToArray());
        }
    }
}