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

[CustomEditor(typeof(Transform))]
public class AntaresGameObjectCustom : Editor
{
    public static readonly string currentYear = "2009 - " + DateTime.Now.Year, AntaresLoadUrl = "http://guild.unity3d.ru/index.php/en/component/content/article/18.html";

    public static class Routines
    {
        public static void GUIAlpha(float alpha)
        {
            GUI.color = new Color(GUI.color.r, GUI.color.g, GUI.color.b, alpha);
        }

        private static string _pathToImages = "";
        public static string pathToImages
        {
            get
            {
                if (_pathToImages == "")
                    _pathToImages = "Assets" + Path.DirectorySeparatorChar + "Antares" + Path.DirectorySeparatorChar + "Images" + Path.DirectorySeparatorChar;
                return _pathToImages;
            }
        }

        private static Texture _imageGoldMan;
        public static Texture imageGoldMan
        {
            get
            {
                if (_imageGoldMan == null) _imageGoldMan = AssetDatabase.LoadAssetAtPath(AntaresGameObjectCustom.Routines.pathToImages + "gold.jpg", typeof(Texture)) as Texture;
                if (_imageGoldMan == null) return EditorGUIUtility.whiteTexture;
                return _imageGoldMan;
            }
        }

        private static Texture _imageBackground;
        public static Texture imageBackground
        {
            get
            {
                if (_imageBackground == null) _imageBackground = AssetDatabase.LoadAssetAtPath(AntaresGameObjectCustom.Routines.pathToImages + "background.png", typeof(Texture)) as Texture;
                if (_imageBackground == null) return EditorGUIUtility.whiteTexture;
                return _imageBackground;
            }
        }

        private static Texture _imageHeaderLeft;
        public static Texture imageHeaderLeft
        {
            get
            {
                if (_imageHeaderLeft == null) _imageHeaderLeft = AssetDatabase.LoadAssetAtPath(AntaresGameObjectCustom.Routines.pathToImages + "HeaderLeft.png", typeof(Texture)) as Texture;
                if (_imageHeaderLeft == null) return EditorGUIUtility.whiteTexture;
                return _imageHeaderLeft;
            }
        }

        private static Texture _imageHeader_f;
        public static Texture imageHeader_f
        {
            get
            {
                if (_imageHeader_f == null) _imageHeader_f = AssetDatabase.LoadAssetAtPath(AntaresGameObjectCustom.Routines.pathToImages + "Header_f.png", typeof(Texture)) as Texture;
                if (_imageHeader_f == null) return EditorGUIUtility.whiteTexture;
                return _imageHeader_f;
            }
        }

        private static Texture _imageFooter;
        public static Texture imageFooter
        {
            get
            {
                if (_imageFooter == null) _imageFooter = AssetDatabase.LoadAssetAtPath(AntaresGameObjectCustom.Routines.pathToImages + "Footer.png", typeof(Texture)) as Texture;
                if (_imageFooter == null) return EditorGUIUtility.whiteTexture;
                return _imageFooter;
            }
        }

        private static Texture _imageIcon;
        public static Texture imageIcon
        {
            get
            {
                if (_imageIcon == null) _imageIcon = AssetDatabase.LoadAssetAtPath(AntaresGameObjectCustom.Routines.pathToImages + "icon.png", typeof(Texture)) as Texture;
                if (_imageIcon == null) return EditorGUIUtility.whiteTexture;
                return _imageIcon;
            }
        }

        private static Texture _imageIconSmall;
        public static Texture imageIconSmall
        {
            get
            {
                if (_imageIconSmall == null) _imageIconSmall = AssetDatabase.LoadAssetAtPath(AntaresGameObjectCustom.Routines.pathToImages + "iconSmall.png", typeof(Texture)) as Texture;
                if (_imageIconSmall == null) return EditorGUIUtility.whiteTexture;
                return _imageIconSmall;
            }
        }

        public static void DrawBackground(Rect rect)
        {
            try
            {
                GUI.enabled = false;
                GUI.Box(rect, imageBackground, GUIStyle.none);
                GUI.enabled = true;
            }
            catch
            {
            }
        }

        public static void DrawBackground(Rect rect, bool guiEnabled)
        {
            try
            {
                GUI.enabled = guiEnabled;
                GUI.Box(rect, imageBackground, GUIStyle.none);
                GUI.enabled = true;
            }
            catch
            {
            }
        }

        public static string donationURL = "http://shop.unity3d.ru/index.php/en/en/component/virtuemart/?page=shop.product_details&flypage=flypage.tpl&product_id=6&category_id=29";
        public static void ShowCredits(Rect rect)
        {
            try
            {
                if (!string.IsNullOrEmpty(WizardAntaresAbout.newVersion))rect.y -= 15;

                EditorGUI.DropShadowLabel(rect, "ANTARES ® " + WizardAntaresAbout.GetVersion());
                EditorGUI.DropShadowLabel(new Rect(rect.x, rect.y + 20, rect.width, rect.height), currentYear);
                if (GUI.Button(rect, new GUIContent("", "DONATION"), GUIStyle.none))
                {
                    Application.OpenURL(donationURL);
                }

                if (!string.IsNullOrEmpty(WizardAntaresAbout.newVersion))
                {
                    GUI.color = AntaresEdMainWindow.colorSelected;
                    if (GUI.Button(new Rect(rect.x, rect.y + 60, rect.width, 15), "Get new version!"))
                        GetNewVersion();
                    GUI.color = Color.white;
                }
            }
            catch
            {
            }
        }

        public static void GetNewVersion()
        {
            Application.OpenURL(AntaresLoadUrl);
        }

        internal static void DrawHeader(Rect lastRect)
        {
            try
            {  
                GUI.DrawTexture(new Rect(lastRect.x, lastRect.y, lastRect.width, 200), imageHeader_f);
                GUI.DrawTexture(new Rect(lastRect.x - 64, lastRect.y, 200, 200), imageHeaderLeft);
            }
            catch
            {
            }
        }

        internal static void DrawHeader(string description)
        {
            try
            {
                Rect lastRect = GUILayoutUtility.GetLastRect();
                DrawHeader(lastRect);
                lastRect.y += 20;
                EditorGUI.DropShadowLabel(lastRect, description);
                if (GUI.Button(lastRect, new GUIContent("", "DONATION"), GUIStyle.none))
                {

                }
                GUILayout.Space(32);
            }
            catch
            {
            }
        }

        internal static void DrawFooter()
        {
            try
            {
                GUILayout.Box("", GUIStyle.none, GUILayout.ExpandWidth(true), GUILayout.Height(77));
                Rect lastRect = GUILayoutUtility.GetLastRect();
                GUI.DrawTexture(new Rect(lastRect.x, lastRect.y - 48, lastRect.width, 128), imageFooter);
                ShowCredits(new Rect((lastRect.x + lastRect.width*.5f - 50), lastRect.y + 20, 100, 32));
            }
            catch
            {
            }
        }

        [MenuItem("Antares/HELP/Open online PDF (eng)")]
        public static void ShowPDFOnline()
        {
            Application.OpenURL("http://www.unity3d.ru/ANTARES/AntaresBrowser_en.pdf");
        }

        [MenuItem("Antares/HELP/Open online PDF (rus)")]
        public static void ShowPDFOnlineRus()
        {
            Application.OpenURL("http://www.unity3d.ru/ANTARES/AntaresBrowser_rus.pdf");
        }

        [MenuItem("Antares/HELP/About Antares Project")]
        public static void ShowAbout()
        {
            WizardAntaresAbout.CreateWindow();
        }

