using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using ConsoleLSystem;
using System;



abstract public class TurtleLSystem : MonoBehaviour
{
    public string LSystemPath;

    public uint steps;

    public Dictionary<string, Func<float[], Tuple<GameObject, Matrix4x4>>> turtleInterpretation = new Dictionary<string, Func<float[], Tuple<GameObject, Matrix4x4>>>();

    private List<GameObject> gameObjects = new List<GameObject>();

    private Mesh getCylinder(int quality,float width,float length) {
        Mesh mesh = new Mesh();
        //mesh.triangles 
        var points = new List<Vector3>();
        var normals = new List<Vector3>();
        var indices = new List<int>();
        for (float i = 0; i < quality; i++) {
            points.Add(new Vector3(Mathf.Cos((i / quality) * 2 * Mathf.PI) * width, 0, Mathf.Sin((i / quality) * 2 * Mathf.PI) * width));
        }
        for (float i = 0; i < quality; i++) {
            points.Add(new Vector3(Mathf.Cos((i / quality) * 2 * Mathf.PI) * width, length, Mathf.Sin((i / quality) * 2 * Mathf.PI) * width));
        }
        //points.Add(new Vector3(0,0,0));
        //points.Add(new Vector3(0, length, 0));

        for (float i = 0; i < quality; i++) {
            normals.Add(new Vector3(Mathf.Cos((i / quality) * 2 * Mathf.PI) * width, 0, Mathf.Sin((i / quality) * 2 * Mathf.PI) * width));
        }
        for (float i = 0; i < quality; i++) {
            normals.Add(new Vector3(Mathf.Cos((i / quality) * 2 * Mathf.PI) * width, 0, Mathf.Sin((i / quality) * 2 * Mathf.PI) * width));
        }

        //for (int i = 0; i < quality; i++) {
        //    indices.Add(i);
        //    indices.Add((i + 1) % quality);
        //    indices.Add(quality * 2);
        //}
        //for (int i = 0; i < quality; i++) {
        //    indices.Add((i + 1) % quality + quality);
        //    indices.Add(i + quality);
        //    indices.Add(quality * 2 + 1);
        //}
        for (int i = 0; i < quality; i++) {
            indices.Add((i + 1) % quality);
            indices.Add(i);
            indices.Add(i + quality);

            indices.Add(i + quality);
            indices.Add((i + 1) % quality + quality);
            indices.Add((i + 1) % quality);
        }
        mesh.vertices = points.ToArray();
        mesh.triangles = indices.ToArray();
        mesh.normals = normals.ToArray();
        return mesh;
    }

    private LSystemEvaluator evaluator = null;

    private void parseRules(StreamReader sr) {

    }
    public void evaluate() {
        for (int i=0; i < steps; i++) {
            evaluator.rewrite();
        }
        Debug.Log(evaluator.lSystemString.ToString());
    }
    private GameObject prepeareGameObject(string name, GameObject gameObject,Matrix4x4 transformation) {
        var instance = Instantiate(gameObject);
        instance.name = String.Format("LSystem Literal {0}", name);
        instance.tag = "LSystemLiteral";
        instance.transform.parent = this.gameObject.transform;
        instance.transform.position = transformation.ExtractPosition() + transformation.MultiplyPoint(instance.transform.position);
        instance.transform.rotation *= transformation.ExtractRotation();
        instance.transform.localScale = Vector3.Scale(transformation.ExtractScale(), instance.transform.localScale);
        return instance;

    }
    void createModelsRecursive(LSystemNode node, Matrix4x4 transformation) {
        while (node != null) {
            Matrix4x4 new_transformation=transformation;
            Func<float[], Tuple<GameObject, Matrix4x4>> interpretation;
            var name = node.literal.name;
            if (turtleInterpretation.TryGetValue(name, out interpretation) || turtleInterpretation.TryGetValue("*.*", out interpretation)) {
                var result = interpretation(node.literal.values);
                new_transformation = new_transformation * result.Item2;
                if (result.Item1 != null) {
                    var instance = prepeareGameObject(name, result.Item1, new_transformation);
                }
                //gameObjects.Add(gameObject);
                //remove scale, rather unnecesary
                new_transformation = new_transformation * Matrix4x4.Scale(result.Item2.ExtractScale()).inverse;
            }
            foreach (var child in node.children) {
                createModelsRecursive(child, new_transformation);
            }
            node = node.mainChild;
            transformation = new_transformation;
        }
    }
    private void clearObjects() {
        //var objects = Resources.FindObjectsOfTypeAll<GameObject>().Where(obj => obj.name == "Name");
        foreach (GameObject gameObject in GameObject.FindGameObjectsWithTag("LSystemLiteral")) {
            DestroyImmediate(gameObject);

        }
        gameObjects = new List<GameObject>();
    }

    public void present() {
        clearObjects();
        createModelsRecursive(evaluator.lSystemString, Matrix4x4.identity);
    }
    abstract protected void initLiteralInterpretation();
    public void loadFile() {
        clearObjects();
        var sr = new StreamReader(LSystemPath);
        evaluator = LSystemFileParser.parseLSystem(sr);
        sr.Close();
        turtleInterpretation = new Dictionary<string, Func<float[], Tuple<GameObject, Matrix4x4>>>();
        initLiteralInterpretation();
    }
    public void evaluateAndPresent() {
        evaluate();
        present();
        Debug.Log(evaluator.lSystemString.ToString().Length);
        //x.name = "aaa";
       
        //Instantiate(x,Matrix4x4.identity);
    }

    
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}