using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using Object = UnityEngine.Object;

namespace Assets.CreateScriptDialog.Editor
{
    public class NewScriptWindow : EditorWindow
    {
        private const int kButtonWidth = 120;
        private const int kLabelWidth = 85;
        private const string kLanguageEditorPrefName = "NewScriptLanguage";
        private const string kTemplatePath = "CreateScriptDialog/SmartScriptTemplates";
        private const string kResourcesTemplatePath = "Resources/SmartScriptTemplates";
        private const string kMonoBehaviourName = "MonoBehaviour";
        private const string kPlainClassName = "Empty Class";
        private const string kCustomEditorClassName = "Editor";
        private const string kTempEditorClassPrefix = "E:";
        private const string kNoTemplateString = "No Template Found";
        // char array can't be const for compiler reasons but this should still be treated as such.
        private readonly char[] kInvalidPathChars = {'<', '>', ':', '"', '|', '?', '*', (char)0};
        private readonly char[] kPathSepChars = {'/', '\\'};
	
        private readonly ScriptPrescription m_ScriptPrescription;
        private string m_BaseClass;
        private string m_CustomEditorTargetClassName = string.Empty;
        private bool m_IsEditorClass;
        private bool m_IsCustomEditor;
        private bool m_FocusTextFieldNow = true;
        private GameObject m_GameObjectToAddTo;
        private string m_Directory = string.Empty;
        private Vector2 m_PreviewScroll;
        private Vector2 m_OptionsScroll;
        private bool m_ClearKeyboardControl;
	
        private int m_TemplateIndex;
        private string[] m_TemplateNames;
	
        class Styles
        {
            public readonly GUIContent m_WarningContent = new GUIContent (string.Empty);
            public readonly GUIStyle m_PreviewBox = new GUIStyle ("OL Box");
            public readonly GUIStyle m_PreviewTitle = new GUIStyle ("OL Title");
            public readonly GUIStyle m_LoweredBox = new GUIStyle ("TextField");
            public readonly GUIStyle m_HelpBox = new GUIStyle ("helpbox");
            public Styles ()
            {
                m_LoweredBox.padding = new RectOffset (1, 1, 1, 1);
            }
        }
        private static Styles m_Styles;
	
        private static string GetAbsoluteBuiltinTemplatePath ()
        {
            return Path.Combine (EditorApplication.applicationContentsPath, kResourcesTemplatePath);
        }
	
        private static string GetAbsoluteCustomTemplatePath ()
        {
            return Path.Combine (Application.dataPath, kTemplatePath);
        }
	
        private void UpdateTemplateNamesAndTemplate ()
        {
            // Remember old selected template name
            string oldSelectedTemplateName = null;
            if (m_TemplateNames != null && m_TemplateNames.Length > 0)
                oldSelectedTemplateName = m_TemplateNames[m_TemplateIndex];
		
            // Get new template names
            m_TemplateNames = GetTemplateNames ();
		
            // Select template
            if (m_TemplateNames.Length == 0)
            {
                m_ScriptPrescription.m_Template = kNoTemplateString;
                m_BaseClass = null;
            }
            else
            {
                if (oldSelectedTemplateName != null && m_TemplateNames.Contains(oldSelectedTemplateName))
                    m_TemplateIndex = m_TemplateNames.ToList().IndexOf(oldSelectedTemplateName);
                else
                    m_TemplateIndex = 0;
                m_ScriptPrescription.m_Template = GetTemplate (m_TemplateNames[m_TemplateIndex]);
            }
		
            HandleBaseClass ();
        }
	
        private void AutomaticHandlingOnChangeTemplate ()
        {
            // Add or remove "Editor" from directory path
            if (m_IsEditorClass)
            {
                if (InvalidTargetPathForEditorScript ())
                    m_Directory = Path.Combine (m_Directory, "Editor");
            }
            else if (m_Directory.EndsWith ("Editor"))
            {
                m_Directory = m_Directory.Substring (0, m_Directory.Length-6).TrimEnd (kPathSepChars);
            }
		
            // Move keyboard focus to relevant field
            if (m_IsCustomEditor)
                m_FocusTextFieldNow = true;
        }
	