        [MenuItem("Antares/Selection/Apply changes to prefabs")]
        public static void ApplyChangesToPrefabs()
        {
            float index = 1;
            foreach (UnityEngine.Object go in Selection.objects)
            {
                EditorUtility.DisplayProgressBar("Apply changes to selected prefabs", "Apply to : " + go.name,
                                                 index++ / Selection.objects.Length);
                if (go.GetType() != typeof(GameObject)) continue;
                UnityEngine.Object prefab = EditorUtility.GetPrefabParent(go);
                if(prefab != null)
                {
                    EditorUtility.ReplacePrefab((GameObject)go, prefab);
                    EditorUtility.ReconnectToLastPrefab(go as GameObject);
                }
            }
            EditorUtility.ClearProgressBar();
        }

        [MenuItem("CONTEXT/Transform/Hide in Hierarchy")]
        public static void HideFromContext()
        {
            if (Selection.activeGameObject == null) return;
            Transform targetTransform = Selection.activeGameObject.transform;
            AntaresAddon addon = targetTransform.gameObject.GetComponent<AntaresAddon>();
            if (addon == null) addon = targetTransform.gameObject.AddComponent<AntaresAddon>();
            if (!addon.hideInHierarchy)
            {
                addon.hideInHierarchy = true;
                targetTransform.gameObject.hideFlags = HideFlags.HideInHierarchy;
            }
            else
            {
                addon.hideInHierarchy = false;
                addon.gameObject.hideFlags = 0;
            }
        }

        [MenuItem("Antares/Hide or Freez/Unhide all hided objects &u")]
        public static void UnhideAllHiden()
        {
            AntaresAddon[] addons = GameObject.FindObjectsOfType(typeof (AntaresAddon)) as AntaresAddon[];
            int count = addons.Length;
            for (int i = 0; i < count; i++ )
            {
                addons[i].gameObject.hideFlags = 0;
                EditorUtility.SetDirty(addons[i].gameObject);
            }
        }

        [MenuItem("Antares/Hide or Freez/Hide all 'marked as hided' objects &h")]
        public static void HideAllHiden()
        {
            AntaresAddon[] addons = GameObject.FindObjectsOfType(typeof(AntaresAddon)) as AntaresAddon[];
            int count = addons.Length;
            for (int i = 0; i < count; i++)
            {
                if (!addons[i].hideInHierarchy) continue;
                addons[i].gameObject.hideFlags = HideFlags.HideInHierarchy;
                EditorUtility.SetDirty(addons[i].gameObject);
            }
        }

        [MenuItem("Antares/GameObject/Add Child &c")]
        static void MenuAddChild()
        {
            Transform selected = Selection.activeTransform;
            Undo.RegisterSceneUndo("Add Child");
            GameObject newChild = new GameObject("_Child");
            newChild.transform.parent = selected;
            Selection.activeTransform = newChild.transform;
            Selection.activeObject = newChild;
        }

        [MenuItem("Antares/GameObject/Add Child &c", true)]
        static bool ValidateMenuAddChild()
        {
            return Selection.activeTransform != null;
        }

        [MenuItem("Antares/GameObject/Add Parent &p")]
        public static void MenuAddParent()
        {
            Transform[] selected = Selection.transforms;
            Undo.RegisterSceneUndo("New Object created");
            GameObject newParent = new GameObject("_Parent");
            newParent.transform.position = selected[0].position;
            newParent.transform.parent = selected[0].parent;
            foreach (Transform selectedTransform in selected)
                selectedTransform.parent = newParent.transform;
            Selection.activeTransform = newParent.transform;
            Selection.activeObject = newParent;
        }

        [MenuItem("Antares/GameObject/Add Parent &p", true)]
        static bool ValidateMenuAddParent()
        {
            return Selection.activeTransform != null;
        }
    }

    Transform targetTransform;
    public static GameObject addonController;
    protected SerializedObject serializedObj;
    public static GameObject addCurvePointsActivated = null;
    public static bool addCurvePointsDeactivate = false, update;
    protected int childsCount = 0;
    protected MeshFilter meshFilter;
    protected bool moreOptions, setPosition;
    protected static bool openAllMoreOptions;

    protected float xSide = 7f;
    protected float zSide = 7f;

    protected bool xRandomAngle = false;
    protected bool yRandomAngle = true;
    protected bool zRandomAngle = false;

    protected static bool selectedIsBrush = false;
    protected Vector3 selectedCenterPosition = Vector3.zero;

    //protected List<GameObject> selObjects = new List<GameObject>();
    protected static GameObject brushParent;
    protected static GameObject brushEmptyObj;
    protected static List<GameObject> brushChilds = new List<GameObject>();
    protected static Hashtable brushObjParents = new Hashtable();
    protected static AntaresDecal.Decal brushDecal;

    protected float lastEditingTime;

    void OnEnable()
    {
        moreOptions = openAllMoreOptions;
        targetTransform = target as Transform;
        serializedObj = new SerializedObject(target);
        childsCount = 0;
        //foreach (Transform t in targetTransform.gameObject.GetComponentsInChildren<Transform>()) childsCount += t.childCount;
        childsCount = targetTransform.gameObject.GetComponentsInChildren<Transform>().Length;
        meshFilter = targetTransform.gameObject.GetComponent<MeshFilter>();
        update = false;
        lastEditingTime = DateTime.Now.Second;
    }

    public static void InitController()
    {
        AssetDatabase.StartAssetEditing();
        AntaresAddonController ac = GameObject.FindObjectOfType(typeof(AntaresAddonController)) as AntaresAddonController;
        if (ac == null) ac = AntaresAddonController.InitController();
        else AntaresAddonController.thisScript = ac;
        SerializedObject aco = new SerializedObject(ac);
        aco.Update();
        addonController = ac.gameObject;
        aco.ApplyModifiedProperties();
        AssetDatabase.StopAssetEditing();
    }

