﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

#if UNITY_EDITOR
public class CopyBoundingWindow : EditorWindow
{
    private static SkinnedMeshRenderer SourceMeshRendererContext;

    public SkinnedMeshRenderer SourceMeshRenderer;
    public GameObject RootGameObject;
    public bool CopyToAllChildren;
    
    private bool done = false;
    private MessageType result = MessageType.None;
    private string resultTxt = "";

    [MenuItem("Tools/JessysUnityTools/CopyBounding")]
    public static void ShowWindow()
    {
        GetWindow<CopyBoundingWindow>(false, "Copy Bounding", true);
    }

    // Add a menu item called "Double Mass" to a Rigidbody's context menu.
   
    void OnGUI()
    {

        SourceMeshRenderer = SourceMeshRendererContext = (SkinnedMeshRenderer) EditorGUILayout.ObjectField("Source", SourceMeshRendererContext, typeof(SkinnedMeshRenderer), true);
        RootGameObject = (GameObject) EditorGUILayout.ObjectField("Root", RootGameObject, typeof(GameObject), true);
        CopyToAllChildren = (bool)EditorGUILayout.Toggle("Copy to all Children?", true);


        if (GUILayout.Button("Copy Boundings from Source to all Objects in Root"))
        {
            if (!SourceMeshRenderer)
            {
                resultTxt = "FAILED: No Source Skinned Mesh Renderer set.";
                result = MessageType.Error;
            }
            else if (!RootGameObject)
            {
                resultTxt = "FAILED: No Root GameObject set.";
                result = MessageType.Error;
            }
            else
            {
                ApplyBounding(SourceMeshRenderer, RootGameObject, CopyToAllChildren);

                resultTxt = "SUCCESS: Copy successfully finished.";
                result = MessageType.Info;
            }

            done = true;
        }

        if (done)
        {
            EditorGUILayout.HelpBox(resultTxt, result);
        }
    }
    
    static bool ApplyBounding(SkinnedMeshRenderer source, GameObject root, bool toChildren)
    {
        Bounds _sourceBounds = source.localBounds;

        SkinnedMeshRenderer[] meshRenderers = root.transform.GetComponentsInChildren<SkinnedMeshRenderer>(true);
        SkinnedMeshRenderer localMeshRenderer = root.transform.GetComponent<SkinnedMeshRenderer>();

        if (localMeshRenderer)
        {
            localMeshRenderer.localBounds = _sourceBounds;
        }

        if (toChildren)
        {
            foreach (SkinnedMeshRenderer skinnedMeshRenderer in meshRenderers)
            {
                skinnedMeshRenderer.localBounds = _sourceBounds;
            }
        }

        return true;
    }

    static private bool isMeshRendererSet = false;

    [MenuItem("GameObject/JessysUnityTools/CopyBounding/Copy", false, 0)]
    static void SetBoundingSource(MenuCommand command)
    {
        SourceMeshRendererContext = ((GameObject)command.context).transform.GetComponent<SkinnedMeshRenderer>();

        if (SourceMeshRendererContext)
        {
            isMeshRendererSet = true;
        }
        else
        {
            EditorUtility.DisplayDialog("Error", "Could not copy bounding - no SkinnedMeshRenderer found.", "Ok");
        }

    }

    [MenuItem("GameObject/JessysUnityTools/CopyBounding/Paste To all Children", true, 1)]
    [MenuItem("GameObject/JessysUnityTools/CopyBounding/Paste", true, 1)]
    static bool validatePaste(MenuCommand command)
    {
        return isMeshRendererSet;
    }

    [MenuItem("GameObject/JessysUnityTools/CopyBounding/Paste", false, 2)]
    static void Paste(MenuCommand command)
    {
        GameObject root = (GameObject)command.context;
        ApplyBounding(SourceMeshRendererContext, root, false);
    }
    
    [MenuItem("GameObject/JessysUnityTools/CopyBounding/Paste To all Children", false, 2)]
    static void PasteToTree(MenuCommand command)
    {
        GameObject root = (GameObject)command.context;
        ApplyBounding(SourceMeshRendererContext, root, true);
        EditorUtility.DisplayDialog(
            "Success",
            $"Copy Bounding from {SourceMeshRendererContext.name} to all SkinnedMeshRenderers in tree of {root.name} successfully done.", "Ok");
    }
    
    

    [MenuItem("GameObject/JessysUnityTools/CopyBounding/Generate Bounds based on Child Bounds", false, 2)]
    static void StartCheckedGenerator(MenuCommand command)
    {
        bool hasAnyScaling = false;
        GameObject root = (GameObject)command.context;

        SkinnedMeshRenderer[] meshRenderers = root.transform.GetComponentsInChildren<SkinnedMeshRenderer>(true);
        foreach (SkinnedMeshRenderer skinnedMeshRenderer in meshRenderers)
        {
            if (
                Math.Abs(skinnedMeshRenderer.transform.localScale.x - 1) > 0.01 ||
                Math.Abs(skinnedMeshRenderer.transform.localScale.y - 1) > 0.01 ||
                Math.Abs(skinnedMeshRenderer.transform.localScale.z - 1) > 0.01
            ) hasAnyScaling = true;
        }

        if (hasAnyScaling)
        {
            int result = EditorUtility.DisplayDialogComplex(
                "Possible scaling issues detected",
                "The selected object Tree contains scaled GameObjects (see transforms). This can cause issues for the generator of a bounding box." 
                + "A generated Bounding Box can be too large or misplaced. How shall we proceed?",
                "[Suggested] Ignore scaled GameObjects", 
                "Cancel Generation",
                "Generate Anyways");

            if (result == 1) return;
            else if (result == 0) GenerateBoundingBox(command, true);
            else if (result == 2) GenerateBoundingBox(command, false);
        }
        else
        {
            GenerateBoundingBox(command, false);
        }
    }
    
