context: using UnityEngine;
  using UnityEditor;
  using System.Collections;
  using System.Collections.Generic;
  using System;
  using System.Text.RegularExpressions;
  using System.Reflection;
  using System.Linq;
  using System.IO;
  
  public class AMCodeView : EditorWindow {
      public static AMCodeView window = null;
  
      public AMOptionsFile oData;
      public AnimatorData aData;
  
      private string[] selStrings = new string[] { "C#", "Javascript" };
      private Vector2 scrollView;
      public static Dictionary<string, string> varNameDictionary;
      private static bool isInspectorOpen = false;
      private string codeCache = "";
      private GUISkin skin = null;
      private string cachedSkinName = null;
  
      private Texture texRightArrow;// inspector right arrow
      private Texture texLeftArrow;	// inspector left arrow
      private Vector2 scrollPos;
      private static Dictionary<int, bool> dictTracks = new Dictionary<int, bool>();
      private static Dictionary<int, bool> dictGroups = new Dictionary<int, bool>();
      private static bool shouldRefresh = false;
      private Vector2 startScrubMousePosition;
      private Vector2 currentMousePosition;
      private float start_width_inspector_open;
      private static float width_inspector_open = 200f;
      // constants
      private const float width_inspector_open_min = 195f;
      private const float width_code_min = 215f;
      private const float width_inspector_closed = 40f;
      private const float inspector_space = 4f;
      private const float height_label_offset = 3f;
      private const float width_indent = 15f;
  
      public enum DragType {
          None = -1,
          ResizeInspector = 0
      }
      public enum ElementType {
          None = -1,
          ResizeInspector = 0,
      }
  
      private int dragType = -1;
      private int mouseOverElement = -1;
      private bool isDragging = false;
      private bool cachedIsDragging = false;
  
      void OnEnable() {
          texRightArrow = AMEditorResource.LoadEditorTexture("am_nav_right");// inspector right arrow
          texLeftArrow = AMEditorResource.LoadEditorTexture("am_nav_left");	// inspector left arrow
  
          window = this;
          this.title = "Code View";
          this.minSize = new Vector2(/*380f*/width_code_min + width_inspector_open_min, 102f);
          this.scrollView = new Vector2(0f, 0f);
          oData = AMOptionsFile.loadFile();
          loadAnimatorData();
          refreshCode();
  
      }
      void OnDisable() {
          window = null;
      }
      void OnHierarchyChange() {
          if(!aData) loadAnimatorData();
      }
      public void reloadAnimatorData() {
          aData = null;
          loadAnimatorData();
      }
      void loadAnimatorData() {
          if(AMTimeline.window != null) {
              aData = AMTimeline.window.aData;
          }
      }
      void Update() {
          processDragLogic();
          /*codeRefreshBuffer--;
          if(aData && codeRefreshBuffer <= 0) {
              codeCache = getCode();	
              codeRefreshBuffer = codeRefreshRate;
              this.Repaint();
          }*/
          //if(EditorWindow.mouseOverWindow==this) this.Repaint();
      }
      void OnGUI() {
          AMTimeline.loadSkin(oData, ref skin, ref cachedSkinName, position);
          GUIStyle textStyle = new GUIStyle(GUI.skin.label);
          textStyle.wordWrap = false;
          textStyle.alignment = TextAnchor.UpperLeft;
          textStyle.padding = new RectOffset(0, 0, 10, 0);
          GUIStyle styleLabelCentered = new GUIStyle(GUI.skin.label);
          styleLabelCentered.alignment = TextAnchor.MiddleCenter;
          if(!aData) {
              AMTimeline.MessageBox("Animator requires an AnimatorData component in your scene. Launch Animator to add the component.", AMTimeline.MessageBoxType.Warning);
              return;
          }
          if(!oData) oData = AMOptionsFile.loadFile();
          #region drag logic
          Event e = Event.current;
          currentMousePosition = e.mousePosition;
          Rect rectWindow = new Rect(0f, 0f, position.width, position.height);
          mouseOverElement = (int)ElementType.None;
          //bool wasDragging = false;
          if(e.type == EventType.MouseDrag && EditorWindow.mouseOverWindow == this) {
              isDragging = true;
          }
          else if(e.type == EventType.MouseUp || /*EditorWindow.mouseOverWindow!=this*/Event.current.rawType == EventType.MouseUp /*|| e.mousePosition.y < 0f*/) {
              if(isDragging) {
                  //wasDragging = true;
                  isDragging = false;
              }
          }
          // set cursor
          if(dragType == (int)DragType.ResizeInspector) EditorGUIUtility.AddCursorRect(rectWindow, MouseCursor.ResizeHorizontal);
          #endregion
          #region resize inspector
          if(dragType == (int)DragType.ResizeInspector) {
              width_inspector_open = start_width_inspector_open + (startScrubMousePosition.x - e.mousePosition.x);
          }
          width_inspector_open = Mathf.Clamp(width_inspector_open, width_inspector_open_min, position.width - width_code_min);
          #endregion
          GUILayout.BeginHorizontal();
          #region code vertical
          GUILayout.BeginVertical(GUILayout.Height(position.height));
          GUILayout.Space(3f);
          if(aData.setCodeLanguage(GUILayout.SelectionGrid(aData.codeLanguage, selStrings, 2/*,styleSelGrid*/))) {
              // save data	
              EditorUtility.SetDirty(aData);
              refreshCode();
          }
          GUILayout.Space(3f);
          // set scrollview background
          GUIStyle styleScrollView = new GUIStyle(GUI.skin.scrollView);
          styleScrollView.normal.background = GUI.skin.GetStyle("GroupElementBG").onNormal.background;
          scrollView = EditorGUILayout.BeginScrollView(scrollView, false, false, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, styleScrollView);
          Vector2 textSize = textStyle.CalcSize(new GUIContent(codeCache));
          GUILayout.BeginHorizontal();
          GUILayout.Space(10f);
          EditorGUILayout.SelectableLabel(codeCache, textStyle, GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true), GUILayout.MinWidth(textSize.x + 30f), GUILayout.MinHeight(textSize.y + 30f));
          GUILayout.EndHorizontal();
          EditorGUILayout.EndScrollView();
          GUILayout.Space(3f);
          GUILayout.BeginHorizontal();
          // refresh button
          if(shouldRefresh) GUI.color = Color.green;
          if(GUILayout.Button(""/*,styleButton*/)) {
              refreshCode();
          }
          GUI.color = Color.white;
          GUI.Label(GUILayoutUtility.GetLastRect(), "Refresh", styleLabelCentered);
          // copy to clipboard button
          if(GUILayout.Button("Copy to Clipboard")) {
              ClipboardHelper.clipBoard = codeCache;
              GUIUtility.keyboardControl = 0;
              GUIUtility.hotControl = 0;
          }
          GUILayout.EndHorizontal();
          GUILayout.Space(3f);
          GUILayout.EndVertical();
          #endregion
          #region track list vertical
          GUILayout.BeginHorizontal(GUILayout.Width((isInspectorOpen ? width_inspector_open : width_inspector_closed)));
          //if(GUILayout.Button("O/C",GUILayout.Width(width_inspector_closed),GUILayout.Height(position.height))) {
          //isInspectorOpen = !isInspectorOpen;
          //}
          // properties button
          GUILayout.BeginVertical(GUILayout.Width(width_inspector_closed));
          GUILayout.Space(width_inspector_closed);
          Rect rectPropertiesButton = new Rect(position.width - (isInspectorOpen ? width_inspector_open : width_inspector_closed) - 1f, 0f, width_inspector_closed, position.height - 28f);
          if(GUI.Button(rectPropertiesButton, "", "label")) {
              isInspectorOpen = !isInspectorOpen;
          }
          GUI.color = AMTimeline.getSkinTextureStyleState("properties_bg").textColor;
          GUI.DrawTexture(rectPropertiesButton, AMTimeline.getSkinTextureStyleState("properties_bg").background);
          GUI.color = Color.white;
          GUI.DrawTexture(new Rect(rectPropertiesButton.x + 8f + (isInspectorOpen ? 1f : 0f), 12f, 22f, 19f), (isInspectorOpen ? texRightArrow : texLeftArrow));
          if(!isInspectorOpen) {
              int numSelected = 0;
              foreach(var pair in dictTracks) if(pair.Value == true) numSelected++;
              if(numSelected < dictTracks.Count) GUI.color = Color.red;
              GUI.Label(new Rect(rectPropertiesButton.x, rectPropertiesButton.y + rectPropertiesButton.height, rectPropertiesButton.width, 28f), numSelected + "/" + dictTracks.Count, styleLabelCentered);
              GUI.color = Color.white;
          }
          GUILayout.EndVertical();
          if(isInspectorOpen) {
              GUILayout.BeginVertical(GUILayout.Width(width_inspector_open - width_inspector_closed));
              GUILayout.Space(inspector_space);
              GUILayout.Label("Track Selection");
              GUILayout.Space(inspector_space);
              GUILayout.BeginHorizontal();
              //GUILayout.Space(inspector_space);
              scrollPos = EditorGUILayout.BeginScrollView(scrollPos/*,styleScrollView*/);
              for(int i = 0; i < aData.getCurrentTake().rootGroup.elements.Count; i++) {
                  int id = aData.getCurrentTake().rootGroup.elements[i];
                  //float height_group_elements = 0f;
                  showGroupElement(id, 0);
              }
              GUILayout.EndScrollView();
              GUILayout.Space(inspector_space);
              GUILayout.EndHorizontal();
              // buttons
              GUILayout.Space(inspector_space);
              GUILayout.BeginHorizontal();
              Rect rectResizeInspector = new Rect(rectPropertiesButton.x, position.height - 15f - 8f, 15f, 15f);
              GUI.Button(rectResizeInspector, "", GUI.skin.GetStyle("ResizeTrackThumb"));
              EditorGUIUtility.AddCursorRect(rectResizeInspector, MouseCursor.ResizeHorizontal);
              if(rectResizeInspector.Contains(e.mousePosition) && mouseOverElement == (int)ElementType.None) {
                  mouseOverElement = (int)ElementType.ResizeInspector;
              }
  
              if(GUILayout.Button("All", GUILayout.Width(42f))) {
                  foreach(var key in dictTracks.Keys.ToList()) {
                      dictTracks[key] = true;
                  }
                  refreshCode();
              }
              if(GUILayout.Button("None", GUILayout.Width(42f))) {
                  foreach(var key in dictTracks.Keys.ToList()) {
                      dictTracks[key] = false;
                  }
                  refreshCode();
              }
              GUILayout.FlexibleSpace();
              if(GUILayout.Button("JSON...", GUILayout.Width(58f))) {
                  // export json
                  exportJSON();
              }
              GUILayout.EndHorizontal();
              GUILayout.Space(inspector_space);
              GUILayout.EndVertical();
          }
          GUILayout.EndHorizontal();
          #endregion
          GUILayout.EndHorizontal();
      }
  
      void showGroupElement(int id, int group_lvl) {
          // returns true if mouse over track
          if(id >= 0) {
              AMTrack _track = aData.getCurrentTake().getTrack(id);
              showTrack(_track, id, group_lvl);
          }
          else {
              showGroup(id, group_lvl);
          }
      }
  
      void showGroup(int id, int group_lvl) {
          // show group
          //float group_x = width_subtrack_space*group_lvl;
  
          //Rect rectGroup = new Rect(group_x,track_y,width_track-group_x,height_group);
          if(!dictGroups.ContainsKey(id)) dictGroups.Add(id, true);
          AMGroup grp = aData.getCurrentTake().getGroup(id);
          GUILayout.BeginHorizontal();
          GUILayout.Space(width_indent * (group_lvl));	// indent
          // foldout
          GUILayout.BeginVertical(GUILayout.Width(15f));
          GUILayout.Space(height_label_offset - 1f);
          if(GUILayout.Button("", "label", GUILayout.Width(15f))) dictGroups[id] = !dictGroups[id];
          GUI.DrawTexture(GUILayoutUtility.GetLastRect(), (dictGroups[id] ? GUI.skin.GetStyle("GroupElementFoldout").normal.background : GUI.skin.GetStyle("GroupElementFoldout").active.background));
          GUILayout.EndVertical();
          // select children button
          if(hasTracks(grp)) {
              GUILayout.BeginVertical(GUILayout.Width(13f));
              GUILayout.Space(height_label_offset + 1f);
              if(GUILayout.Button("", GUILayout.Width(13f), GUILayout.Height(15f))) {
                  bool? newValue = null;
                  toggleChildren(grp, ref newValue);
                  refreshCode();
                  /*if(gameObjsSelected[i] != null) {
                      gameObjsSelected[i] = !gameObjsSelected[i];
                      setAllChildrenSelection((bool)gameObjsSelected[i], i, gameObjsDepth[i]);
                  }*/
  
              }
              Rect rectSelectAllTexture = GUILayoutUtility.GetLastRect();
              GUILayout.EndVertical();
  
              rectSelectAllTexture.x += 3f;
              rectSelectAllTexture.y += 4f;
              rectSelectAllTexture.width = 7f;
              rectSelectAllTexture.height = 8f;
              if(!GUI.enabled) GUI.color = new Color(GUI.color.r, GUI.color.g, GUI.color.b, 0.25f);
              GUI.DrawTexture(rectSelectAllTexture, AMTimeline.getSkinTextureStyleState("select_all").background);
              GUI.color = Color.white;
          }
          GUILayout.BeginVertical();
          GUILayout.Space(height_label_offset);
          GUILayout.Label(grp.group_name);
          GUILayout.EndHorizontal();
          GUILayout.EndHorizontal();
          group_lvl++;	// increment group_lvl for sub-elements
          if(dictGroups[id]) {
              for(int j = 0; j < grp.elements.Count; j++) {
                  int _id = grp.elements[j];
                  showGroupElement(_id, group_lvl);
              }
          }
      }
      bool hasTracks(AMGroup grp) {
          foreach(int id in grp.elements) {
              if(id >= 0) return true;
              else if(hasTracks(aData.getCurrentTake().getGroup(id))) return true;
          }
          return false;
      }
      void toggleChildren(AMGroup grp, ref bool? newValue) {
          foreach(int _id in grp.elements) {
              if(_id < 0) toggleChildren(aData.getCurrentTake().getGroup(_id), ref newValue);
              else {
                  if(newValue == null) newValue = !dictTracks[_id];
                  dictTracks[_id] = (bool)newValue;
              }
  
          }
      }
      void showTrack(AMTrack _track, int id, int group_level) {
          if(!dictTracks.ContainsKey(id)) dictTracks.Add(id, true);
  
          GUILayout.BeginHorizontal();
          GUILayout.Space(width_indent * group_level);
          bool prev = dictTracks[id];
          dictTracks[id] = GUILayout.Toggle(dictTracks[id], "");
          if(dictTracks[id] != prev) {
              // changed
              refreshCode();
          }
          GUILayout.BeginVertical();
          GUILayout.Space(height_label_offset);
          GUILayout.Label(_track.name);
          GUILayout.EndVertical();
          GUILayout.EndHorizontal();
      }
      string getCode() {
          if(!aData) return "";
          varNameDictionary = new Dictionary<string, string>();
          string code = "// " + aData.getCurrentTake().name;
          code += "\n";
          appendGroupCode(aData.getCurrentTake().getGroup(0), ref code);
          return code;
      }
  
      void refreshCode(bool resetControls = true) {
          if(shouldRefresh) shouldRefresh = false;
          codeCache = getCode();
          if(resetControls) {
              try {
                  GUIUtility.keyboardControl = 0;
                  GUIUtility.hotControl = 0;
              }
              catch {
                  // do nothing
              }
          }
          this.Repaint();
      }
  
      void appendGroupCode(AMGroup _grp, ref string code) {
  
gt:         foreach(int element_id in _grp.elements) {