    private float alpha = .35f;
    public override void OnInspectorGUI()
    {
        if (alpha < 1)alpha += .01f;

        if (addCurvePointsActivated != null && addCurvePointsActivated.transform != targetTransform.gameObject)
        {
            AntaresBezierController bc = addCurvePointsActivated.GetComponent<AntaresBezierController>();
            if (addCurvePointsDeactivate)
            {
                addCurvePointsActivated = null;
                if (bc != null) AntaresCurveEditor.createPoints = false;
                addCurvePointsDeactivate = false;
            }
            else
            {
                if (addCurvePointsActivated != null) Selection.activeGameObject = addCurvePointsActivated;
                if (bc != null) AntaresCurveEditor.createPoints = true;
            }
        }

        Vector3 startPos = targetTransform.localPosition;
        startPos = EditorGUILayout.Vector3Field("Position", startPos);
        if (targetTransform.localPosition != startPos)
        {
            if (update == false) Undo.RegisterSceneUndo("Transform Change");
            targetTransform.localPosition = startPos;
            update = true;
        }
        
        if(targetTransform.parent != null)
        {
            Vector3 startRot = targetTransform.localRotation.eulerAngles;
            Vector3 rot = EditorGUILayout.Vector3Field("Rotation", startRot); 
            if (startRot != rot)
            {
                if (update == false) Undo.RegisterSceneUndo("Transform Change");
                targetTransform.localRotation = Quaternion.identity;
                targetTransform.Rotate(rot);
                update = true;
            }
        }
        else 
        {
            Vector3 startRot = targetTransform.rotation.eulerAngles;
            Vector3 rot = EditorGUILayout.Vector3Field("Rotation", startRot);
            if (startRot != rot)
            {
                if (update == false) Undo.RegisterSceneUndo("Transform Change");
                targetTransform.rotation = Quaternion.identity;
                targetTransform.Rotate(rot);
                update = true;
            }
        }

        try
        {
            Vector3 startScale = targetTransform.localScale;
            startScale = EditorGUILayout.Vector3Field("Scale", startScale);
            if (startScale != targetTransform.localScale)
            {
                if (update == false) Undo.RegisterSceneUndo("Transform Change");
                targetTransform.localScale = startScale;
                update = true;
            }
        }
        catch { }

        if (update) serializedObj.Update();
        GUILayout.Label("Total Childs Count : " + (childsCount - 1));

        GUI.enabled = targetTransform.gameObject.GetComponent<AntaresAddon>() == null;
        if (GUILayout.Button(new GUIContent("Add Antares Addon", "Will add Antares Addon to this Object."), EditorStyles.miniButton, GUILayout.Width(128)))
        {
            GameObject go = targetTransform.gameObject;
            AntaresAddon aa = go.GetComponent<AntaresAddon>();
            if (aa == null)
            {
                if (go.GetComponent<AntaresAddonController>() == null)
                {
                    go.AddComponent<AntaresAddon>();
                    EditorUtility.SetDirty(target);
                }
            }
            if (addonController == null) InitController();
        }
        GUI.enabled = true;
        Rect iconRect = GUILayoutUtility.GetLastRect();

        Routines.ShowCredits(new Rect(iconRect.xMax + 84, iconRect.y - 20, 100, 32));
        if (alpha < 1) { GUI.color = new Color(GUI.color.r, GUI.color.g, GUI.color.b, alpha);
            alpha += .01f; }
        GUIStyle.none.Draw(new Rect(iconRect.xMax + 15, iconRect.y - 20, 64, 64), Routines.imageIcon, false, false, false, false);
        GUI.color = Color.white;

        GUILayout.Label("More Antares options : ");
        moreOptions = EditorGUILayout.Foldout(moreOptions, "");

        if (moreOptions) MoreOptions();
        if (update)
        {
            if (lastEditingTime + 1f > DateTime.Now.Second) return;
            serializedObj.ApplyModifiedProperties();
            update = false;
            lastEditingTime = DateTime.Now.Second;
            Undo.RegisterSceneUndo("Transform Change");
        }
    }

    void HideUnhide()
    {
        GUILayout.BeginHorizontal();
        AntaresAddon addon = targetTransform.gameObject.GetComponent<AntaresAddon>();
        if (GUILayout.Button(new GUIContent("Hide in Hierarchy", "Hide this object in Hierarchy.\nYou can unhide it from Antares Menu."), EditorStyles.miniButton, GUILayout.Width(128)))
        {
            if (addon == null) addon = targetTransform.gameObject.AddComponent<AntaresAddon>();
            if (!addon.hideInHierarchy)
            {
                addon.hideInHierarchy = true;
                targetTransform.gameObject.hideFlags = HideFlags.HideInHierarchy;
            }
            else
            {
                addon.hideInHierarchy = false;
                addon.gameObject.hideFlags = 0;
            }
            EditorUtility.SetDirty(targetTransform.gameObject);
        }
        if (addon != null)
        {
            GUI.enabled = false;
            GUILayout.Toggle(addon.hideInHierarchy, "");
            GUI.enabled = true;
        }
        GUILayout.EndHorizontal();
    }

    void MoreOptions()
    {
        GUI.enabled = Selection.activeTransform == targetTransform;
        if (setPosition) GUI.color = AntaresEdMainWindow.colorSelected;
        if (GUILayout.Button(new GUIContent("Set Position", "Set position by mouse click"), EditorStyles.miniButton, GUILayout.Width(128)))
        {
            setPosition = !setPosition;
        }
        GUI.color = Color.white;
        GUI.enabled = true;

        int selectionsCount = Selection.gameObjects.Length;

        //GUI.enabled = selectionsCount > 1;
        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button(new GUIContent("Random Position", "Set random position near current position"), EditorStyles.miniButton, GUILayout.Width(128)))
        {
            for (int i = 0; i < selectionsCount; i++)
            {
                Selection.gameObjects[i].transform.position += new Vector3(UnityEngine.Random.Range(-xSide * 0.5f, zSide * 0.5f), 0, UnityEngine.Random.Range(-xSide * 0.5f, zSide * 0.5f));
            }
            //setPosition = !setPosition;
        }

        GUILayout.Label(new GUIContent("x:", "X side of spreading"), GUILayout.Width(40));
        xSide = EditorGUILayout.FloatField(xSide, GUILayout.Width(40));
        GUILayout.Label(new GUIContent("z:", "Z side of spreading"), GUILayout.Width(40));
        zSide = EditorGUILayout.FloatField(zSide, GUILayout.Width(40));

        EditorGUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal();
        float xAngle = 0f;
        float yAngle = 0f;
        float zAngle = 0f;
        if (GUILayout.Button(new GUIContent("Random Angles", "Set random Angles"), EditorStyles.miniButton, GUILayout.Width(128)))
        {
            for (int i = 0; i < selectionsCount; i++)
            {
                if (xRandomAngle)
                {
                    xAngle = UnityEngine.Random.Range(0, 360);
                }
                if (yRandomAngle)
                {
                    yAngle = UnityEngine.Random.Range(0, 360);
                }
                if (zRandomAngle)
                {
                    zAngle = UnityEngine.Random.Range(0, 360);
                }
                Selection.gameObjects[i].transform.Rotate(xAngle, yAngle, zAngle, Space.World); // += new Vector3(UnityEngine.Random.Range(-xSide * 0.5f, zSide * 0.5f), 0, UnityEngine.Random.Range(-xSide * 0.5f, zSide * 0.5f));
            }
            //setPosition = !setPosition;
        }
        if (GUILayout.Button(new GUIContent("Set angles to 0", "Set angles to 0"), EditorStyles.miniButton, GUILayout.Width(128)))
        {
            for (int i = 0; i < selectionsCount; i++)
            {
                Quaternion curRot = Selection.gameObjects[i].transform.rotation;
                Vector3 newEA = Vector3.zero;
                if (!xRandomAngle)
                {
                    newEA.x = curRot.eulerAngles.x;
                }
                if (!yRandomAngle)
                {
                    newEA.y = curRot.eulerAngles.y;
                }
                if (!zRandomAngle)
                {
                    newEA.z = curRot.eulerAngles.z;
                }
                Selection.gameObjects[i].transform.eulerAngles = newEA;
            }
        }

        xRandomAngle = GUILayout.Toggle(xRandomAngle, new GUIContent(":x", "Random angle around X axe"));
        yRandomAngle = GUILayout.Toggle(yRandomAngle, new GUIContent(":y", "Random angle around Y axe"));
        zRandomAngle = GUILayout.Toggle(zRandomAngle, new GUIContent(":z", "Random angle around Z axe"));
        EditorGUILayout.EndHorizontal();

        selectionsCount = Selection.gameObjects.Length;
        GUI.enabled = selectionsCount > 1;
        if (GUILayout.Button(new GUIContent("Parent All Selected", "Will parent all selected objects to new one."), EditorStyles.miniButton, GUILayout.Width(128)))
        {
            Routines.MenuAddParent();
        }

        EditorGUILayout.BeginHorizontal();
        GUI.enabled = meshFilter != null;
        if (GUILayout.Button("Create Empty Parent", EditorStyles.miniButton, GUILayout.Width(128)))
        {
            GameObject parent = new GameObject(targetTransform.gameObject.name + "_parent");
            parent.transform.position = targetTransform.TransformPoint(meshFilter.sharedMesh.bounds.center);
            targetTransform.parent = parent.transform;
            Undo.RegisterSceneUndo("Create Empty Parent");
        }
        HelpButton("Create Empty Parent", "Will create an empty object in the mesh center of the selected object and will parent this selected object to the new one created.");
        GUI.enabled = true;
        EditorGUILayout.EndHorizontal();