        private string GetBaseClass (string templateContent)
        {
            var firstLine = templateContent.Substring (0, templateContent.IndexOf ("\n", StringComparison.Ordinal));
            if (firstLine.Contains ("BASECLASS"))
            {
                var baseClass = firstLine.Substring (10).Trim ();
                if (baseClass != string.Empty)
                    return baseClass;
            }
            return null;
        }
	
        private static bool GetFunctionIsIncluded (string baseClassName, string functionName, bool includeByDefault)
        {
            var prefName = "FunctionData_" + (baseClassName != null ? baseClassName + "_" : string.Empty) + functionName;
            return EditorPrefs.GetBool (prefName, includeByDefault);
        }
	
        private static void SetFunctionIsIncluded (string baseClassName, string functionName, bool include)
        {
            var prefName = "FunctionData_" + (baseClassName != null ? baseClassName + "_" : string.Empty) + functionName;
            EditorPrefs.SetBool (prefName, include);
        }
	
        private void HandleBaseClass ()
        {
            if (m_TemplateNames.Length == 0)
            {
                m_BaseClass = null;
                return;
            }
		
            // Get base class
            m_BaseClass = GetBaseClass (m_ScriptPrescription.m_Template);
		
            // If base class was found, strip first line from template
            if (m_BaseClass != null)
                m_ScriptPrescription.m_Template =
                    m_ScriptPrescription.m_Template.Substring (m_ScriptPrescription.m_Template.IndexOf ("\n", StringComparison.Ordinal)+1);
		
            m_IsEditorClass = IsEditorClass (m_BaseClass);
            m_IsCustomEditor = (m_BaseClass == kCustomEditorClassName);
            m_ScriptPrescription.m_StringReplacements.Clear ();
		
            // Try to find function file first in custom templates folder and then in built-in
            var functionDataFilePath = Path.Combine (GetAbsoluteCustomTemplatePath (), m_BaseClass+".functions.txt");
            if (!File.Exists (functionDataFilePath))
                functionDataFilePath = Path.Combine (GetAbsoluteBuiltinTemplatePath (), m_BaseClass+".functions.txt");
		
            if (!File.Exists (functionDataFilePath))
            {
                m_ScriptPrescription.m_Functions = null;
            }
            else
            {
                var reader = new StreamReader (functionDataFilePath);
                var functionList = new List<FunctionData> ();
                var lineNr = 1;
                while (!reader.EndOfStream)
                {
                    var functionLine = reader.ReadLine ();
                    var functionLineWhole = functionLine;
                    try
                    {
                        if (functionLine.Substring (0, 7).ToLower () == "header ")
                        {
                            functionList.Add (new FunctionData (functionLine.Substring (7)));
                            continue;
                        }
					
                        var function = new FunctionData ();
					
                        var defaultInclude = false;
                        if (functionLine.Substring (0, 8) == "DEFAULT ")
                        {
                            defaultInclude = true;
                            functionLine = functionLine.Substring (8);
                        }
					
                        if (functionLine.Substring (0, 9) == "override ")
                        {
                            function.isVirtual = true;
                            functionLine = functionLine.Substring (9);
                        }
					
                        var returnTypeString = GetStringUntilSeperator (ref functionLine, " ");
                        function.returnType = (returnTypeString == "void" ? null : returnTypeString);
                        function.name = GetStringUntilSeperator (ref functionLine, "(");
                        var parameterString = GetStringUntilSeperator (ref functionLine, ")");
                        if (function.returnType != null)
                            function.returnDefault = GetStringUntilSeperator (ref functionLine, ";");
                        function.comment = functionLine;
					
                        var parameterStrings = parameterString.Split (new[]{','}, StringSplitOptions.RemoveEmptyEntries);
                        var parameterList = new List<ParameterData> ();
                        for (var i=0; i<parameterStrings.Length; i++)
                        {
                            var paramSplit = parameterStrings[i].Trim().Split (' ');
                            parameterList.Add (new ParameterData (paramSplit[1], paramSplit[0]));
                        }
                        function.parameters = parameterList.ToArray ();
					
                        function.include = GetFunctionIsIncluded (m_BaseClass, function.name, defaultInclude);
					
                        functionList.Add (function);
                    }
                    catch (Exception e)
                    {
                        Debug.LogWarning ("Malformed function line: \""+functionLineWhole+"\"\n  at "+functionDataFilePath+":"+lineNr+"\n"+e);
                    }
                    lineNr++;
                }
                m_ScriptPrescription.m_Functions = functionList.ToArray ();
			
            }
        }
	