    static void GenerateBoundingBox(MenuCommand command, bool ignoreScaleing)
    {
        GameObject root = (GameObject)command.context;

        // GameObject prefab = root.transform.Find("Prefab").gameObject;
        // GameObject prefab2 = root.transform.Find("Prefab_2").gameObject;
        // GameObject prefab3 = root.transform.Find("Prefab_3").gameObject;



        SkinnedMeshRenderer[] meshRenderers = root.transform.GetComponentsInChildren<SkinnedMeshRenderer>(true);

        Vector3? minStart = null;
        Vector3? maxEnd = null;

        foreach (SkinnedMeshRenderer skinnedMeshRenderer in meshRenderers)
        {
            if (
                ignoreScaleing && (
                    Math.Abs(skinnedMeshRenderer.transform.localScale.x - 1) > 0.01 ||
                    Math.Abs(skinnedMeshRenderer.transform.localScale.y - 1) > 0.01 ||
                    Math.Abs(skinnedMeshRenderer.transform.localScale.z - 1) > 0.01
                )
            ) continue;
            


            // skinnedMeshRenderer.localBounds = _sourceBounds;

            Vector3 center = skinnedMeshRenderer.localBounds.center;
            Vector3 end = skinnedMeshRenderer.localBounds.extents;
            Vector3 scale = skinnedMeshRenderer.transform.localScale;

            end = new Vector3(end.x * scale.x, end.y * scale.y, end.z * scale.z);

            Vector3 start = center + skinnedMeshRenderer.transform.localPosition - end;
            end = center + skinnedMeshRenderer.transform.localPosition + end;
            //
            // GameObject startO = Instantiate(prefab, start, Quaternion.identity);
            // GameObject endO = Instantiate(prefab, end, Quaternion.identity);
            //
            // startO.name = "Start-" + skinnedMeshRenderer.transform.name;
            // endO.name = "End-" + skinnedMeshRenderer.transform.name;

            if (!minStart.HasValue) minStart = start;
            if (!maxEnd.HasValue) maxEnd = end;

            float startX = minStart.Value.x < start.x ? minStart.Value.x : start.x;
            float startY = minStart.Value.y < start.y ? minStart.Value.y : start.y;
            float startZ = minStart.Value.z < start.z ? minStart.Value.z : start.z;

            float endX = maxEnd.Value.x > end.x ? maxEnd.Value.x : end.x;
            float endY = maxEnd.Value.y > end.y ? maxEnd.Value.y : end.y;
            float endZ = maxEnd.Value.z > end.z ? maxEnd.Value.z : end.z;

            minStart = new Vector3(startX, startY, startZ);
            maxEnd = new Vector3(endX, endY, endZ);
        }

        // GameObject mstartO = Instantiate(prefab2, minStart.Value, Quaternion.identity);
        // GameObject mendO = Instantiate(prefab2, maxEnd.Value, Quaternion.identity);
        //
        // mstartO.name = "MaxStart";
        // mendO.name = "MaxEnd";

        Vector3 newExtends = new Vector3(
            (maxEnd.Value.x - minStart.Value.x) / 2,
            (maxEnd.Value.y - minStart.Value.y) / 2,
            (maxEnd.Value.z - minStart.Value.z) / 2
        );

        Vector3 newCenter = minStart.Value + newExtends;

        foreach (SkinnedMeshRenderer skinnedMeshRenderer in meshRenderers)
        {
            if (
                ignoreScaleing && (
                    Math.Abs(skinnedMeshRenderer.transform.localScale.x - 1) > 0.01 ||
                    Math.Abs(skinnedMeshRenderer.transform.localScale.y - 1) > 0.01 ||
                    Math.Abs(skinnedMeshRenderer.transform.localScale.z - 1) > 0.01
                )
            ) continue;

            Vector3 tCenter = newCenter - skinnedMeshRenderer.transform.localPosition;
            skinnedMeshRenderer.localBounds = new Bounds(
                    new Vector3(
                        tCenter.x / skinnedMeshRenderer.transform.localScale.x,
                        tCenter.y / skinnedMeshRenderer.transform.localScale.y,
                        tCenter.z / skinnedMeshRenderer.transform.localScale.z
                    ),
                    new Vector3(
                        (newExtends.x / skinnedMeshRenderer.transform.localScale.x) * 2,
                        (newExtends.y / skinnedMeshRenderer.transform.localScale.y) * 2,
                        (newExtends.z / skinnedMeshRenderer.transform.localScale.z) * 2
                        )
                );
        }
        
    }
}

#endif
