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

abstract public class TurtleLSystemEnvironment : TurtleLSystem
{
    public Environment environment;
    //    private LSystemEvaluator evaluator = null;
    //    private float nextStep;
    public sbyte shadowStrength;
    public int lookForLightAngle = 35;
    public float lookForLightLength = 2.0f;

    public Matrix4x4 lightDirection(Matrix4x4 transformation, Matrix4x4 resultTransformation, GameObject shape, LSystemNode node)
    {
        int minShadow = 10000;
        Matrix4x4 bestTransformation = transformation;
        int maxShadowTries = 20;
        int shadowRotate = 360 / 20;
        int shadowStrength_ = 0;
        bool clearShape = false;
        if (shape == null)
        {
            clearShape = true;
            shape = new GameObject("ShadowConeShape");
        }
        GameObject instance;
        Matrix4x4 new_transformation = transformation;
        foreach (GameObject gameObject in GameObject.FindGameObjectsWithTag("ShadowCone"))
        {
            DestroyImmediate(gameObject);
        }
        Matrix4x4 objectTransformation = (transformation * resultTransformation);
        int nr_shadow_str = 1;

        if (environment.sendShadowToLSystem)
        {

        }
        bestTransformation = transformation * resultTransformation;
        if (environment.rotateTowardsLight)
        {
            minShadow = shadowStrength_;
            if (shadowStrength_ / nr_shadow_str > 5.0f)
            {
                shadowConeLimiter++;
                //        transformation = Matrix4x4.Translate(gameObject.transform.position)*transformation;
                for (int angle = 0; angle <= lookForLightAngle; angle += 10)
                {
                    for (int i = 0; i < 360; i += shadowRotate)
                    {
                        shadowStrength_ = 0;
                        for (float length = 0.5f; length < lookForLightLength; length += environment.voxelSize * 0.5f)
                        {
                            new_transformation = (transformation * (resultTransformation * Matrix4x4.Rotate(Quaternion.Euler(0, i, 0)) * Matrix4x4.Rotate(Quaternion.Euler(0, 0, angle)) * Matrix4x4.Translate(new Vector3(0, length, 0))));
                            shadowStrength_ += (int)environment.shadowStrength(new_transformation.ExtractPosition() + transformation.MultiplyPoint(shape.transform.position));
                            //                        if(shadowConeLimiter % 10 == 0)
                            //                        {
                            //                        instance = Instantiate(environment.shape);
                            //                        instance.name = "ShadowCone";
                            //                        instance.tag = "ShadowCone";
                            //                        instance.transform.position = new_transformation.ExtractPosition()+transformation.MultiplyPoint(shape.transform.position);
                            //                        instance.transform.rotation *= transformation.ExtractRotation();
                            //                        }
                        }
                        if (shadowStrength_ < minShadow)
                        {

                            minShadow = shadowStrength_;
                            bestTransformation = (transformation * (resultTransformation * Matrix4x4.Rotate(Quaternion.Euler(0, i, 0)) * Matrix4x4.Rotate(Quaternion.Euler(0, 0, angle))));
                        }
                    }
                }
            }
            if (minShadow > 220 * lookForLightLength * 2.0f)
            {
                bestTransformation = Matrix4x4.Translate(new Vector3(0, 0, 0));
            }
        }
        if (clearShape)
        {
            DestroyImmediate(shape);
        }
        return bestTransformation;
    }
    public void evaluate()
    {

        base.evaluate();
    }
    public void evaluateAndPresent()
    {
        evaluate();
        present();
        //        Debug.Log(evaluator.lSystemString.ToString().Length);
        //x.name = "aaa";

        //Instantiate(x,Matrix4x4.identity);
    }
    public void loadFile()
    {
        //        nextStep = Time.time;
        //        clearObjects();
        var sr = new StreamReader(LSystemPath);
        evaluator = LSystemFileParser.parseLSystem(sr);
        sr.Close();
        turtleInterpretation = new Dictionary<string, Func<float[], Tuple<GameObject, Matrix4x4>>>();
        initLiteralInterpretation();
    }
    public void present()
    {
        clearObjects();
        createModelsRecursiveInEnviroment(evaluator.lSystemString, Matrix4x4.Translate(gameObject.transform.position / 2.0f));//Matrix4x4.identity);
    }
    void refreshShadows()
    {
        environment.Reset();
        foreach (GameObject gameObject in GameObject.FindGameObjectsWithTag("LSystemLiteral"))
        {

            //            if(!isParentThisGameObject(gameObject))
            //            {
            environment.addShadow(gameObject.GetComponent<Transform>().position, shadowStrength);
            //            }
            //            else
            //            {
            //                if(isParentThisGameObject(gameObject, 2))
            //                {
            //                    environment.addShadowPoint(gameObject.GetComponent<Transform>().position, 6);
            //                }
            //            }
        }
    }
    bool isParentThisGameObject(GameObject gameObject, int depth = 3)
    {
        if (gameObject.transform.parent != null)
        {
            if (gameObject.transform.parent.gameObject == this.gameObject)
            {
                return true;
            }
            else
            {
                if (depth >= 0)
                {
                    return isParentThisGameObject(gameObject.transform.parent.gameObject, depth - 1);
                }
            }
        }
        return false;
    }
    int objectCounter = 0;
    int shadowConeLimiter = 0;
    public int childrenCount(LSystemNode node, int depth = 3)
    {
        int c = 0;
        if (node.children.Count > 0)
        {
            c += node.children.Count;
            if (depth > 0)
            {
                foreach (LSystemNode child in node.children)
                {
                    c += childrenCount(child, depth - 1);
                }
            }
        }
        return c;
    }
    public void createModelsRecursiveInEnviroment(LSystemNode node, Matrix4x4 transformation, int depth = 0)
    {
        //        transformation *= Matrix4x4.Translate(gameObject.transform.position);
        while (node != null && objectCounter < 25000)
        {
            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);
                Debug.Log("D: " + node.literal);
                new_transformation = new_transformation * Matrix4x4.Rotate(Quaternion.Euler(UnityEngine.Random.Range(-0.5f, 0.5f), UnityEngine.Random.Range(-0.5f, 0.5f), UnityEngine.Random.Range(-0.5f, 0.5f)));
                new_transformation = lightDirection(new_transformation, result.Item2, result.Item1, node);

                if (new_transformation == Matrix4x4.Translate(new Vector3(0, 0, 0)) && environment.cutBranchesWithMaxShadow)
                {
                    Debug.Log(" children " + node.children.Count);
                    //                    node.literal.
                    if (childrenCount(node) < 5)
                    {
                        LSystemNode destroyNode = node;
                        if (destroyNode.parent != null)
                        {
                            destroyNode.parent.children.Remove(node);
                            if (destroyNode.parent.mainChild == null)
                            {
                                destroyNode.parent.mainChild = destroyNode.parent.children[0];
                            }
                            node = node.mainChild;
                            destroyNode = null;
                            //                    DestroyImmediate(instance);
                            objectCounter--;
                        }
                        continue;
                    }
                }
                if (result.Item1 != null && new_transformation != Matrix4x4.Translate(new Vector3(0, 0, 0)))
                {
                    var instance = this.prepeareGameObject(name, result.Item1, new_transformation);
                    environment.addShadow(new_transformation.ExtractPosition() + transformation.MultiplyPoint(result.Item1.transform.position), (sbyte)(shadowStrength / 8.0f));
                }

                environment.addShadow(new_transformation.ExtractPosition() + transformation.ExtractPosition(), (sbyte)(shadowStrength / 8.0f));
                environment.addShadow((new_transformation * Matrix4x4.Translate(new Vector3(0, 1, 0))).ExtractPosition() + transformation.ExtractPosition(), (sbyte)(shadowStrength / 8.0f));
                //remove scale, rather unnecesary
                new_transformation = new_transformation * Matrix4x4.Scale(result.Item2.ExtractScale()).inverse;
            }
            if (node != null)
            {
                List<LSystemNode> children_tmp = new List<LSystemNode>();
                foreach (var child in node.children)
                {
                    children_tmp.Add(child);
                }
                foreach (var child in children_tmp)
                {
                    if (child != null)
                    {
                        createModelsRecursiveInEnviroment(child, new_transformation, depth + 1);
                    }
                }
                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);
        //
        //        }
        objectCounter = 0;
        int removed = 1;
        while (removed > 0)
        {
            removed = 0;
            foreach (Transform child in this.gameObject.transform)
            {
                //                environment.addShadow(gameObject.GetComponent<Transform>().position, (sbyte)-shadowStrength);
                if (child.gameObject.tag == "LSystemLiteral")
                {
                    DestroyImmediate(child.gameObject);
                }
                removed++;
                //            GameObject.Destroy(child.gameObject);
            }

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