        private string GetStringUntilSeperator (ref string source, string sep)
        {
            var index = source.IndexOf (sep, StringComparison.Ordinal);
            var result = source.Substring (0, index).Trim ();
            source = source.Substring (index + sep.Length).Trim (' ');
            return result;
        }
	
        private string GetTemplate (string nameWithoutExtension)
        {
            var path = Path.Combine (GetAbsoluteCustomTemplatePath (), nameWithoutExtension + "." + extension + ".txt");
            if (File.Exists (path))
                return File.ReadAllText (path);
		
            path = Path.Combine (GetAbsoluteBuiltinTemplatePath (), nameWithoutExtension + "." + extension + ".txt");
            return File.Exists (path) ? File.ReadAllText (path) : kNoTemplateString;
        }
	
        private string GetTemplateName ()
        {
            return m_TemplateNames.Length == 0 ? kNoTemplateString : m_TemplateNames[m_TemplateIndex];
        }

        // Custom comparer to sort templates alphabetically,
        // but put MonoBehaviour and Plain Class as the first two
        private class TemplateNameComparer : IComparer<string>
        {
            private static int GetRank (string s)
            {
                switch (s)
                {
                    case kMonoBehaviourName:
                        return 0;
                    case kPlainClassName:
                        return 1;
                }
                return s.StartsWith(kTempEditorClassPrefix) ? 100 : 2;
            }
		
            public int Compare (string x, string y)
            {
                var rankX = GetRank (x);
                var rankY = GetRank (y);
                return rankX == rankY ? string.Compare (x, y, StringComparison.Ordinal) : rankX.CompareTo (rankY);
            }
        }
	
        private string[] GetTemplateNames ()
        {
            var templates = new List<string> ();
		
            // Get all file names of custom templates
            if (Directory.Exists (GetAbsoluteCustomTemplatePath ()))
                templates.AddRange (Directory.GetFiles (GetAbsoluteCustomTemplatePath ()));
		
            // Get all file names of built-in templates
            if (Directory.Exists (GetAbsoluteBuiltinTemplatePath ()))
                templates.AddRange (Directory.GetFiles (GetAbsoluteBuiltinTemplatePath ()));
		
            if (templates.Count == 0)
                return new string[0];
		
            // Filter and clean up list
            templates = templates
                .Distinct ()
                .Where (f => (f.EndsWith ("."+extension+".txt")))
                .Select (f => Path.GetFileNameWithoutExtension (f.Substring (0, f.Length-4)))
                .ToList ();
		
            // Determine which scripts have editor class base class
            for (var i=0; i<templates.Count; i++)
            {
                var templateContent = GetTemplate(templates[i]);
                if (IsEditorClass (GetBaseClass (templateContent)))
                    templates[i] = kTempEditorClassPrefix + templates[i];
            }
		
            // Order list
            templates = templates
                .OrderBy (f => f, new TemplateNameComparer ())
                .ToList ();
		
            // Insert separator before first editor script template
            var inserted = false;
            for (var i=0; i<templates.Count; i++)
            {
                if (!templates[i].StartsWith(kTempEditorClassPrefix)) continue;
                templates[i] = templates[i].Substring (kTempEditorClassPrefix.Length);
                if (inserted) continue;
                templates.Insert(i, string.Empty);
                inserted = true;
            }
		
            // Return list
            return templates.ToArray();
        }

