context: using UnityEngine;
  using UnityEditor;
  using System.Collections;
  using System.Collections.Generic;
  using System.Reflection;
  using System.IO;
  using System.Text;
  using SimpleJSON;
  
  namespace M8.Editor {
      public class InputBinder : EditorWindow {
          public const string PrefKlass = "InputBinder";
          public const string PrefText = "Text";
  
          private TextAsset mTextFile;
          private string mTextName = "";
          private string mTextFilePath = "";
  
          private enum InputType {
              Unity,
              KeyCode,
              InputMap
          }
  
          private struct BindData {
              public InputManager.Bind bind;
              public InputType[] keyTypes;
              public bool foldOut;
  
              public void DeleteKey(int ind) {
                  bind.keys.RemoveAt(ind);
                  keyTypes = M8.ArrayUtil.RemoveAt(keyTypes, ind);
              }
  
              public void ResizeKeys(int newLen) {
                  if(keyTypes == null) {
                      if(bind.keys == null) {
                          bind.keys = new List<InputManager.Key>();
                          keyTypes = new InputType[0];
                      }
                      else if(bind.keys.Count == 0) {
                          keyTypes = new InputType[0];
                      }
                      else {
                          RefreshKeyTypes();
                      }
                  }
  
                  int prevLen = keyTypes.Length;
  
                  if(prevLen != newLen) {
                      System.Array.Resize(ref keyTypes, newLen);
  
                      if(prevLen > keyTypes.Length) {
                          bind.keys.RemoveRange(newLen - 1, bind.keys.Count - newLen);
                      }
                      else {
                          for(int i = prevLen; i < keyTypes.Length; i++) {
                              keyTypes[i] = InputType.Unity;
                              bind.keys.Add(new InputManager.Key());
                          }
                      }
                  }
              }
  
              public void RefreshKeyType(int ind) {
                  if(bind.keys[ind].map != InputKeyMap.None)
                      keyTypes[ind] = InputType.InputMap;
                  else if(bind.keys[ind].code != KeyCode.None)
                      keyTypes[ind] = InputType.KeyCode;
                  else
                      keyTypes[ind] = InputType.Unity;
              }
  
              public void RefreshKeyTypes() {
                  if(bind.keys != null && bind.keys.Count > 0) {
                      if(keyTypes == null || keyTypes.Length != bind.keys.Count) {
                          keyTypes = new InputType[bind.keys.Count];
                      }
  
                      for(int i = 0; i < keyTypes.Length; i++)
                          RefreshKeyType(i);
                  }
              }
  
              //call before saving, basically clears out specific key binds based on key type
              public void ApplyKeyTypes() {
                  if(keyTypes != null && bind.keys != null) {
                      for(int i = 0; i < keyTypes.Length; i++) {
                          switch(keyTypes[i]) {
                              case InputType.Unity:
                                  bind.keys[i].code = KeyCode.None;
                                  bind.keys[i].map = InputKeyMap.None;
                                  break;
  
                              case InputType.KeyCode:
                                  bind.keys[i].input = "";
                                  bind.keys[i].map = InputKeyMap.None;
                                  break;
  
                              case InputType.InputMap:
                                  bind.keys[i].input = "";
                                  bind.keys[i].code = KeyCode.None;
                                  break;
                          }
                      }
                  }
              }
          }
  
          private BindData[] mBinds;
  
          private Vector2 mBindsScroll;
  
          [MenuItem("M8/Input Binder")]
          static void DoIt() {
              EditorWindow.GetWindow(typeof(InputBinder));
          }
  
          void OnSelectionChange() {
              Repaint();
          }
  
          void OnEnable() {
              string path = EditorPrefs.GetString(Utility.PreferenceKey(PrefKlass, PrefText), "");
              if(!string.IsNullOrEmpty(path)) {
                  Object obj = AssetDatabase.LoadAssetAtPath(path, typeof(TextAsset));
                  if(obj != null)
                      mTextFile = (TextAsset)obj;
              }
          }
  
          void OnDisable() {
              if(mTextFile != null)
                  EditorPrefs.SetString(Utility.PreferenceKey(PrefKlass, PrefText), AssetDatabase.GetAssetPath(mTextFile));
          }
  
          void OnGUI() {
              TextAsset prevTextFile = mTextFile;
  
              EditorGUIUtility.LookLikeControls(80.0f);
  
              GUILayout.Space(6f);
              GUILayout.BeginHorizontal();
  
              bool doCreate = false;
  
              if(mTextFile == null) {
                  GUI.backgroundColor = Color.green;
                  doCreate = GUILayout.Button("Create", GUILayout.Width(76f));
  
                  GUI.backgroundColor = Color.white;
                  mTextName = GUILayout.TextField(mTextName);
              }
  
              GUILayout.EndHorizontal();
  
              if(mTextFile != null) {
                  mTextName = mTextFile.name;
                  mTextFilePath = AssetDatabase.GetAssetPath(mTextFile);
              }
              else if(!string.IsNullOrEmpty(mTextName)) {
                  mTextFilePath = Utility.GetSelectionFolder() + mTextName + ".txt";
              }
  
              if(doCreate && !string.IsNullOrEmpty(mTextName)) {
                  File.WriteAllText(mTextFilePath, "");
  
                  AssetDatabase.Refresh();
  
                  mTextFile = (TextAsset)AssetDatabase.LoadAssetAtPath(mTextFilePath, typeof(TextAsset));
              }
  
              GUILayout.BeginHorizontal();
  
              GUILayout.Label("Select: ");
  
              mTextFile = (TextAsset)EditorGUILayout.ObjectField(mTextFile, typeof(TextAsset), false);
  
              GUILayout.EndHorizontal();
  
              if(!string.IsNullOrEmpty(mTextFilePath))
                  GUILayout.Label("Path: " + mTextFilePath);
              else {
                  GUILayout.Label("Path: <none>" + mTextFilePath);
              }
  
              if(GUILayout.Button("Edit Input Actions")) {
                  EditorWindow.GetWindow(typeof(InputMapper));
              }
  
              Utility.DrawSeparator();
  
              bool refreshBinds = mTextFile != prevTextFile;
  
              if(mTextFile != null) {
                  List<string> actions = InputMapper.actions;
  
                  //initialize bind data
                  if(mBinds == null || mBinds.Length != actions.Count) {
                      if(mBinds == null) {
                          mBinds = new BindData[actions.Count];
                      }
                      else {
                          System.Array.Resize<BindData>(ref mBinds, actions.Count);
                      }
  
                      refreshBinds = true;
                  }
  
                  //load from file
                  if(refreshBinds && mTextFile.text.Length > 0) {
                      //load data
                      //
                      var loadBinds = new List<InputManager.Bind>();
                      var json = JSON.Parse(mTextFile.text).AsArray;
                      foreach(var node in json) {
                          var entryNode = node.Value;
  
                          int action = entryNode["action"].AsInt;
                          InputManager.Control control = (InputManager.Control)System.Enum.Parse(typeof(InputManager.Control), entryNode["control"].ToString());
                          bool axisInvert = entryNode["axisInvert"].AsBool;
                          float deadZone = entryNode["deadZone"].AsFloat;
                          bool forceRaw = entryNode["forceRaw"].AsBool;
  
                          List<InputManager.Key> inpkeys;
  
                          if(entryNode["keys"] != null) {
                              var keyArrayNode = entryNode["keys"].AsArray;
                              inpkeys = new List<InputManager.Key>(keyArrayNode.Count);
                              for(int i = 0; i < keyArrayNode.Count; i++) {
                                  int player = keyArrayNode[i]["player"].AsInt;
                                  string input = keyArrayNode[i]["input"].ToString();
                                  KeyCode code = (KeyCode)System.Enum.Parse(typeof(KeyCode), keyArrayNode[i]["code"].ToString());
                                  InputKeyMap map = (InputKeyMap)System.Enum.Parse(typeof(InputKeyMap), keyArrayNode[i]["map"].ToString());
                                  InputManager.ButtonAxis axis = (InputManager.ButtonAxis)System.Enum.Parse(typeof(InputManager.ButtonAxis), keyArrayNode[i]["axis"].ToString());
                                  int index = keyArrayNode[i]["index"].AsInt;
  
                                  inpkeys.Add(new InputManager.Key() { player = player, input = input, code = code, map = map, axis = axis, index = index });
                              }
                          }
                          else
                              inpkeys = new List<InputManager.Key>();
  
                          loadBinds.Add(new InputManager.Bind() { action = action, control = control, deadZone = deadZone, keys = inpkeys });
                      }
                      //
                      foreach(InputManager.Bind bind in loadBinds) {
                          if(bind.action < mBinds.Length) {
                              mBinds[bind.action].bind = bind;
                              mBinds[bind.action].RefreshKeyTypes();
                          }
                      }
                  }
  
                  //display
                  mBindsScroll = GUILayout.BeginScrollView(mBindsScroll);
  
                  for(int i = 0; i < mBinds.Length; i++) {
                      if(mBinds[i].bind == null) {
  
gt:                         mBinds[i].bind = new InputManager.Bind();