using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;

public class ActiveScoppedElements : MonoBehaviour
{
    
    [Serializable]
    public struct ObjectToInstantiate {
        public List<String> elements;
        public GameObject obj;
    }

    public enum AnimationState
    {
        IDLE,
        GORIGHT,
        GOLEFT
    }


    public TextAsset jsonFile;
    public TextAsset atomJsonFile;
    public HashSet<String> actives = new HashSet<String>();
    public HashSet<GameObject> InstantiatesObject = new HashSet<GameObject>();
    public HashSet<GameObject> AtomInstantiatesObjects = new HashSet<GameObject>();
    public TextMeshProUGUI textToUpdate;
    public TextMeshProUGUI objectInstantiateText;
    public HashSet<GameObject> DisableGameObjects = new HashSet<GameObject>();
    public GameObject[] InformationObjs;
    public GameObject[] DebugObjs;
    private AnimationState _animationState = AnimationState.IDLE;
    private float _animationSpeed = 1.0f;
    public GameObject atomPrefabToInstantiate;
    private bool IsLock = false;

    private AtomeFactory _atomFactory;
        

    void Start()
    {
        _atomFactory = new AtomeFactory(this.atomJsonFile);
        Debug.Log("AtomFactory created !");
        /*if (button != null)
        {
            button.onClick.AddListener(this.Center);
        }*/
        if (this.textToUpdate != null)
        {
            textToUpdate.text = "Scanner des Markers...";
        }
    }

    public void AddScoppedElement(String name)
    {
        Debug.Log("Added " + name);
        if (!actives.Contains(name))
        {
            actives.Add(name);
            RefreshText();
            RefreshAtom(name, true);
        }
    }

    public void Center()
    {
        Debug.Log("Center called !");
        foreach (var obj in InstantiatesObject)
        {
            if (Camera.main == null)
            {
                throw new Exception("NO MAIN CAMERA DEFINED ");
            }
            Vector3 cameraPosition = Camera.main.transform.position;

            // Obtenez la rotation de la caméra sans inclure la rotation autour de l'axe Y
            Quaternion cameraRotation = Quaternion.Euler(0, Camera.main.transform.rotation.eulerAngles.y, 0);

            // Calculez la position finale en ajoutant la direction vers l'avant multipliée par la distance désirée
            Vector3 position = cameraPosition + cameraRotation * Vector3.forward * 0.35f;

            //var instantiatedObj = Instantiate(objt.obj, position, cameraRotation);
            
            obj.transform.position = position;
            obj.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
            obj.transform.rotation = cameraRotation;
        }
    }

    public void onClickStopAnimation()
    {
        Quaternion cameraRotation = Quaternion.Euler(0, Camera.main.transform.rotation.eulerAngles.y, 0);
        this._animationState = AnimationState.IDLE;
        foreach (var obj in InstantiatesObject)
        {
            obj.transform.rotation = cameraRotation;
        }
        
    }

    public void onClickPauseAnimation()
    {
        this._animationState = AnimationState.IDLE;
    }

    public void onClickStartAnimation()
    {
        this._animationState = AnimationState.GORIGHT;
    }

    public void onClickRightAnimation()
    {
        this._animationState = AnimationState.GORIGHT;
    }

    public void onClickGoLeftAnimation()
    {
        this._animationState = AnimationState.GOLEFT;
    }

    public void onClickSpeedUp()
    {
        this._animationSpeed += 0.5f;
    }

    public void onClickSpeedDown()
    {
        this._animationSpeed -= 0.5f;
    }

    public void onClickToggleLockMode()
    {
        this.IsLock = !this.IsLock;
    }

    void Update()
    {
        if (this._animationState == AnimationState.GORIGHT)
        {
            foreach (var obj in InstantiatesObject)
            {
                obj.transform.Rotate(0, this._animationSpeed, 0, Space.Self);
                /*Vector3 position = obj.GetComponent<SphereCollider>().bounds.center;
                obj.transform.RotateAround(position, new Vector3(0, 0.1f, 0), 30 * Time.deltaTime);*/
            }
        }
    }

    private void RefreshText()
    {
        if (this.textToUpdate != null)
        {
            this.textToUpdate.text = String.Join(", ", this.actives);
        }
    }
    