        private string extension
        {
            get
            {
                switch (m_ScriptPrescription.m_Lang)
                {
                    case Language.CSharp:
                        return "cs";
                    case Language.JavaScript:
                        return "js";
                    case Language.Boo:
                        return "boo";
                    default:
                        throw new ArgumentOutOfRangeException ();
                }
            }
        }
	
        [MenuItem ("Component/Scripts/New Script...", false, 0)]
        public static void OpenFromComponentMenu ()
        {
            Init ();
        }
	
        [MenuItem ("Component/Scripts/New Script...", true, 0)]
        public static bool OpenFromComponentMenuValidation ()
        {
            return (Selection.activeObject is GameObject);
        }
	
        [MenuItem ("Assets/Create/Script...", false, 100)]
        public static void OpenFromAssetsMenu ()
        {
            Init ();
        }
	
        private static void Init ()
        {
            GetWindow<NewScriptWindow> (true, "Create Script");
        }

        public NewScriptWindow ()
        {
            // Large initial size
            position = new Rect (50, 50, 770, 500);
            // But allow to scale down to smaller size
            minSize = new Vector2 (550, 400);
		
            m_ScriptPrescription = new ScriptPrescription ();
        }

        public void OnEnable ()
        {
            m_ScriptPrescription.m_Lang = (Language)EditorPrefs.GetInt (kLanguageEditorPrefName, 0);
            UpdateTemplateNamesAndTemplate ();
            OnSelectionChange ();
        }

        public void OnGUI ()
        {
            if (m_Styles == null)
                m_Styles = new Styles ();
		
#pragma warning disable 618
            EditorGUIUtility.LookLikeControls (85);
#pragma warning restore 618
		
            if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return && CanCreate ())
                Create ();
		
            EditorGUILayout.BeginHorizontal ();
            {
                GUILayout.Space (10);
			
                PreviewGUI ();
                GUILayout.Space (10);
			
                EditorGUILayout.BeginVertical ();
                {
                    OptionsGUI ();
				
                    GUILayout.Space (10);
                    //GUILayout.FlexibleSpace ();
				
                    CreateAndCancelButtonsGUI ();
                } EditorGUILayout.EndVertical ();
			
                GUILayout.Space (10);
            } EditorGUILayout.EndHorizontal ();
		
            GUILayout.Space (10);
		
            // Clear keyboard focus if clicking a random place inside the dialog,
            // or if ClearKeyboardControl flag is set.
            if (!m_ClearKeyboardControl && (Event.current.type != EventType.MouseDown || Event.current.button != 0))
                return;
            GUIUtility.keyboardControl = 0;
            m_ClearKeyboardControl = false;
            Repaint ();
        }
	
        private bool CanCreate ()
        {
            return m_ScriptPrescription.m_ClassName.Length > 0 &&
                   !File.Exists (TargetPath ()) &&
                   !ClassAlreadyExists () &&
                   !ClassNameIsInvalid () &&
                   !TargetClassDoesNotExist () &&
                   !TargetClassIsNotValidType () &&
                   !InvalidTargetPath () &&
                   !InvalidTargetPathForEditorScript ();
        }
	
        private void Create ()
        {
            CreateScript ();

            if (CanAddComponent ())
                InternalEditorUtility.AddScriptComponentUnchecked (m_GameObjectToAddTo,
                    AssetDatabase.LoadAssetAtPath (TargetPath (), typeof (MonoScript)) as MonoScript);
		
            Close ();
            GUIUtility.ExitGUI ();
        }
	
