context: using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Linq;
  using System.Reflection;
  using System.Text;
  
  // TODO: Change this namespace to something specific to your plugin here.
  //EG:
  // namespace MyPlugin_KACWrapper
  namespace NE_Science.KAC
  {
      ///////////////////////////////////////////////////////////////////////////////////////////
      // BELOW HERE SHOULD NOT BE EDITED - this links to the loaded KAC module without requiring a Hard Dependancy
      ///////////////////////////////////////////////////////////////////////////////////////////
  
      /// <summary>
      /// The Wrapper class to access KAC from another plugin
      /// </summary>
      public class KACWrapper
      {
          protected static System.Type KACType;
          protected static System.Type KACAlarmType;
  
          protected static Object actualKAC = null;
  
          /// <summary>
          /// This is the Kerbal Alarm Clock object
          /// 
          /// SET AFTER INIT
          /// </summary>
          public static KACAPI KAC = null;
          /// <summary>
          /// Whether we found the KerbalAlarmClock assembly in the loadedassemblies. 
          /// 
          /// SET AFTER INIT
          /// </summary>
          public static Boolean AssemblyExists { get { return (KACType != null); } }
          /// <summary>
          /// Whether we managed to hook the running Instance from the assembly. 
          /// 
          /// SET AFTER INIT
          /// </summary>
          public static Boolean InstanceExists { get { return (KAC != null); } }
          /// <summary>
          /// Whether we managed to wrap all the methods/functions from the instance. 
          /// 
          /// SET AFTER INIT
          /// </summary>
          private static Boolean _KACWrapped = false;
  
          /// <summary>
          /// Whether the object has been wrapped and the APIReady flag is set in the real KAC
          /// </summary>
          public static Boolean APIReady { get { return _KACWrapped && KAC.APIReady && !NeedUpgrade; } }
  
  
          public static Boolean NeedUpgrade { get; private set; }
  
          /// <summary>
          /// This method will set up the KAC object and wrap all the methods/functions
          /// </summary>
          /// <param name="Force">This option will force the Init function to rebind everything</param>
          /// <returns></returns>
          public static Boolean InitKACWrapper()
          {
              //if (!_KACWrapped )
              //{
              //reset the internal objects
              _KACWrapped = false;
              actualKAC = null;
              KAC = null;
              LogFormatted("Attempting to Grab KAC Types...");
  
              //find the base type
              AssemblyLoader.loadedAssemblies.TypeOperation(t =>
              {
                  if (t.FullName == "KerbalAlarmClock.KerbalAlarmClock")
                      KACType = t;
              });
  
              if (KACType == null)
              {
                  return false;
              }
  
              LogFormatted("KAC Version:{0}", KACType.Assembly.GetName().Version.ToString());
              if (KACType.Assembly.GetName().Version.CompareTo(new System.Version(3, 0, 0, 5)) < 0)
              {
                  //No TimeEntry or alarmchoice options = need a newer version
                  NeedUpgrade = true;
              }
              
              //now the Alarm Type
              KACAlarmType = AssemblyLoader.loadedAssemblies
                  .Select(a => a.assembly.GetExportedTypes())
                  .SelectMany(t => t)
                  .FirstOrDefault(t => t.FullName == "KerbalAlarmClock.KACAlarm");
  
              if (KACAlarmType == null)
              {
                  return false;
              }
  
              //now grab the running instance
              LogFormatted("Got Assembly Types, grabbing Instance");
  
              try {
                  actualKAC = KACType.GetField("APIInstance", BindingFlags.Public | BindingFlags.Static).GetValue(null);
              } catch (Exception) {
                  NeedUpgrade = true;
                  LogFormatted("No APIInstance found - most likely you have KAC v2 installed");
                  //throw;
              }
              if (actualKAC == null)
              {
                  LogFormatted("Failed grabbing Instance");
                  return false;
              }
  
              //If we get this far we can set up the local object and its methods/functions
              LogFormatted("Got Instance, Creating Wrapper Objects");
              KAC = new KACAPI(actualKAC);
              //}
              _KACWrapped = true;
              return true;
          }
  
          /// <summary>
          /// The Type that is an analogue of the real KAC. This lets you access all the API-able properties and Methods of the KAC
          /// </summary>
          public class KACAPI
          {
  
              internal KACAPI(Object KAC)
              {
                  //store the actual object
                  actualKAC = KAC;
  
                  //these sections get and store the reflection info and actual objects where required. Later in the properties we then read the values from the actual objects
                  //for events we also add a handler
                  LogFormatted("Getting APIReady Object");
                  APIReadyField = KACType.GetField("APIReady", BindingFlags.Public | BindingFlags.Static);
                  LogFormatted("Success: " + (APIReadyField != null).ToString());
  
                  //WORK OUT THE STUFF WE NEED TO HOOK FOR PEOPEL HERE
                  LogFormatted("Getting Alarms Object");
                  AlarmsField = KACType.GetField("alarms", BindingFlags.Public | BindingFlags.Static);
                  actualAlarms = AlarmsField.GetValue(actualKAC);
                  LogFormatted("Success: " + (actualAlarms != null).ToString());
  
                  //Events
                  LogFormatted("Getting Alarm State Change Event");
                  onAlarmStateChangedEvent = KACType.GetEvent("onAlarmStateChanged", BindingFlags.Public | BindingFlags.Instance);
                  LogFormatted_DebugOnly("Success: " + (onAlarmStateChangedEvent != null).ToString());
                  LogFormatted_DebugOnly("Adding Handler");
                  AddHandler(onAlarmStateChangedEvent, actualKAC, AlarmStateChanged);
  
                  //Methods
                  LogFormatted("Getting Create Method");
                  CreateAlarmMethod = KACType.GetMethod("CreateAlarm", BindingFlags.Public | BindingFlags.Instance);
                  LogFormatted_DebugOnly("Success: " + (CreateAlarmMethod != null).ToString());
  
                  LogFormatted("Getting Delete Method");
                  DeleteAlarmMethod = KACType.GetMethod("DeleteAlarm", BindingFlags.Public | BindingFlags.Instance);
                  LogFormatted_DebugOnly("Success: " + (DeleteAlarmMethod != null).ToString());
  
                  LogFormatted("Getting DrawAlarmAction");
                  DrawAlarmActionChoiceMethod = KACType.GetMethod("DrawAlarmActionChoiceAPI", BindingFlags.Public | BindingFlags.Instance);
                  LogFormatted_DebugOnly("Success: " + (DrawAlarmActionChoiceMethod != null).ToString());
  
                  //LogFormatted("Getting DrawTimeEntry");
                  //DrawTimeEntryMethod = KACType.GetMethod("DrawTimeEntryAPI", BindingFlags.Public | BindingFlags.Instance);
                  //LogFormatted_DebugOnly("Success: " + (DrawTimeEntryMethod != null).ToString());
  
  				//Commenting out rubbish lines
                  //MethodInfo[] mis = KACType.GetMethods(BindingFlags.Public | BindingFlags.Instance);
                  //foreach (MethodInfo mi in mis)
                  //{
                  //    LogFormatted("M:{0}-{1}", mi.Name, mi.DeclaringType);
                  //}
              }
  
              private Object actualKAC;
  
              private FieldInfo APIReadyField;
              /// <summary>
              /// Whether the APIReady flag is set in the real KAC
              /// </summary>
              public Boolean APIReady
              {
                  get
                  {
                      if (APIReadyField == null)
                          return false;
  
                      return (Boolean)APIReadyField.GetValue(null);
                  }
              }
  
              #region Alarms
              private Object actualAlarms;
              private FieldInfo AlarmsField;
  
              /// <summary>
              /// The list of Alarms that are currently active in game
              /// </summary>
              internal KACAlarmList Alarms
              {
                  get
                  {
                      return ExtractAlarmList(actualAlarms);
                  }
              }
  
              /// <summary>
              /// This converts the KACAlarmList actual object to a new List for consumption
              /// </summary>
              /// <param name="actualAlarmList"></param>
              /// <returns></returns>
              private KACAlarmList ExtractAlarmList(Object actualAlarmList)
              {
                  KACAlarmList ListToReturn = new KACAlarmList();
                  try
                  {
                      //iterate each "value" in the dictionary
  
                      foreach (var item in (IList)actualAlarmList)
                      {
  
gt:                         KACAlarm r1 = new KACAlarm(item);