        if (selectedIsBrush) GUI.color = AntaresEdMainWindow.colorSelected;
        if (GUILayout.Button("Make Brush!"))
        {
            selectedIsBrush = !selectedIsBrush;

            if (selectedIsBrush)
            {
                Undo.RegisterSceneUndo("Create a Super Brush");
                openAllMoreOptions = true;
                brushParent = new GameObject("BrushParent");
                //brushParent.hideFlags = HideFlags.DontSave | HideFlags.HideInHierarchy;

                selectedCenterPosition = Vector3.zero;
                int SelectionCount = Selection.gameObjects.Length;
                for (int i = 0; i < SelectionCount; i++)
                {
                    selectedCenterPosition += Selection.gameObjects[i].transform.position;
                }
                selectedCenterPosition = selectedCenterPosition / SelectionCount;

                brushParent.transform.position = selectedCenterPosition;
                CloneObjectsToBrush();

                float maxDist = 1f;
                foreach (Renderer r in brushParent.GetComponentsInChildren<Renderer>())
                    if (Vector3.Distance(brushParent.transform.position, r.transform.position) > maxDist) maxDist = Vector3.Distance(brushParent.transform.position, r.transform.position);
                brushDecal = AntaresDecal.CreateShablon(128, maxDist * 1.5f);
                brushDecal.decalTransform.parent = brushParent.transform;
            }
            else
            {
                DestroyImmediate(brushParent);
            }

        }
        GUI.color = Color.white;

        HideUnhide();

        openAllMoreOptions = GUILayout.Toggle(openAllMoreOptions, new GUIContent(" : Expend More Options", "Open this foldout in all inspectors"));
    }

    protected void CloneObjectsToBrush()
    {
        int selectionCount = Selection.gameObjects.Length;
        brushChilds.Clear();
        brushObjParents.Clear();
        for (int i = 0; i < selectionCount; i++)
        {
            Undo.RegisterSceneUndo("Create Super Brush");
            GameObject baseObject = Selection.gameObjects[i];
            UnityEngine.Object basePrefParent = EditorUtility.GetPrefabParent(baseObject);
            GameObject newGO;
            if (basePrefParent != null)
            {
                newGO = EditorUtility.InstantiatePrefab(basePrefParent) as GameObject;
                newGO.transform.position = baseObject.transform.position;
                newGO.transform.rotation = baseObject.transform.rotation;
                newGO.transform.parent = brushParent.transform;
            }
            else
            {
                newGO = Instantiate(baseObject, baseObject.transform.position, baseObject.transform.rotation) as GameObject;
                newGO.transform.parent = brushParent.transform;
            }
            brushChilds.Add(newGO);
            if (baseObject.transform.parent != null)
            {
                brushObjParents.Add(newGO, baseObject.transform.parent.gameObject);
            }
            brushEmptyObj = new GameObject("brushEmptyObj");
            //brushEmptyObj.hideFlags = HideFlags.HideInHierarchy | HideFlags.DontSave;
            brushEmptyObj.transform.position = new Vector3(100000, 100000, 100000);
            brushEmptyObj.transform.parent = brushParent.transform;
        }
        Selection.activeGameObject = brushEmptyObj;
    }

    Vector3 decalYShift = new Vector3(0, -.1f, 0);

    private Vector3 scenePivot = Vector3.zero;
    void OnSceneGUI()
    {
        AlignCamera();

        if (setPosition)
        {
            if (Event.current.type == EventType.MouseDown)
            {
                RaycastHit hit;
                Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
                hit = (RaycastHit)HandleUtility.RaySnap(ray);
                if (hit.transform != null)
                {
                    Undo.RegisterSceneUndo("Set position by mouse click");
                    targetTransform.position = hit.point;
                    setPosition = false;
                    if (targetTransform.root == null) Selection.activeObject = targetTransform.gameObject;
                    else Selection.activeObject = targetTransform.root.gameObject;
                }
            }
        }

        if (selectedIsBrush)
        {
            RaycastHit hit, hitt;
            RaycastHit hitChild;
            Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
            try
            {
                if (HandleUtility.RaySnap(ray) == null) return;
                hitt = (RaycastHit)HandleUtility.RaySnap(ray);
            }
            catch { return; }
            hit = hitt;
            if (hit.transform != null)
            {
                brushParent.transform.position = hit.point;

                int bChilds = brushChilds.Count;

                for (int i = 0; i < bChilds; i++)
                {
                    GameObject baseObject = brushChilds[i];
                    baseObject.active = false;
                    try
                    {
                        if (Physics.Raycast(baseObject.transform.position + new Vector3(0, 50, 0), Vector3.down, out hitChild, 100)) baseObject.transform.position = hitChild.point;
                    }
                    catch
                    {

                    }
                    baseObject.active = true;
                }

                if (Event.current.type == EventType.MouseDown)
                {
                    GameObject newGO;
                    GameObject newGOParent;
                    Undo.RegisterSceneUndo("Place objects by Super Brush");
                    for (int i = 0; i < bChilds; i++)
                    {
                        GameObject baseObject = brushChilds[i];
                        UnityEngine.Object basePrefParent = EditorUtility.GetPrefabParent(baseObject);

                        newGOParent = null;
                        if (brushObjParents.ContainsKey(baseObject))
                        {
                            newGOParent = brushObjParents[baseObject] as GameObject;
                        }
                        if (basePrefParent != null)
                        {
                            newGO = EditorUtility.InstantiatePrefab(basePrefParent) as GameObject;
                            newGO.transform.position = baseObject.transform.position;
                            newGO.transform.rotation = baseObject.transform.rotation;
                            if (newGOParent != null)
                            {
                                newGO.transform.parent = newGOParent.transform;
                            }
                        }
                        else
                        {
                            newGO = Instantiate(baseObject, baseObject.transform.position, baseObject.transform.rotation) as GameObject;
                            if (newGOParent != null)
                            {
                                newGO.transform.parent = newGOParent.transform;
                            }
                        }
                    }
                }
                Selection.activeGameObject = brushEmptyObj;

                if (brushDecal != null)
                {
                    brushDecal.decalTransform.position = hit.point + decalYShift;
                    AntaresDecal.DrawCircle(brushDecal, 500, 1000);
                }
            }
        }
    }

    public void AlignCamera()
    {
        if (Event.current.type == EventType.MouseDown && Event.current.button == 2)
        {
            SceneView.current.FrameSelected();
        }

        if (Event.current.type == EventType.MouseUp && Event.current.button == 2 && scenePivot != Vector3.zero)
        {
            SceneView.current.pivot = targetTransform.position;
        }
    }

    public static void HelpButton(string title, string hint)
    {
        GUI.enabled = true;
        if (GUILayout.Button("Help", EditorStyles.miniButton, GUILayout.Width(64)))
        {
            EditorUtility.DisplayDialog(title, hint, "OK");
        }
    }
}

[CustomEditor(typeof(AntaresAddon))]
public class AntaresAddonEditor : Editor
{
    private AntaresAddon targetObject;
    private SerializedObject serializedObj;
    string tagToAdd = "Untagged";
    public static AntaresAddonEditor thisScript;
    bool update;
    Color buttonColor;

    void OnEnable()
    {
        targetObject = target as AntaresAddon;
        serializedObj = new SerializedObject(target);
        thisScript = this;
        buttonColor = AntaresEdMainWindow.colorSelected;
        if (AntaresGameObjectCustom.addonController == null) AntaresGameObjectCustom.InitController();
    }