        private void CreateAndCancelButtonsGUI ()
        {
            var canCreate = CanCreate ();

            // Create string to tell the user what the problem is
            var blockReason = string.Empty;
            if (!canCreate && m_ScriptPrescription.m_ClassName != string.Empty)
            {
                if (File.Exists (TargetPath ()))
                    blockReason = "A script called \"" + m_ScriptPrescription.m_ClassName + "\" already exists at that path.";
                else if (ClassAlreadyExists ())
                    blockReason = "A class called \"" + m_ScriptPrescription.m_ClassName + "\" already exists.";
                else if (ClassNameIsInvalid ())
                    blockReason = "The script name may only consist of a-z, A-Z, 0-9, _.";
                else if (TargetClassDoesNotExist ())
                    if (m_CustomEditorTargetClassName == string.Empty)
                        blockReason = "Fill in the script component to make an editor for.";
                    else
                        blockReason = "A class called \"" + m_CustomEditorTargetClassName + "\" could not be found.";
                else if (TargetClassIsNotValidType ())
                    blockReason = "The class \"" + m_CustomEditorTargetClassName + "\" is not of type UnityEngine.Object.";
                else if (InvalidTargetPath ())
                    blockReason = "The folder path contains invalid characters.";
                else if (InvalidTargetPathForEditorScript ())
                    blockReason = "Editor scripts should be stored in a folder called Editor.";
            }
		
            // Warning about why the script can't be created
            if (blockReason != string.Empty)
            {
                m_Styles.m_WarningContent.text = blockReason;
                GUILayout.BeginHorizontal (m_Styles.m_HelpBox);
                {
                    GUILayout.Label (m_Styles.m_WarningContent, EditorStyles.wordWrappedMiniLabel);
                } GUILayout.EndHorizontal ();
            }

            // Cancel and create buttons
            GUILayout.BeginHorizontal ();
            {
                GUILayout.FlexibleSpace ();

                if (GUILayout.Button ("Cancel", GUILayout.Width (kButtonWidth)))
                {
                    Close ();
                    GUIUtility.ExitGUI ();
                }
			
                var guiEnabledTemp = GUI.enabled;
                GUI.enabled = canCreate;
                if (GUILayout.Button (GetCreateButtonText (), GUILayout.Width (kButtonWidth)))
                {
                    Create ();
                }
                GUI.enabled = guiEnabledTemp;
            } GUILayout.EndHorizontal ();
        }
	
        private bool CanAddComponent ()
        {
            return (m_GameObjectToAddTo != null && m_BaseClass == kMonoBehaviourName);
        }
	
        private void OptionsGUI ()
        {
            EditorGUILayout.BeginVertical (GUILayout.ExpandWidth (true));
            {
                GUILayout.BeginHorizontal ();
                {
                    NameGUI ();
                    LanguageGUI ();
                } GUILayout.EndHorizontal ();
			
                GUILayout.Space (10);
			
                TargetPathGUI ();
			
                GUILayout.Space (20);
			
                TemplateSelectionGUI ();
			
                if (GetTemplateName () == kMonoBehaviourName)
                {
                    GUILayout.Space (10);
                    AttachToGUI ();
                }
			
                if (m_IsCustomEditor)
                {
                    GUILayout.Space (10);
                    CustomEditorTargetClassNameGUI ();
                }
			
                GUILayout.Space (10);
			
                FunctionsGUI ();
            } EditorGUILayout.EndVertical ();
        }
	
        private bool FunctionHeader (string header, bool expandedByDefault)
        {
            GUILayout.Space (5);
            var expanded = GetFunctionIsIncluded (m_BaseClass, header, expandedByDefault);
            var expandedNew = GUILayout.Toggle (expanded, header, EditorStyles.foldout);
            if (expandedNew != expanded)
                SetFunctionIsIncluded (m_BaseClass, header, expandedNew);
            return expandedNew;
        }
	