    static IEnumerable<string> GetPermutations(List<string> list)
    {
        if (list.Count == 1)
        {
            // Cas de base : une seule chaîne
            yield return list[0];
        }
        else
        {
            // Récursion pour obtenir les permutations du reste de la liste
            foreach (var item in list)
            {
                var remaining = new List<string>(list);
                remaining.Remove(item);

                foreach (var subPermutation in GetPermutations(remaining))
                {
                    // Concaténer l'élément actuel avec les permutations du reste
                    yield return item + subPermutation;
                }
            }
        }
    }

    private void DisableAllMarkerModel()
    {
        foreach (var o in GameObject.FindGameObjectsWithTag("OBJ"))
        {
            o.SetActive(false);
            DisableGameObjects.Add(o);
        }
    }

    private void EnableAllMarkerModel()
    {
        foreach (var o in DisableGameObjects)
        {
            o.SetActive(true);
        }
        DisableGameObjects.Clear();
    }

    private GameObject FindGameObjectWithFormula()
    {
        var factory = MoleculeFactory.getInstrance(this.jsonFile);
        factory.setAtomFactory(this._atomFactory);
        Debug.Log("ACTIVES ELEMENTS:");
        Debug.Log(this.actives);
        foreach (var formula in GetPermutations(this.actives.ToList()))
        {
            Debug.Log("FORMULA: " + String.Join("", formula));
            if (factory.hasMolecule(String.Join("", formula)))
            {
                return factory.createMolecule(String.Join("", formula));
            }
        }
        return null;
    }

    private String GetFormula()
    {
        var factory = MoleculeFactory.getInstrance(this.jsonFile);
        factory.setAtomFactory(this._atomFactory);
        foreach (var formula in GetPermutations(this.actives.ToList()))
        {
            Debug.Log("FORMULA: " + String.Join("", formula));
            if (factory.hasMolecule(String.Join("", formula)))
            {
                return String.Join("", formula);
            }
        }
        return null;
    }

    private void RenderAtom(String elementName, bool toAdd)
    {
        var cleanedElement = CleanStringOfDigits(elementName);
        if (!toAdd)
        {
            try
            {
                var a = AtomInstantiatesObjects.First(t => t.name == ("ATOM_" + cleanedElement));
                if (a != null)
                {
                    AtomInstantiatesObjects.Remove(a);
                    Destroy(a);
                    Debug.Log("AtomInstantiatesObjects.Remove(a) called !");
                    return;
                }
            }
            catch (Exception ex)
            {
                Debug.Log("pas trouvé");
                return;
            }
            
        }
        var atomObject = GameObject.Find(elementName);
        if (atomObject == null) {
            Debug.Log("atomObject=null");
            return;
        }
        if (atomObject.transform.childCount >= 1) {
            //atomObject.transform.GetChild(0).GetComponent<GameObject>().SetActive(true);
            Debug.Log("Object already created !");
            return;
        }
        
        var factory = MoleculeFactory.getInstrance(this.jsonFile);
        factory.setAtomFactory(this._atomFactory);
        if (!factory.GetAtomFactory().hasAtome(cleanedElement))
        {
            Debug.Log("No atom found with this formula '" + cleanedElement + "'");
            return;
        }
        var obj = this._atomFactory.createAnimatedAtome(cleanedElement);
        obj.name = "ATOM_" + cleanedElement;
        obj.transform.localScale = new Vector3(0.3f, 0.3f, 0.3f);
        
        Quaternion cameraRotation = Quaternion.Euler(0, Camera.main.transform.rotation.eulerAngles.y, 0);
        obj.transform.localPosition = new Vector3(0, 0, 0);
        obj.transform.SetParent(atomObject.transform, false);
        obj.transform.rotation = cameraRotation;
        AtomInstantiatesObjects.Add(obj);
        
        //var obj = Instantiate(atomPrefabToInstantiate, new Vector3(0, 0, 0), Quaternion.identity);
        //AtomPrefab objPrefab = obj.GetComponent<AtomPrefab>();
        //AtomeInformation info = factory.GetAtomFactory().createAtome(cleanedElement);
        //objPrefab.Render(info);
    }
    