    public override void OnInspectorGUI()
    {
        update = false;
        targetObject = target as AntaresAddon;

        AntaresGameObjectCustom.Routines.DrawHeader("ANTARES ADDON");
        EditorGUILayout.Separator();

        GUILayout.Label("Object Description : ");
        targetObject.descFoldout = EditorGUILayout.Foldout(targetObject.descFoldout, "");
        if (targetObject.descFoldout)
        {
            GUI.SetNextControlName("DateButton");
            GUI.color = buttonColor;
            if (GUILayout.Button("Add Date", EditorStyles.miniButton, GUILayout.Width(64)))
            {
                GUI.FocusControl("DateButton");
                targetObject.objectDescription += "\n";
                targetObject.objectDescription += DateTime.Now;
                update = true;
            }
            GUI.color = Color.white;
            GUILayout.Label("" + DateTime.Now);

            EditorGUILayout.Separator();
            GUILayout.BeginHorizontal(); GUILayout.Space(5);
            targetObject.descriptionScroll = EditorGUILayout.BeginScrollView(targetObject.descriptionScroll, true, true/*, GUILayout.Width(300)*/, GUILayout.Height(256));
            string oldDesc = targetObject.objectDescription;
            GUIStyle style = EditorStyles.textField;
            style.wordWrap = true;
            targetObject.objectDescription = GUILayout.TextArea(targetObject.objectDescription/*, GUILayout.Width(300.0f)*/, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));//GUILayout.Height(AntaresAddonController.GetDescCount()));
            if (oldDesc != targetObject.objectDescription) update = true;
            EditorGUILayout.EndScrollView();
            GUILayout.EndHorizontal();
        }

        GUILayout.Label("Multitags : " + targetObject.MultitagsCount());
        targetObject.multiTagFoldout = EditorGUILayout.Foldout(targetObject.multiTagFoldout, "");
        if (targetObject.multiTagFoldout) DrawMultitagsFoldout();


        if (update)
        {
            serializedObj.Update();
            serializedObj.ApplyModifiedProperties();
        }

        AntaresGameObjectCustom.Routines.DrawFooter();
    }

    private void DrawMultitagsFoldout()
    {
        bool iMInScene = Selection.activeTransform == targetObject.transform, tagAdded = false;
        GUI.enabled = iMInScene;
        EditorGUILayout.BeginHorizontal();

        string tagBefore = tagToAdd;

        tagToAdd = EditorGUILayout.TagField(tagToAdd, GUILayout.Width(128));
        if (tagToAdd != tagBefore && tagToAdd != "Untagged" && tagToAdd != "") tagAdded = true;
        GUI.enabled = !(tagToAdd == "Untagged");
        GUI.color = buttonColor;
        GUILayout.Label("   Select tag to add");

        if(tagAdded)
        {
            Undo.RegisterSceneUndo("Add Multitag");
            if (targetObject.transform.childCount > 0)
            {
                if (EditorUtility.DisplayDialog("Add Multitag to children ?", "If 'YES', the Multitag (" + tagToAdd + ") will be added to all childrens of this object (" + targetObject.name + ")", "YES", "NO"))
                {
                    foreach (Transform t in targetObject.gameObject.GetComponentsInChildren<Transform>())
                    {
                        AntaresAddon a = t.gameObject.GetComponent<AntaresAddon>();
                        if (a == null) a = t.gameObject.AddComponent<AntaresAddon>();
                        a.MultitagAdd(tagToAdd);
                    }
                }
            }
            targetObject.MultitagAdd(tagToAdd);
            update = true;
        }

        AntaresGameObjectCustom.HelpButton("Help", "Add as many tags to single object, as you Wish. You can access to the multitags from AntaresAddon Component on this Game Object ");
        EditorGUILayout.EndHorizontal();

        string[] tagsList = null;
        if (iMInScene)
        {
            targetObject.TryToRestoreList();
            tagsList = targetObject.GetMultitagsList();
        }
        else tagsList = targetObject.GetMultitagsListNoRuntime();

        int count = tagsList.Length;
        EditorGUILayout.Separator();
        GUI.enabled = true;
        GUI.enabled = iMInScene;
        GUI.color = Color.white;
        for (int i = 0; i < count; i++)
        {
            EditorGUILayout.BeginHorizontal();
            GUILayout.Button(tagsList[i], EditorStyles.miniButtonLeft, GUILayout.Width(128));
            if (GUILayout.Button("Delete", EditorStyles.miniButtonRight, GUILayout.Width(64)))
            {
                Undo.RegisterSceneUndo("Remove Multitag");
                if (targetObject.transform.childCount > 0)
                {
                    if (EditorUtility.DisplayDialog("Remove Multitag from children ?",
                                                    "If 'YES', the Multitag (" + tagToAdd +
                                                    ") will be removed from all childrens of this object (" +
                                                    targetObject.name + ")", "YES", "NO"))
                    {
                        foreach (Transform t in targetObject.gameObject.GetComponentsInChildren<Transform>())
                        {
                            AntaresAddon a = t.gameObject.GetComponent<AntaresAddon>();
                            if (a == null) a.MultitagRemove(tagsList[i]);
                        }
                    }
                }
                targetObject.MultitagRemove(tagsList[i]);
                update = true;
            }
            EditorGUILayout.EndHorizontal();
        }

        EditorGUILayout.Separator();
        GUI.enabled = !(count == 0);
        GUI.enabled = iMInScene;
        GUI.color = buttonColor;
        if (GUILayout.Button("Remove All Multitags", EditorStyles.miniButton, GUILayout.Width(128)))
        {
            Undo.RegisterSceneUndo("Remove all Multitags");
            if (targetObject.transform.childCount > 0)
            {
                if (EditorUtility.DisplayDialog("Remove All Multitags from children ?",
                                                "If 'YES', All Multitags will be removed from all childrens of this object (" +
                                                targetObject.name + ")", "YES", "NO"))
                {
                    foreach (Transform t in targetObject.gameObject.GetComponentsInChildren<Transform>())
                    {
                        AntaresAddon a = t.gameObject.GetComponent<AntaresAddon>();
                        if (a == null) a.RemoveAllMultitags();
                    }
                }
            }
            targetObject.RemoveAllMultitags();
            update = true;
        }
        GUI.color = Color.white;
        GUI.enabled = true;
    }
}

[CustomEditor(typeof(AntaresAddonController))]
public class AntaresAddonContEditor : Editor
{
    protected AntaresAddonController controller;
    public static bool tagsFoldout = false;
    protected SerializedObject serializedObj;

    void OnEnable()
    {
        AntaresAddonController ac = target as AntaresAddonController;

        controller = ac;
        serializedObj = new SerializedObject(target);
    }

    public override void OnInspectorGUI()
    {
        serializedObj.Update();
        AntaresGameObjectCustom.Routines.DrawHeader("DO NOT REMOVE THIS OBJECT");

        controller.freeMemoryOnPlay = GUILayout.Toggle(controller.freeMemoryOnPlay, "Free Memory on Play");

        GUILayout.BeginHorizontal();

        GUILayout.Label("Description max lines count : ");
        controller.maxDescLinesCount = EditorGUILayout.IntField(controller.maxDescLinesCount, GUILayout.Width(64));
        GUILayout.EndHorizontal();

        serializedObj.ApplyModifiedProperties();
        AntaresGameObjectCustom.Routines.DrawFooter();
    }
}

[CustomEditor(typeof(AntaresBezierController))]
public class AntaresCurveEditor : Editor
{
    AntaresBezierController targetObject;
    public static bool createPoints = false;
    SerializedObject serializedObject;
    CurvedRoad road;