        private void FunctionsGUI ()
        {
            if (m_ScriptPrescription.m_Functions == null)
            {
                GUILayout.FlexibleSpace ();
                return;
            }

            EditorGUILayout.BeginHorizontal ();
            {
                GUILayout.Label ("Functions", GUILayout.Width (kLabelWidth - 4));
			
                EditorGUILayout.BeginVertical (m_Styles.m_LoweredBox);
                m_OptionsScroll = EditorGUILayout.BeginScrollView (m_OptionsScroll);
                {
                    var expanded = FunctionHeader ("General", true);
				
                    for (var i = 0; i < m_ScriptPrescription.m_Functions.Length; i++)
                    {
                        var func = m_ScriptPrescription.m_Functions[i];
					
                        if (func.name == null)
                        {
                            expanded = FunctionHeader (func.comment, false);
                        }
                        else if (expanded)
                        {
                            var toggleRect = GUILayoutUtility.GetRect (GUIContent.none, EditorStyles.toggle);
                            toggleRect.x += 15;
                            toggleRect.width -= 15;
                            var include = GUI.Toggle (toggleRect, func.include, new GUIContent (func.name, func.comment));
                            if (include != func.include)
                            {
                                m_ScriptPrescription.m_Functions[i].include = include;
                                SetFunctionIsIncluded (m_BaseClass, func.name, include);
                            }
                        }
                    }
                } EditorGUILayout.EndScrollView ();
                EditorGUILayout.EndVertical ();
			
            } EditorGUILayout.EndHorizontal ();
        }

        private void AttachToGUI ()
        {
            GUILayout.BeginHorizontal ();
            {
                m_GameObjectToAddTo = EditorGUILayout.ObjectField ("Attach to", m_GameObjectToAddTo, typeof (GameObject), true) as GameObject;

                if (ClearButton ())
                    m_GameObjectToAddTo = null;
            } GUILayout.EndHorizontal ();
		
            HelpField ("Click a GameObject or Prefab to select.");
        }
	
        private void SetClassNameBasedOnTargetClassName ()
        {
            if (m_CustomEditorTargetClassName == string.Empty)
                m_ScriptPrescription.m_ClassName = string.Empty;
            else
                m_ScriptPrescription.m_ClassName = m_CustomEditorTargetClassName + "Editor";
        }
	
        private void CustomEditorTargetClassNameGUI ()
        {
            GUI.SetNextControlName ("CustomEditorTargetClassNameField");
		
            var newName = EditorGUILayout.TextField ("Editor for", m_CustomEditorTargetClassName);
            m_ScriptPrescription.m_StringReplacements["$TargetClassName"] = newName;
            if (newName != m_CustomEditorTargetClassName)
            {
                m_CustomEditorTargetClassName = newName;
                SetClassNameBasedOnTargetClassName ();
            }
		
            if (m_FocusTextFieldNow && Event.current.type == EventType.repaint)
            {
                GUI.FocusControl ("CustomEditorTargetClassNameField");
                m_FocusTextFieldNow = false;
                Repaint ();
            }
		
            HelpField ("Script component to make an editor for.");
        }
	
        private void TargetPathGUI ()
        {
            m_Directory = EditorGUILayout.TextField ("Save in", m_Directory, GUILayout.ExpandWidth (true));
		
            HelpField ("Click a folder in the Project view to select.");
        }

        private bool ClearButton ()
        {
            return GUILayout.Button ("Clear", EditorStyles.miniButton, GUILayout.Width (40));
        }

        private void TemplateSelectionGUI ()
        {
            m_TemplateIndex = Mathf.Clamp (m_TemplateIndex, 0, m_TemplateNames.Length-1);
            var templateIndexNew = EditorGUILayout.Popup ("Template", m_TemplateIndex, m_TemplateNames);
            if (templateIndexNew != m_TemplateIndex)
            {
                m_TemplateIndex = templateIndexNew;
                UpdateTemplateNamesAndTemplate ();
                AutomaticHandlingOnChangeTemplate ();
            }
        }