    private void RefreshAtom(String elementName, bool toAdd)
    {
        var objToFind = FindGameObjectWithFormula();
        if (objToFind != null)
        {
            Debug.Log("Object to instantiate found !");
            if (Camera.main == null)
            {
                throw new Exception("NO MAIN CAMERA DEFINED ");
            }
            
            foreach (var atomInstantiatesObject in AtomInstantiatesObjects)
            {
                Destroy(atomInstantiatesObject);
            }
            AtomInstantiatesObjects.Clear();
            
            Vector3 cameraPosition = Camera.main.transform.position;
            Quaternion cameraRotation = Quaternion.Euler(0, Camera.main.transform.rotation.eulerAngles.y, 0);
            Vector3 position = cameraPosition + cameraRotation * Vector3.forward * 0.35f;
            
            objToFind.transform.position = position;
            objToFind.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
            objToFind.transform.rotation = cameraRotation;
            
            MeshRenderer meshRenderer = objToFind.GetComponent<MeshRenderer>();
            if (meshRenderer != null)
            {
                float sizeInDirection = meshRenderer.bounds.size.x; // Pour la hauteur, ajustez selon votre besoin
                Debug.Log("SizeInDirection: " + sizeInDirection);
                objToFind.transform.position -= objToFind.transform.right * sizeInDirection * 0.175f;
            }
            
            Debug.Log("RefreshAtom(): Created " + objToFind.name);

            objToFind.name = "[N] " + objToFind.name;
            InstantiatesObject.Add(objToFind);
            this.OnInstantiateObject(objToFind);
            Debug.Log("Instantiate " + objToFind.name);
            DisableAllMarkerModel();
        } else {
            foreach (var o in InstantiatesObject) {
                Destroy(o);
                OnDestroyObject(o);
            }
            InstantiatesObject.Clear();
            EnableAllMarkerModel();
            RenderAtom(elementName, toAdd);
        }
        
        if (InstantiatesObject.Count > 0 && objectInstantiateText != null) {
            objectInstantiateText.text = String.Join(", ", InstantiatesObject.Select((s) => s.name));
        } else if (objectInstantiateText != null) {
            objectInstantiateText.text = "En attente...";
        }
    }

    private void SetMoleculeInfo(String formula)
    {
        if (formula == null) {
            return;
        }
        var factory = MoleculeFactory.getInstrance(this.jsonFile);
        factory.setAtomFactory(this._atomFactory);
        Molecule mol = factory.GetMoleculeByFormula(formula);
        var descriptionGO = GameObject.Find("TXT_DESC").GetComponent<TextMeshProUGUI>();
        var atomNbGO = GameObject.Find("TXT_ATOM_NB").GetComponent<TextMeshProUGUI>();
        var symGO = GameObject.Find("TXT_SYM").GetComponent<TextMeshProUGUI>();
        var nameGO = GameObject.Find("TXT_NAME").GetComponent<TextMeshProUGUI>();
        nameGO.text = mol.name;
        symGO.text = mol.formula;
        descriptionGO.text = "Masse moleculaire: ";
        atomNbGO.text = mol.atoms.Length + " Atomes";
    }

    private void OnInstantiateObject(GameObject obj)
    {
        foreach (var informationObj in InformationObjs)
        {
            informationObj.SetActive(true);
        }
        SetMoleculeInfo(GetFormula());
        foreach (var debugObj in DebugObjs)
        {
            debugObj.SetActive(false);
        }
    }

    private void OnDestroyObject(GameObject obj)
    {
        foreach (var informationObj in InformationObjs)
        {
            informationObj.SetActive(false);
        }
        foreach (var debugObj in DebugObjs)
        {
            debugObj.SetActive(true);
        }
    }
    
    private string CleanStringOfNonDigits( string s )
    {
        if (string.IsNullOrEmpty(s)) return s;
        StringBuilder sb = new StringBuilder(s.Length) ;
        for (int i = 0; i < s.Length; ++i)
        {
            char c = s[i];
            if ( c < '0' ) continue ;
            if ( c > '9' ) continue ;
            sb.Append(s[i]);
        }
        string cleaned = sb.ToString();
        return cleaned;
    }

    private string CleanStringOfDigits(string s)
    {
        return new String(s.Where(Char.IsLetter).ToArray());
    }

    public void RemoveUnscoppedElement(String elementName)
    {
        if (this.IsLock)
        {
            return;
        }
        Debug.Log("Removed " + elementName);
        actives.Remove(elementName);
        RefreshText();
        RefreshAtom(elementName, false);
    }
}