    void OnEnable()
    {
        targetObject = target as AntaresBezierController;
        serializedObject = new SerializedObject(target);
        road = targetObject.gameObject.GetComponent<CurvedRoad>();
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();

        AntaresGameObjectCustom.Routines.DrawHeader("ANTARES CURVE");

        GUILayout.BeginHorizontal();
        GUILayout.Label("Hide Gizmos");
        targetObject.hideGizmos = EditorGUILayout.Toggle(targetObject.hideGizmos);
        GUILayout.EndHorizontal();

        GUILayout.Label("Control Points : " + targetObject.transform.childCount);
        GUILayout.Label("Curve points Count : " + targetObject.wayPoints.Length);
        GUILayout.Label("Curve Length : " + targetObject.GetCurveLenght().ToString("f2") + " metres");

        EditorGUILayout.BeginHorizontal();
        GUILayout.Label("Smooth steps : ");
        targetObject.curvedPontsCount = EditorGUILayout.IntField(targetObject.curvedPontsCount, GUILayout.Width(64));
        EditorGUILayout.EndHorizontal();

        if (createPoints) GUI.color = AntaresEdWindow.colorSelected;
        if (GUILayout.Button("Create Points by Mouse Click", EditorStyles.miniButton))
        {
            createPoints = !createPoints;
            if (createPoints) AntaresGameObjectCustom.addCurvePointsActivated = targetObject.gameObject;
            else AntaresGameObjectCustom.addCurvePointsDeactivate = true;
        }
        GUI.color = AntaresEdWindow.colorGUI;

        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("Invert Curve", EditorStyles.miniButtonLeft)) targetObject.InvertCurve();
        if (GUILayout.Button("Clear Curve", EditorStyles.miniButtonMid))
        {
            Undo.RegisterSceneUndo("Delete Curve Points List");
            targetObject.ClearCurve();
        }
        if (GUILayout.Button("Reset Curve", EditorStyles.miniButtonRight))
        {
            Undo.RegisterSceneUndo("Reset Spiral parameters");
            targetObject.ResetCurveParameters();
        }
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("Create Spiral", EditorStyles.miniButtonLeft) || GUILayout.Button("Refresh Spiral", EditorStyles.miniButtonRight))
        {
            Undo.RegisterSceneUndo("Create Curve Spiral");
            targetObject.CreateSpiral();
        }
        EditorGUILayout.EndHorizontal();

        GUI.enabled = road == null;
        if (GUILayout.Button("Create Curved Road", EditorStyles.miniButtonLeft)) targetObject.gameObject.AddComponent<CurvedRoad>();
        GUI.enabled = true;

        targetObject.spiralPropFoldout = EditorGUILayout.Foldout(targetObject.spiralPropFoldout, "Spiral Parameters");


        #region spiralPropFoldout
        if (targetObject.spiralPropFoldout)
        {
            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("Spiral Height : ");
            targetObject.spiralHeight = EditorGUILayout.FloatField(targetObject.spiralHeight, GUILayout.Width(64));
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("Bottom Radius : ");
            targetObject.radiusD = EditorGUILayout.FloatField(targetObject.radiusD, GUILayout.Width(64));
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("Up Radius : ");
            targetObject.radiusU = EditorGUILayout.FloatField(targetObject.radiusU, GUILayout.Width(64));
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("Rounds : ");
            targetObject.oborots = EditorGUILayout.FloatField(targetObject.oborots, GUILayout.Width(64));
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("Control Points Count : ");
            targetObject.pointCount = EditorGUILayout.FloatField(targetObject.pointCount, GUILayout.Width(64));
            EditorGUILayout.EndHorizontal();

            targetObject.CW = GUILayout.Toggle(targetObject.CW, "Clock Wise Directon");
        }
        #endregion

        EditorGUILayout.BeginHorizontal();
        if (targetObject.lineRendererCurve != null) GUI.enabled = false;
        if (GUILayout.Button("Add Line Renderer for Curve", EditorStyles.miniButtonLeft))
        {
            Undo.RegisterSceneUndo("Add Line Renderer for Curve");
            LineRenderer lr = targetObject.gameObject.AddComponent<LineRenderer>();
            targetObject.SetLineRendererForCurve(lr);
            targetObject.lineRendererCurve = lr;
            EditorUtility.SetDirty(lr);
        }
        GUI.enabled = true;
        if (targetObject.lineRendererControl != null) GUI.enabled = false;
        if (GUILayout.Button("Add Line Renderer for Controls", EditorStyles.miniButtonRight))
        {
            Undo.RegisterSceneUndo("Add Line Renderer for Controls");
            LineRenderer lr = targetObject.gameObject.AddComponent<LineRenderer>();
            targetObject.SetLineRenderer(lr);
            targetObject.lineRendererControl = lr;
            EditorUtility.SetDirty(lr);
        }
        GUI.enabled = true;
        EditorGUILayout.EndHorizontal();

        serializedObject.ApplyModifiedProperties();
        AntaresGameObjectCustom.Routines.DrawFooter();
    }

    [MenuItem(("Antares/Curve/Create Curve"))]
    static void CreateCurve()
    {
        if(EditorWindow.GetWindow(typeof(AntaresEdWindow)) != null)
        {
            EditorWindow.GetWindow(typeof (AntaresEdWindow)).Close();
        }
        if (AntaresGameObjectCustom.addonController == null) AntaresGameObjectCustom.InitController();
        GameObject curve = new GameObject("Curve", typeof(AntaresAddon), typeof(AntaresBezierController));
        AntaresAddon and = curve.GetComponent<AntaresAddon>();
        and.objectDescription = "New Antares Curve";
        Selection.activeGameObject = curve;
    }

    void OnSceneGUI()
    {
        if (!createPoints) return;
        if (Event.current.type == EventType.MouseDown)
        {
            RaycastHit hit;
            Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
            try
            {
                hit = (RaycastHit)HandleUtility.RaySnap(ray); 
                if (hit.transform != null)
                {
                    Undo.RegisterSceneUndo("Create curve Pont");
                    GameObject point = new GameObject("Point", typeof(AntaresCurvePoint));
                    point.transform.position = hit.point;
                    targetObject.InvertCurve();
                    point.transform.parent = targetObject.transform;
                    targetObject.InvertCurve();

                    EditorGUIUtility.PingObject(targetObject.gameObject);
                    AntaresGameObjectCustom.addCurvePointsActivated = targetObject.gameObject;
                }
            }
            catch 
            {
                Debug.Log("Filed to add Curve point.");
            }
        }
    }
}

[CustomEditor(typeof(AntaresCurvePoint))]
public class AntaresCurvePointEditor : Editor
{
    AntaresCurvePoint targetObject;

    void OnEnable()
    {
        targetObject = target as AntaresCurvePoint;
    }

    public override void OnInspectorGUI()
    {
        AntaresGameObjectCustom.Routines.DrawHeader(GUILayoutUtility.GetLastRect());
        if (GUILayout.Button("Go to the root", EditorStyles.miniButton))
        {
            Selection.activeGameObject = targetObject.transform.parent.gameObject;
        }
        AntaresGameObjectCustom.Routines.DrawFooter();
    }
}

[CustomEditor(typeof(CurvedRoad))]
public class AntaresCurvedRoad : Editor
{
    CurvedRoad targetObject;
    SerializedObject serializedObject;
    bool roadNeedUpdate = false, applyChanges = false;

    void OnEnable()
    {
        targetObject = target as CurvedRoad;
        targetObject.InitFromEditor();
        serializedObject = new SerializedObject(targetObject);
        if (targetObject.road == null) roadNeedUpdate = false;
    }

    [MenuItem("Antares/Curve/Create Curved Road")]
    public static void AddCurvedRoad()
    {
        Selection.activeGameObject.AddComponent<CurvedRoad>();
    }