        private void NameGUI ()
        {
            GUI.SetNextControlName ("ScriptNameField");
            m_ScriptPrescription.m_ClassName = EditorGUILayout.TextField ("Name", m_ScriptPrescription.m_ClassName);
		
            if (m_FocusTextFieldNow && !m_IsCustomEditor && Event.current.type == EventType.repaint)
            {
                GUI.FocusControl ("ScriptNameField");
                m_FocusTextFieldNow = false;
            }
        }

        private void LanguageGUI ()
        {
            var langNew = (Language) EditorGUILayout.EnumPopup (m_ScriptPrescription.m_Lang, GUILayout.Width (80));

            if (langNew != m_ScriptPrescription.m_Lang)
            {
                m_ScriptPrescription.m_Lang = langNew;
                EditorPrefs.SetInt (kLanguageEditorPrefName, (int)langNew);
                UpdateTemplateNamesAndTemplate ();
                AutomaticHandlingOnChangeTemplate ();
            }
        }

        private void PreviewGUI ()
        {
            EditorGUILayout.BeginVertical (GUILayout.Width (Mathf.Max (position.width * 0.4f, position.width - 380f)));
            {
                // Reserve room for preview title
                var previewHeaderRect = GUILayoutUtility.GetRect (new GUIContent ("Preview"), m_Styles.m_PreviewTitle);
			
                // Secret! Toggle curly braces on new line when double clicking the script preview title
                var evt = Event.current;
                if (evt.type == EventType.MouseDown && evt.clickCount == 2 && previewHeaderRect.Contains (evt.mousePosition))
                {
                    EditorPrefs.SetBool ("CurlyBracesOnNewLine", !EditorPrefs.GetBool ("CurlyBracesOnNewLine"));
                    Repaint ();
                }

                // Preview scroll view
                m_PreviewScroll = EditorGUILayout.BeginScrollView (m_PreviewScroll, m_Styles.m_PreviewBox);
                {
                    EditorGUILayout.BeginHorizontal ();
                    {
                        // Tiny space since style has no padding in right side
                        GUILayout.Space (5);
		
                        // Preview text itself
                        var previewStr = new NewScriptGenerator (m_ScriptPrescription).ToString ();
                        var r = GUILayoutUtility.GetRect (
                            new GUIContent (previewStr),
                            EditorStyles.miniLabel,
                            GUILayout.ExpandWidth (true),
                            GUILayout.ExpandHeight (true));
                        EditorGUI.SelectableLabel (r, previewStr, EditorStyles.miniLabel);
                    } EditorGUILayout.EndHorizontal ();
                } EditorGUILayout.EndScrollView ();

                // Draw preview title after box itself because otherwise the top row
                // of pixels of the slider will overlap with the title
                GUI.Label (previewHeaderRect, new GUIContent ("Preview Script"), m_Styles.m_PreviewTitle);
			
                GUILayout.Space (4);
            } EditorGUILayout.EndVertical ();
        }
	
        private bool InvalidTargetPath ()
        {
            if (m_Directory.IndexOfAny (kInvalidPathChars) >= 0)
                return true;
            if (TargetDir ().Split (kPathSepChars, StringSplitOptions.None).Contains (string.Empty))
                return true;
            return false;
        }
	
        private bool InvalidTargetPathForEditorScript ()
        {
            return m_IsEditorClass && !m_Directory.ToLower ().Split (kPathSepChars).Contains ("editor");
        }

        private bool IsFolder (Object obj)
        {
            return Directory.Exists (AssetDatabase.GetAssetPath (obj));
        }
	
        private void HelpField (string helpText)
        {
            GUILayout.BeginHorizontal ();
            GUILayout.Label (string.Empty, GUILayout.Width (kLabelWidth - 4));
            GUILayout.Label (helpText, m_Styles.m_HelpBox);
            GUILayout.EndHorizontal ();
        }

        private string TargetPath ()
        {
            return Path.Combine (TargetDir (), m_ScriptPrescription.m_ClassName + "." + extension);
        }

        private string TargetDir ()
        {
            return Path.Combine ("Assets", m_Directory.Trim (kPathSepChars));
        }
	
        private bool ClassNameIsInvalid ()
        {
            return !System.CodeDom.Compiler.CodeGenerator.IsValidLanguageIndependentIdentifier (
                m_ScriptPrescription.m_ClassName);
        }
	
        private bool ClassExists (string className)
        {
            return AppDomain.CurrentDomain.GetAssemblies ()
                .Any (a => a.GetType (className, false) != null);
        }
	
        private bool ClassAlreadyExists ()
        {
            if (m_ScriptPrescription.m_ClassName == string.Empty)
                return false;
            return ClassExists (m_ScriptPrescription.m_ClassName);
        }
	
        private bool TargetClassDoesNotExist ()
        {
            if (!m_IsCustomEditor)
                return false;
            if (m_CustomEditorTargetClassName == string.Empty)
                return true;
            return !ClassExists (m_CustomEditorTargetClassName);
        }
	
        private bool TargetClassIsNotValidType ()
        {
            if (!m_IsCustomEditor)
                return false;
            if (m_CustomEditorTargetClassName == string.Empty)
                return true;
            return AppDomain.CurrentDomain.GetAssemblies ()
                .All (a => !typeof(Object).IsAssignableFrom(a.GetType (m_CustomEditorTargetClassName, false)));
        }

        private string GetCreateButtonText ()
        {
            return CanAddComponent () ? "Create and Attach" : "Create";
        }
	
        private void CreateScript ()
        {
            if (!Directory.Exists (TargetDir ()))
                Directory.CreateDirectory (TargetDir ());

            var writer = new StreamWriter (TargetPath ());
            writer.Write (new NewScriptGenerator (m_ScriptPrescription).ToString ());
            writer.Close ();
            writer.Dispose ();
            AssetDatabase.Refresh ();
        }
	
        private void OnSelectionChange ()
        {
            m_ClearKeyboardControl = true;
		
            if (Selection.activeObject == null)
                return;
		
            if (IsFolder (Selection.activeObject))
            {
                m_Directory = AssetPathWithoutAssetPrefix (Selection.activeObject);
                if (m_IsEditorClass && InvalidTargetPathForEditorScript ())
                {
                    m_Directory = Path.Combine (m_Directory, "Editor");
                }
            }
            else if (Selection.activeGameObject != null)
            {
                m_GameObjectToAddTo = Selection.activeGameObject;
            }
            else if (m_IsCustomEditor && Selection.activeObject is MonoScript)
            {
                m_CustomEditorTargetClassName = Selection.activeObject.name;
                SetClassNameBasedOnTargetClassName ();
            }
			
            Repaint ();
        }
	
        private static string AssetPathWithoutAssetPrefix (Object obj)
        {
            return AssetDatabase.GetAssetPath (obj).Substring (7);
        }
	
        private static bool IsEditorClass (string className)
        {
            return className != null && GetAllClasses ("UnityEditor").Contains(className);
        }

        /// Method to populate a list with all the class in the namespace provided by the user
        static List<string> GetAllClasses (string nameSpace)
        {
            // Get the UnityEditor assembly
            var asm = Assembly.GetAssembly (typeof (UnityEditor.Editor));
		
            // Create a list for the namespaces
            var namespaceList = new List<string> ();
		
            // Create a list that will hold all the classes the suplied namespace is executing
            var returnList = new List<string> ();
		
            foreach (var type in asm.GetTypes ())
            {
                if (type.Namespace == nameSpace)
                    namespaceList.Add (type.Name);
            }
		
            // Now loop through all the classes returned above and add them to our classesName list
            foreach (var className in namespaceList)
                returnList.Add (className);
		
            return returnList;
        }
    }
}