    [MenuItem("Antares/Curve/Create Curved Road", true)]
    public static bool ValidateAddCurvedRoad()
    {
        if (Selection.activeGameObject == null) return false;
        return Selection.activeGameObject.GetComponent<AntaresBezierController>() != null &&
            Selection.activeGameObject.GetComponent<CurvedRoad>() == null;
    }

    [MenuItem("Antares/Camera/Add Camera Layers Distance component")]
    public static void AddCameraDistance()
    {
        Selection.activeGameObject.AddComponent<CameraLayersDistance>();
    }

    [MenuItem("Antares/Camera/Add Camera Layers Distance component", true)]
    public static bool ValidateAddCameraDistance()
    {
        if (Selection.activeGameObject == null) return false;
        return Selection.activeGameObject.GetComponent<Camera>() != null && 
            Selection.activeGameObject.GetComponent<CameraLayersDistance>() == null;
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        AntaresGameObjectCustom.Routines.DrawHeader("ANTARES CURVED ROAD");

        EditorGUILayout.BeginHorizontal();
        GUI.enabled = targetObject.road == null;
        if (GUILayout.Button("Create Road", EditorStyles.miniButtonLeft))
        {
            targetObject.road = new GameObject("Curve Road");
            targetObject.road.transform.position = targetObject.gameObject.transform.position;
            targetObject.road.transform.parent = targetObject.gameObject.transform;
            targetObject.CreateRoad();
        }
        GUI.enabled = !GUI.enabled;
        if (GUILayout.Button("Delete Road", EditorStyles.miniButtonRight))
        {
            if (EditorUtility.DisplayDialog("Delete Road", "Are you sure ?", "Delete", "Cancel"))
            {
                Undo.RegisterSceneUndo("Delete Curved Road");
                DestroyImmediate(targetObject.road);
                roadNeedUpdate = true;
            }
        }
        EditorGUILayout.EndHorizontal();
        GUI.enabled = targetObject.road;
        if (roadNeedUpdate) GUI.color = AntaresEdMainWindow.colorSelected;
        if (GUILayout.Button("Update Road", EditorStyles.miniButton))
        {
            Undo.RegisterSceneUndo("Curved Road Update");
            DestroyImmediate(targetObject.road);
            targetObject.road = new GameObject("Curve Road");
            targetObject.road.transform.position = targetObject.gameObject.transform.position;
            targetObject.road.transform.parent = targetObject.gameObject.transform;
            targetObject.CreateRoad();
            roadNeedUpdate = applyChanges = false;
        }
        if (GUI.color != Color.white) GUI.color = Color.white;
        GUI.enabled = true;

        EditorGUILayout.BeginHorizontal();
        int oldLayer = targetObject.raycastLayer;
        GUILayout.Label(new GUIContent("Raycast Layer : ", "Select your Terrain layer"));
        targetObject.raycastLayer = EditorGUILayout.LayerField(targetObject.raycastLayer, GUILayout.Width(200));
        if (targetObject.raycastLayer != oldLayer)
        {
            roadNeedUpdate = true;
        }
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal();
        Material oldMaterial = targetObject.roadMaterial;
        GUILayout.Label(new GUIContent("Road Material : ", "Material of the road mesh"));
        targetObject.roadMaterial = EditorGUILayout.ObjectField(targetObject.roadMaterial, typeof(Material), GUILayout.Width(200)) as Material;
        if (targetObject.roadMaterial != oldMaterial) roadNeedUpdate = true;
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal();
        float oldFloat = targetObject.roadWidth;
        GUILayout.Label(new GUIContent("Road Width : ", ""));
        targetObject.roadWidth = EditorGUILayout.FloatField(targetObject.roadWidth, GUILayout.Width(64));
        if (oldFloat != targetObject.roadWidth) roadNeedUpdate = true;

        oldFloat = targetObject.yShift;
        GUILayout.Label(new GUIContent("Y Shift : ", "The height of a roadbed from an terrain surface"));
        targetObject.yShift = EditorGUILayout.FloatField(targetObject.yShift, GUILayout.Width(64));
        if (oldFloat != targetObject.yShift) roadNeedUpdate = true;
        EditorGUILayout.EndHorizontal();

        if (roadNeedUpdate && !applyChanges)
        {
            serializedObject.ApplyModifiedProperties();
            applyChanges = true;
        }

        AntaresGameObjectCustom.Routines.DrawFooter();
    }
}

public static class AntaresDecal
{
    public class Decal
    {
        public Mesh decalMesh = null;
        public MeshFilter decalMeshFilter = null;
        public GameObject decalGameObject = null;
        public Transform decalTransform = null;
        public Transform[] shablon = null;
    }

    public static void DrawCircle(Decal decal, float ray_Shift, float distance)
    {
        if (decal == null || decal.shablon == null) return;
        int count = decal.shablon.Length;
        if (count == 0) return;
        Vector3 rayShift = Vector3.up * ray_Shift, lastPoint = new Vector3(-1, -1, -1), firstPoint = Vector3.zero, upShift = Vector3.up * .25f;
        RaycastHit hit = new RaycastHit();
        for (int i = 0; i < count; i++)
        {
            if (Physics.Raycast(decal.shablon[i].position + rayShift, Vector3.down, out hit, distance))
            {
                Debug.DrawLine(hit.point + upShift, hit.point, Color.green);
                if (lastPoint != new Vector3(-1, -1, -1))
                {
                    Debug.DrawLine(lastPoint, hit.point, Color.cyan);
                    Debug.DrawLine(lastPoint + upShift, hit.point + upShift, Color.cyan);
                }
                else firstPoint = hit.point;
                lastPoint = hit.point;
            }
        }
        try
        {
            Debug.DrawLine(firstPoint, hit.point, Color.cyan);
            Debug.DrawLine(firstPoint + upShift, hit.point + upShift, Color.cyan);
        }
        catch { }
    }

    public static Decal CreateShablon(int circleCount, float radius)
    {
        Decal decal = new Decal();
        decal.decalGameObject = new GameObject("Decal");
        //decal.decalGameObject.hideFlags = HideFlags.DontSave | HideFlags.HideInHierarchy;
        decal.decalTransform = decal.decalGameObject.transform;
        decal.shablon = new Transform[circleCount];
        float stepAngle = 360f / circleCount;
        for (int i = 0; i < circleCount; i++)
        {
            decal.shablon[i] = new GameObject("Decal Point " + i).transform;
            //decal.shablon[i].hideFlags = HideFlags.DontSave | HideFlags.HideInHierarchy;
            decal.shablon[i].Rotate(Vector3.up * (stepAngle * i));
            decal.shablon[i].Translate(0, 0, radius, Space.Self);
            decal.shablon[i].parent = decal.decalTransform;
        }

        return decal;
    }

    public static Decal CreateDecal(Material material)
    {
        Decal decal = new Decal();
        decal.decalGameObject = CreateDecal_9(material);
        decal.decalMeshFilter = decal.decalGameObject.GetComponent<MeshFilter>();
        decal.decalMesh = decal.decalMeshFilter.sharedMesh;
        decal.decalTransform = decal.decalGameObject.transform;

        return decal;
    }

    public static GameObject CreateDecal_9(Material material)
    {
        GameObject decal = new GameObject("Decal");
        MeshFilter mf = decal.AddComponent<MeshFilter>();
        MeshRenderer mr = decal.AddComponent<MeshRenderer>();
        Mesh myMesh = new Mesh();
        int vertCount = 9;
        Color[] colors = new Color[vertCount];
        for (int i = 0; i < vertCount; i++) colors[i] = Color.white;
        myMesh.vertices = new Vector3[vertCount];
        myMesh.triangles = new int[24];
        int[] triangles = new int[myMesh.triangles.Length];
        Vector3[] vertices = new Vector3[vertCount];
        myMesh.uv = new Vector2[vertCount];
        Vector2[] uv = new Vector2[vertCount];

        vertices[0] = Vector3.zero; uv[0] = new Vector2(.5f, .5f);
        vertices[1] = new Vector3(-.5f, 0, 0); uv[1] = new Vector2(0, .5f);
        vertices[2] = new Vector3(-.5f, 0, .5f); uv[2] = new Vector2(0, 1);

        vertices[3] = new Vector3(0, 0, .5f); uv[3] = new Vector2(.5f, 1);
        vertices[4] = new Vector3(.5f, 0, .5f); uv[4] = new Vector2(1, 1);
        vertices[5] = new Vector3(.5f, 0, 0); uv[5] = new Vector2(1, .5f);

        vertices[6] = new Vector3(.5f, 0, -.5f); uv[6] = new Vector2(1, 0);
        vertices[7] = new Vector3(0, 0, -.5f); uv[7] = new Vector2(.5f, 0);
        vertices[8] = new Vector3(-.5f, 0, -.5f); uv[8] = new Vector2(0, 0);

        int step = 1;
        for (int i = 0; i < 8; i++)
        {
            triangles[i * 3] = 0;
            triangles[i * 3 + 1] = step++;
            if (i == 7) triangles[i * 3 + 2] = 1;
            else triangles[i * 3 + 2] = step;
        }

        CurvedRoad.ReverseNormals(myMesh);
        myMesh.vertices = vertices;
        myMesh.uv = uv;
        myMesh.triangles = triangles;
        myMesh.colors = colors;
        mf.sharedMesh = myMesh;
        mf.sharedMesh.RecalculateNormals();
        mf.sharedMesh.RecalculateBounds();
        mf.sharedMesh.name = decal.name + "_mesh";
        mr.material = material;
        decal.renderer.castShadows = decal.renderer.receiveShadows = false;

        return decal;
    }

    public static void UpdateDecalPosition(Decal decal, float shiftfromHit, float shiftForRay, float distance, bool showDebug)
    {
        Transform parent = decal.decalTransform.parent;
        decal.decalTransform.parent = null;
        RaycastHit hit;
        Vector3[] vertices = new Vector3[decal.decalMesh.vertexCount];
        decal.decalMesh.vertices.CopyTo(vertices, 0);

        Vector3 shift = new Vector3(0, shiftfromHit, 0), rayShift = new Vector3(0, shiftForRay, 0);

        for (int i = 0; i < 9; i++)
        {
            if (Physics.Raycast(decal.decalTransform.TransformPoint(vertices[i]) + rayShift, Vector3.down, out hit, distance))
            {
                vertices[i] = decal.decalTransform.InverseTransformPoint(hit.point + shift);
                Debug.DrawLine(decal.decalTransform.TransformPoint(vertices[i]) + rayShift, hit.point, Color.green);
            }
        }

        decal.decalMesh.vertices = vertices;
        decal.decalMesh.RecalculateNormals();
        decal.decalMesh.RecalculateBounds();

        decal.decalTransform.parent = parent;
    }
}

[CustomEditor(typeof(CameraLayersDistance))]
public class AntaresCameraLayersDistance : Editor
{
    SerializedObject serializedObject;
    CameraLayersDistance targetObject;

    void OnEnable()
    {
        targetObject = target as CameraLayersDistance;
        serializedObject = new SerializedObject(targetObject);
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        AntaresGameObjectCustom.Routines.DrawHeader("ANTARES CAMERA LAYERS DISTANCE");

        string layerName = "";
        float[] cullDistance = null;
        if (targetObject.layerCullDistances == null) cullDistance = targetObject.camera.layerCullDistances;
        else cullDistance = targetObject.layerCullDistances;
        bool changed = false;
        float currentDistance = 0;
        float infDistance = Mathf.Infinity;

        for (int i = 0; i < 32; i++)
        {
            layerName = LayerMask.LayerToName(i);
            if ( layerName == "") continue;
            if (targetObject.HasLayer(i))
            {
                GUILayout.BeginHorizontal();
                GUILayout.Button(new GUIContent(layerName, ""), EditorStyles.miniButton, GUILayout.Width(128));
                GUILayout.Label(" Distance : ");
                currentDistance = cullDistance[i];
                if (cullDistance[i] == 0) infDistance = Mathf.Infinity;
                else infDistance = cullDistance[i];
                cullDistance[i] = EditorGUILayout.FloatField(infDistance, GUILayout.Width(64));
                if (currentDistance != cullDistance[i] && cullDistance[i] != Mathf.Infinity) changed = true;
                GUILayout.EndHorizontal();
            }
            if (changed)
            {
                targetObject.camera.layerCullDistances = targetObject.layerCullDistances = cullDistance;
                EditorUtility.SetDirty(targetObject.gameObject);
                serializedObject.ApplyModifiedProperties();
            }
        }
        AntaresGameObjectCustom.Routines.DrawFooter();
    }
}

[CustomEditor(typeof(FixedJoint))]
public class FixedJointEditor : Editor
{
    [DrawGizmo (GizmoType.Selected | GizmoType.Active)]
    static void RenderFJGizmo (FixedJoint fj, GizmoType gizmoType)
    {
        if (fj.rigidbody == null) return;
        Vector3 srcPos = fj.transform.TransformPoint(fj.rigidbody.centerOfMass);
        Gizmos.color = Color.green;
        Gizmos.DrawWireSphere(srcPos, .05f);
        Gizmos.color = Color.white;
        Gizmos.DrawIcon(fj.transform.position, string.Format("{0}KLIK_10.tif", Curve.pathToGizmos));
        if (fj.connectedBody == null) return;

        Gizmos.color = Color.cyan;
        Vector3 targetPos = fj.connectedBody.transform.TransformPoint(fj.connectedBody.centerOfMass);
        Gizmos.DrawWireSphere( targetPos, .03f);
        Gizmos.color = Color.yellow;
        Gizmos.DrawLine(srcPos, targetPos);

        Gizmos.color = Color.gray;
        GizmoDrawBounds(fj.connectedBody.gameObject.GetComponent<MeshFilter>().sharedMesh, fj.connectedBody.transform);
        Gizmos.color = Color.white;
    }

    public static void GizmoDrawBounds(Mesh mesh, Transform tr)
    {
        if (mesh == null) return;

        int count = mesh.triangles.Length;
        for(int i = 1; i < count; i++)
        {
            Gizmos.DrawLine(tr.TransformPoint(mesh.vertices[mesh.triangles[i]]), tr.TransformPoint(mesh.vertices[mesh.triangles[i-1]]));
        }
    }
}

[CustomEditor(typeof(HingeJoint))]
public class HingeJointEditor : Editor
{
    [DrawGizmo(GizmoType.Selected | GizmoType.Pickable)]
    static void RenderHJGizmo(HingeJoint hj, GizmoType gizmoType)
    {
        if (hj.rigidbody == null) return;
        Vector3 srcPos = hj.transform.TransformPoint(hj.anchor);
        Gizmos.color = Color.green;
        Gizmos.DrawWireSphere(srcPos, .05f);
        Gizmos.color = Color.white;
        Gizmos.DrawIcon(srcPos, string.Format("{0}KLIK_10.tif", Curve.pathToGizmos));
        if (hj.connectedBody == null) return;

        Gizmos.color = Color.cyan;
        Vector3 targetPos = hj.connectedBody.transform.TransformPoint(hj.connectedBody.centerOfMass);
        Gizmos.DrawWireSphere(targetPos, .03f);
        Gizmos.color = Color.yellow;
        Gizmos.DrawLine(srcPos, targetPos);
    }
}