private void Update()
        {
            if (Time.unscaledTime > lastConnectionUpdate + 10)
            {
                lastConnectionUpdate = Time.unscaledTime;

                // Only update in the editor, since that's the only place where we need to actively track the inventory.
                if (HighLogic.LoadedScene == GameScenes.EDITOR)
                {
                    string bodyName;

                    if (FlightGlobals.ActiveVessel != null)
                    {
                        bodyName = FlightGlobals.ActiveVessel.mainBody.bodyName;
                    }
                    else
                    {
                        bodyName = "Kerbin";
                    }

                    if (XenoIndustrySignpost.IsConnected(bodyName))
                    {
                        StartCoroutine(XenoIndustrySignpost.GetClusterioInventory(bodyName, clusterioInventory));
                    }
                }
            }
        }
        // TODO
        //
        // - XenoIndustryLaunchCosts
        // --- loading from config files using a rule system
        // - ClusterioConnector
        // --- ability to connect to multiple masters and hold multiple connections open
        // --- only acts on external commands, no "smart" behaviour of its own
        //
        // - a single class
        //
        // ADD
        // - XenoIndustryCargo - dedicated cargo tanks (separate for solids and liquids) than can be loaded and unloaded using an interface when on the launchpad
        // - XenoIndustrySignpost
        // --- class handling the Clusterio connections based on planets (planetName => urlAddress dictionary)
        // --- loading from signpost.json file (later from a signpost server?)
        // --- other classes should route their requests through the XenoIndustrySignpost one, which then in turn sends the proper url address to ClusterioConnector
        // --- should even decide which connections to keep open and how long
        //

        public void Awake()
        {
            instance = this;

            DontDestroyOnLoad(this);

            GameEvents.onGUIApplicationLauncherReady.Add(OnGUIAppLauncherReady);
            GameEvents.onGUIApplicationLauncherDestroyed.Add(OnGUIApplicationLauncherDestroyed);

            StreamReader reader = new StreamReader(MOD_PATH + "config.json");

            if (reader != null)
            {
                JSONNode modConfig = JSON.Parse(reader.ReadToEnd());

                if (modConfig["debug"] != null)
                {
                    Debug.Log(String.Format("ClusterioTest: sciencePerSciencePack is {0}", modConfig["sciencePerSciencePack"]));
                    debug = modConfig["debug"];
                }
            }

            XenoIndustrySignpost.LoadSignpost();

            windowRect = new Rect(Screen.width / 2 - 150, Screen.height / 2 - 150, 300, 100);

            clusterioInventory = new Dictionary <string, int>();
        }
        private void Update()
        {
            if (Time.unscaledTime > lastConnectionUpdate + 10)
            {
                lastConnectionUpdate = Time.unscaledTime;

                // Periodically update Clusterio inventory if not ingame
                if (HighLogic.LoadedScene == GameScenes.SPACECENTER || HighLogic.LoadedScene == GameScenes.EDITOR || HighLogic.LoadedScene == GameScenes.TRACKSTATION)
                {
                    string bodyName;

                    if (FlightGlobals.ActiveVessel != null)
                    {
                        bodyName = FlightGlobals.ActiveVessel.mainBody.bodyName;
                    }
                    else
                    {
                        bodyName = "Kerbin";
                    }

                    if (XenoIndustrySignpost.IsConnected(bodyName))
                    {
                        StartCoroutine(XenoIndustrySignpost.GetClusterioInventory(bodyName, clusterioInventory));
                    }
                    else
                    {
                        XenoIndustrySignpost.RefreshConnection(bodyName);
                    }
                }
            }
        }
        private void DeductLaunchPrice()
        {
            string bodyName = "Kerbin"; // Only launches from Kerbin are possible at this point

            foreach (KeyValuePair <string, int> kvPair in latestLaunchCosts)
            {
                StartCoroutine(XenoIndustrySignpost.RemoveItemsFromClusterio(bodyName, clusterioInventory, kvPair.Key, kvPair.Value));
            }
        }
        public static IEnumerator RemoveItemsFromClusterio(string bodyName, Dictionary <string, int> clusterioInventory, string itemName, int count, Action <bool> onFinished = null)
        {
            if (!BodyHasServer(bodyName))
            {
                Debug.Log(String.Format("XenoIndustrySignpost: Sending a RemoveItemsFromClusterio request to a celestial {0} which has no assigned server!", bodyName));
                yield break;
            }

            XenoIndustrySignpostBodyAddress address = bodyAddresses[bodyName];

            // Connect to the server if we aren't connected yet
            if (!ClusterioConnector.IsConnected(address.masterIP, address.masterPort))
            {
                ClusterioConnector.ConntectToMaster(address.masterIP, address.masterPort, address.masterAuthToken);
            }

            // First, we need to see what items are stored in the Clusterio server, and wait for that request to be finished
            yield return(XenoIndustryCore.instance.StartCoroutine(XenoIndustrySignpost.GetClusterioInventory(bodyName, clusterioInventory)));

            // Don't do a remove request if there's not enough items, or if the item isn't there at all
            if (!clusterioInventory.ContainsKey(itemName) || clusterioInventory[itemName] < count)
            {
                yield break;
            }

            Dictionary <string, string> sendValues = new Dictionary <string, string>();

            sendValues["name"]  = itemName;
            sendValues["count"] = count.ToString();

            Debug.Log(String.Format("ClusterioUtil: removing {0} of {1} from item repository", count, itemName));

            string apiCommand = "/api/remove";

            ClusterioMessage resultMessage = new ClusterioMessage();

            yield return(ClusterioConnector.SendPostRequest(address.masterIP, address.masterPort, apiCommand, resultMessage, sendValues));

            if (resultMessage.result == ClusterioMessageResult.SUCCESS)
            {
                if (onFinished != null)
                {
                    onFinished(true);
                }
            }
            else
            {
                if (onFinished != null)
                {
                    onFinished(false);
                }
            }
        }
        /*public void Start()
         * {
         *  Debug.Log("ClusterioTest: Start");
         * }*/

        private void OnGameSceneSwitchRequested(GameEvents.FromToAction <GameScenes, GameScenes> sceneSwitch)
        {
            if (HighLogic.CurrentGame.Parameters.CustomParams <XenoIndustryCoreGameParameters>().enabled)
            {
                if (toolbarButton != null)
                {
                    toolbarButton.SetFalse();
                }

                // If the player has reverted to VAB or SPH, refund the spent items
                if (sceneSwitch.from == GameScenes.FLIGHT && sceneSwitch.to == GameScenes.EDITOR)
                {
                    string bodyName = "Kerbin"; // Only launches from Kerbin are possible at this point

                    foreach (KeyValuePair <string, int> kvPair in latestLaunchCosts)
                    {
                        StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, kvPair.Key, kvPair.Value));
                    }
                }
            }
        }
        IEnumerator TransferScienceToClusterio()
        {
            int scienceTransferAmount     = (int)(ResearchAndDevelopment.Instance.Science - (ResearchAndDevelopment.Instance.Science % sciencePerSciencePack));
            int sciencePackTransferAmount = (int)(scienceTransferAmount / sciencePerSciencePack);

            ClusterioMessage resultMessage = new ClusterioMessage();

            string bodyName = "Kerbin"; // Science packs can only be converted at Kerbin for now

            yield return(StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, "space-science-pack", sciencePackTransferAmount, (success) =>
            {
                if (success)
                {
                    // Show results as text
                    Debug.Log("TransferScienceToClusterio: science sent successfully");

                    // Only subtract science in KSP if the request successfuly reached the Clusterio master server
                    ResearchAndDevelopment.Instance.AddScience(-scienceTransferAmount, TransactionReasons.ScienceTransmission);
                }
            }
                                                                                 )));
        }
        private void OnVesselRecovered(ProtoVessel recoveredVessel, bool quick)
        {
            if (HighLogic.CurrentGame.Parameters.CustomParams <XenoIndustryCoreGameParameters>().enabled)
            {
                Dictionary <string, int> recoveredResources = new Dictionary <string, int>();

                CalculateLaunchCosts(ref recoveredResources, recoveredVessel);

                if (recoveredResources.Count > 0)
                {
                    string message = "Following resources have been recovered: \n";

                    string bodyName = "Kerbin"; // Recovery is only possible on Kerbin for now

                    foreach (KeyValuePair <string, int> kvPair in recoveredResources)
                    {
                        message += String.Format("\n {0} {1}", kvPair.Value, kvPair.Key);

                        StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, kvPair.Key, kvPair.Value));
                    }

                    message += "\n";

                    if (!quick)
                    {
                        MultiOptionDialog dialog = new MultiOptionDialog("ClusterioResourceRecovery", message, "Recovery successful", UISkinManager.GetSkin("KSP window 7"),
                                                                         new DialogGUIBase[]
                        {
                            new DialogGUIButton("Continue", null)
                        });

                        PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), dialog, false, null);
                    }
                }
            }
        }
        private void OnCargoWindowInternal(int id)
        {
            GUILayout.BeginVertical();

            string bodyName;

            if (FlightGlobals.ActiveVessel != null)
            {
                bodyName = FlightGlobals.ActiveVessel.mainBody.bodyName;
            }
            else
            {
                bodyName = "Kerbin";
            }

            if (!XenoIndustrySignpost.BodyHasServer(bodyName))
            {
                GUILayout.Label("This celestial body has no associated master server.");
            }
            else if (!XenoIndustrySignpost.IsConnected(bodyName))
            {
                GUILayout.Label("Cannot connect to Clusterio master server!");

                GUILayout.Label("Error: " + XenoIndustrySignpost.GetConnectionError(bodyName));

                if (GUILayout.Button("Refresh connection"))
                {
                    XenoIndustrySignpost.RefreshConnection(bodyName);
                }
            }
            else if (FlightGlobals.ActiveVessel != null && FlightGlobals.ActiveVessel.situation != Vessel.Situations.LANDED && FlightGlobals.ActiveVessel.situation != Vessel.Situations.PRELAUNCH && FlightGlobals.ActiveVessel.situation != Vessel.Situations.SPLASHED)
            {
                GUILayout.Label("Cannot transfer cargo until vessel is landed");
            }
            else
            {
                // Clusterio inventory handling
                if (HighLogic.LoadedScene == GameScenes.SPACECENTER || HighLogic.LoadedScene == GameScenes.EDITOR || HighLogic.LoadedScene == GameScenes.TRACKSTATION || HighLogic.LoadedScene == GameScenes.FLIGHT)
                {
                    GUILayout.Label("Available inventory:");

                    if (clusterioInventory.Count > 0)
                    {
                        int spaceRemaining = (int)(windowActivePart.cargoResource.maxAmount - windowActivePart.cargoResource.amount);
                        int netTransfer    = 0;

                        // Item names
                        GUILayout.BeginHorizontal();

                        GUILayout.BeginVertical();
                        GUILayout.Label("Resource");
                        GUILayout.Space(8);

                        foreach (KeyValuePair <string, int> kvPair in clusterioInventory)
                        {
                            GUILayout.Label(kvPair.Key + ":");
                        }
                        GUILayout.EndVertical();

                        // Loaded cargo
                        GUILayout.BeginVertical();
                        GUILayout.Label("Loaded");
                        GUILayout.Space(8);

                        foreach (KeyValuePair <string, int> kvPair in clusterioInventory)
                        {
                            if (windowActivePart.carriedCargo.ContainsKey(kvPair.Key))
                            {
                                GUILayout.Label(windowActivePart.carriedCargo[kvPair.Key].ToString());
                            }
                            else
                            {
                                GUILayout.Label("0");
                            }
                        }

                        GUILayout.EndVertical();

                        // Item amount to transfer
                        GUILayout.BeginVertical();
                        GUILayout.Label("Transfer");
                        GUILayout.Space(8);

                        foreach (KeyValuePair <string, int> kvPair in clusterioInventory)
                        {
                            if (!cargoSelected.ContainsKey(kvPair.Key))
                            {
                                cargoSelected[kvPair.Key] = 0;
                            }

                            int result;

                            int.TryParse(GUILayout.TextField(cargoSelected[kvPair.Key].ToString()), out result);

                            cargoSelected[kvPair.Key] = result;
                            spaceRemaining           -= result;
                            netTransfer += result;
                        }
                        GUILayout.EndVertical();

                        // Items stocks
                        GUILayout.BeginVertical();
                        GUILayout.Label("Available");
                        GUILayout.Space(8);

                        foreach (KeyValuePair <string, int> kvPair in clusterioInventory)
                        {
                            GUILayout.Label(kvPair.Value.ToString());
                        }

                        GUILayout.EndVertical();
                        GUILayout.EndHorizontal();

                        GUILayout.Space(8);

                        // Remaining space
                        GUILayout.BeginHorizontal();

                        GUILayout.Label("Space remaining:");
                        GUILayout.Label(spaceRemaining.ToString() + " / " + ((int)windowActivePart.cargoResource.maxAmount).ToString());

                        GUILayout.EndHorizontal();

                        // Net amount of items transferred to part
                        GUILayout.BeginHorizontal();

                        GUILayout.Label("Net items transferred:");
                        GUILayout.Label(netTransfer.ToString());

                        GUILayout.EndHorizontal();

                        GUILayout.Space(8);

                        GUILayout.Label(windowResponse);

                        if (GUILayout.Button("Transfer cargo"))
                        {
                            // Check if we aren't trying to load more than the module can actually hold
                            if (spaceRemaining < 0)
                            {
                                windowResponse = "Cannot load more cargo than the module can hold";
                            }
                            else
                            {
                                bool success = true;

                                // Check if we aren't trying to more cargo than the the server actually has
                                foreach (KeyValuePair <string, int> kvPair in cargoSelected)
                                {
                                    if (clusterioInventory.ContainsKey(kvPair.Key))
                                    {
                                        if (kvPair.Value > clusterioInventory[kvPair.Key])
                                        {
                                            windowResponse = String.Format("Cannot load {0} of item {1}, only {2} is available", kvPair.Value, kvPair.Key, clusterioInventory[kvPair.Key]);
                                            success        = false;
                                            break;
                                        }
                                    }
                                }

                                // Check if we aren't trying to unload more cargo than the module has
                                foreach (KeyValuePair <string, int> kvPair in cargoSelected)
                                {
                                    if (kvPair.Value < 0)
                                    {
                                        if (!windowActivePart.carriedCargo.ContainsKey(kvPair.Key))
                                        {
                                            windowResponse = String.Format("Cannot unload {0} item {1}, none currently loaded", kvPair.Value, kvPair.Key);
                                            success        = false;
                                            break;
                                        }

                                        if (-kvPair.Value > windowActivePart.carriedCargo[kvPair.Key])
                                        {
                                            windowResponse = String.Format("Cannot unload {0} of item {1}, only {2} currently loaded", kvPair.Value, kvPair.Key, windowActivePart.carriedCargo[kvPair.Key]);
                                            success        = false;
                                            break;
                                        }
                                    }
                                }

                                if (success)
                                {
                                    windowResponse = "Cargo transfer successful";

                                    foreach (KeyValuePair <string, int> kvPair in cargoSelected)
                                    {
                                        // Sanity check
                                        if (kvPair.Value == 0)
                                        {
                                            continue;
                                        }

                                        Action <bool> cargoCallback = delegate(bool requestSuccessful)
                                        {
                                            if (requestSuccessful)
                                            {
                                                if (windowActivePart.carriedCargo.ContainsKey(kvPair.Key))
                                                {
                                                    windowActivePart.carriedCargo[kvPair.Key] += kvPair.Value;
                                                }
                                                else
                                                {
                                                    windowActivePart.carriedCargo[kvPair.Key] = kvPair.Value;
                                                }
                                            }
                                        };

                                        if (kvPair.Value > 0)
                                        {
                                            StartCoroutine(XenoIndustrySignpost.RemoveItemsFromClusterio(bodyName, clusterioInventory, kvPair.Key, kvPair.Value, cargoCallback));
                                        }
                                        else
                                        {
                                            StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, kvPair.Key, -kvPair.Value, cargoCallback));
                                        }
                                    }

                                    windowActivePart.cargoResource.amount += netTransfer;

                                    cargoSelected.Clear();
                                }
                            }
                        }
                    }
                    else
                    {
                        GUILayout.Label("No cargo available for transfer.");
                    }

                    GUILayout.Space(16);

                    if (GUILayout.Button("Refresh Clusterio inventory"))
                    {
                        StartCoroutine(XenoIndustrySignpost.GetClusterioInventory(bodyName, clusterioInventory));
                    }
                }
            }

            GUILayout.EndVertical();

            // ---
            GUI.DragWindow();
        }
        private void OnWindowInternal(int id)
        {
            GUILayout.BeginVertical();

            string bodyName = "Kerbin"; // Since this is for launching, only Kerbin is possible for now

            if (!XenoIndustrySignpost.BodyHasServer(bodyName))
            {
                GUILayout.Label("This celestial body has no associated master server.");
            }
            else if (!XenoIndustrySignpost.IsConnected(bodyName))
            {
                GUILayout.Label("Cannot connect to Clusterio master server!");

                GUILayout.Label("Error: " + XenoIndustrySignpost.GetConnectionError(bodyName));

                if (GUILayout.Button("Refresh connection"))
                {
                    XenoIndustrySignpost.RefreshConnection(bodyName);
                }
            }
            else
            {
                // Show ship costs in editor
                if (HighLogic.LoadedScene == GameScenes.EDITOR)
                {
                    //GUILayout.Space(16);
                    //GUILayout.Label("", GUI.skin.horizontalSlider);
                    //GUILayout.Space(8);

                    if (latestLaunchCosts.Count > 0)
                    {
                        // Item names
                        GUILayout.BeginHorizontal();

                        GUILayout.BeginVertical();
                        GUILayout.Label("Resource");
                        GUILayout.Space(8);

                        foreach (KeyValuePair <string, int> kvPair in latestLaunchCosts)
                        {
                            GUILayout.Label(kvPair.Key + ":");
                        }
                        GUILayout.EndVertical();

                        // Item costs
                        GUILayout.BeginVertical();
                        GUILayout.Label("Required");
                        GUILayout.Space(8);

                        foreach (KeyValuePair <string, int> kvPair in latestLaunchCosts)
                        {
                            GUILayout.Label(kvPair.Value.ToString());
                        }
                        GUILayout.EndVertical();

                        // Items stocks
                        GUILayout.BeginVertical();
                        GUILayout.Label("Available");
                        GUILayout.Space(8);

                        foreach (KeyValuePair <string, int> kvPair in latestLaunchCosts)
                        {
                            GUILayout.Label((clusterioInventory.ContainsKey(kvPair.Key)) ? clusterioInventory[kvPair.Key].ToString() : 0.ToString());
                        }

                        GUILayout.EndVertical();
                        GUILayout.EndHorizontal();

                        GUILayout.Space(8);

                        if (GUILayout.Button("Refresh Clusterio inventory"))
                        {
                            StartCoroutine(XenoIndustrySignpost.GetClusterioInventory(bodyName, clusterioInventory));
                        }
                    }
                    else
                    {
                        GUILayout.Label("None");
                    }
                }
            }

            GUILayout.EndVertical();

            // ---
            GUI.DragWindow();
        }
        IEnumerator ClusterioPreflightResourceCheck()
        {
            Debug.Log("XenoIndustryLaunchCosts: PreflightResourceCheck");

            string bodyName = "Kerbin"; // Launching only works on Kerbin for now

            yield return(StartCoroutine(XenoIndustrySignpost.GetClusterioInventory(bodyName, clusterioInventory)));

            CalculateLaunchCosts(ref latestLaunchCosts);

            bool pass = true;

            foreach (KeyValuePair <string, int> kvPair in latestLaunchCosts)
            {
                if (!clusterioInventory.ContainsKey(kvPair.Key) || clusterioInventory[kvPair.Key] < kvPair.Value)
                {
                    pass = false;
                    break;
                }
            }

            if (!pass)
            { // Insufficient resources to launch
                string message = "You do not have enough resources to launch this vessel! This vessel needs: \n";

                foreach (KeyValuePair <string, int> kvPair in latestLaunchCosts)
                {
                    message += String.Format("\n {0} {1} (you have {2})", kvPair.Value, kvPair.Key, (clusterioInventory.ContainsKey(kvPair.Key)) ? clusterioInventory[kvPair.Key] : 0);
                }

                message += "\n";

                MultiOptionDialog dialog = new MultiOptionDialog("InsufficientClusterioResources", message, "Insufficient resources!", UISkinManager.GetSkin("KSP window 7"),
                                                                 new DialogGUIBase[]
                {
                    new DialogGUIButton("Unable to Launch", null)
                });

                PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), dialog, false, null);
            }
            else
            { // Launch can proceed
                string message = "Launching this vessel will require: \n";

                foreach (KeyValuePair <string, int> kvPair in latestLaunchCosts)
                {
                    message += String.Format("\n {0} {1} (you have {2})", kvPair.Value, kvPair.Key, (clusterioInventory.ContainsKey(kvPair.Key)) ? clusterioInventory[kvPair.Key] : 0);
                }

                message += "\n";

                MultiOptionDialog dialog = new MultiOptionDialog("ClusterioLaunchConfirmation", message, "Launch Possible", UISkinManager.GetSkin("KSP window 7"),
                                                                 new DialogGUIBase[]
                {
                    new DialogGUIButton("Launch", new Callback(ProceedToLaunch)),
                    new DialogGUIButton("Cancel", null)
                });

                PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), dialog, false, null);
            }
        }
        private void OnCoreWindowInternal(int id)
        {
            GUILayout.BeginVertical();

            string bodyName;

            if (FlightGlobals.ActiveVessel != null)
            {
                bodyName = FlightGlobals.ActiveVessel.mainBody.bodyName;
            }
            else
            {
                bodyName = "Kerbin";
            }

            if (!XenoIndustrySignpost.BodyHasServer(bodyName))
            {
                GUILayout.Label("This celestial body has no associated master server.");
            }
            else if (!XenoIndustrySignpost.IsConnected(bodyName))
            {
                GUILayout.Label("Cannot connect to Clusterio master server!");

                GUILayout.Label("Error: " + XenoIndustrySignpost.GetConnectionError(bodyName));

                if (GUILayout.Button("Refresh connection"))
                {
                    XenoIndustrySignpost.RefreshConnection(bodyName);
                }
            }
            else
            {
                // Clusterio inventory handling
                if (HighLogic.LoadedScene == GameScenes.SPACECENTER || HighLogic.LoadedScene == GameScenes.EDITOR || HighLogic.LoadedScene == GameScenes.TRACKSTATION || HighLogic.LoadedScene == GameScenes.FLIGHT)
                {
                    GUILayout.Label("Clusterio inventory:");

                    if (clusterioInventory.Count > 0)
                    {
                        GUILayout.BeginHorizontal();

                        GUILayout.BeginVertical();
                        foreach (KeyValuePair <string, int> kvPair in clusterioInventory)
                        {
                            GUILayout.Label(kvPair.Key + ":");
                        }
                        GUILayout.EndVertical();

                        GUILayout.BeginVertical();
                        foreach (KeyValuePair <string, int> kvPair in clusterioInventory)
                        {
                            GUILayout.Label(kvPair.Value.ToString());
                        }
                        GUILayout.EndVertical();

                        GUILayout.EndHorizontal();
                    }
                    else
                    {
                        GUILayout.Label("Clusterio inventory is empty.");
                    }

                    GUILayout.Space(8);

                    if (GUILayout.Button("Refresh Clusterio inventory"))
                    {
                        StartCoroutine(XenoIndustrySignpost.GetClusterioInventory(bodyName, clusterioInventory));
                    }
                }

                if (debug)
                {
                    if (GUILayout.Button("Add 1000 rocket component items"))
                    {
                        StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, "low-density-structure", 1000));
                        StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, "rocket-control-unit", 1000));
                        StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, "solar-panel", 1000));
                        StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, "uranium-238", 1000));
                        StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, "accumulator", 1000));
                        StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, "electric-mining-drill", 1000));
                        StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, "electric-furnace", 1000));
                        StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, "radar", 1000));
                        StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, "processing-unit", 1000));
                    }

                    if (GUILayout.Button("Add 1000 rocket fuel items"))
                    {
                        StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, "rocket-fuel", 1000));
                        StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, "explosives", 1000));
                    }
                    if (GUILayout.Button("Write out CelestialBody names"))
                    {
                        XenoIndustrySignpost.WriteOutCelestialBodies();
                    }
                }
            }

            GUILayout.EndVertical();

            // ---
            GUI.DragWindow();
        }
        private void OnClusterioWindowInternal(int id)
        {
            GUILayout.BeginVertical();

            string bodyName = "Kerbin"; // Science transfer can only be done on Kerbin right now

            if (!XenoIndustrySignpost.BodyHasServer(bodyName))
            {
                GUILayout.Label("This celestial body has no associated master server.");
            }
            else if (!XenoIndustrySignpost.IsConnected(bodyName))
            {
                GUILayout.Label("Cannot connect to Clusterio master server!");

                GUILayout.Label("Error: " + XenoIndustrySignpost.GetConnectionError(bodyName));

                if (GUILayout.Button("Refresh connection"))
                {
                    XenoIndustrySignpost.RefreshConnection(bodyName);
                }
            }
            else if (ResearchAndDevelopment.Instance == null)
            {
                GUILayout.Label("Cannot transfer science in sandbox mode!");
            }
            else
            {
                // Science transfer to Factorio
                //if (HighLogic.LoadedScene == GameScenes.SPACECENTER || HighLogic.LoadedScene == GameScenes.EDITOR || HighLogic.LoadedScene == GameScenes.TRACKSTATION || HighLogic.LoadedScene == GameScenes.FLIGHT)
                if (HighLogic.LoadedScene == GameScenes.SPACECENTER || HighLogic.LoadedScene == GameScenes.TRACKSTATION || HighLogic.LoadedScene == GameScenes.FLIGHT)
                {
                    /*GUILayout.Space(16);
                     * GUILayout.Label("", GUI.skin.horizontalSlider);
                     * GUILayout.Space(8);*/

                    int scienceTransferAmount = (int)(ResearchAndDevelopment.Instance.Science - (ResearchAndDevelopment.Instance.Science % sciencePerSciencePack));

                    GUILayout.Label(String.Format("Can use {0} science to transfer {1} science packs to Factorio.", scienceTransferAmount, (scienceTransferAmount / sciencePerSciencePack).ToString()));

                    GUILayout.Space(8);

                    if (GUILayout.Button("Transfer Science to Clusterio") && ResearchAndDevelopment.Instance != null)
                    {
                        Debug.Log("ClusterioTest: transferring science to Factorio");

                        StartCoroutine(TransferScienceToClusterio());
                    }

                    GUILayout.Space(8);

                    if (GUILayout.Button("Refresh Clusterio inventory"))
                    {
                        StartCoroutine(XenoIndustrySignpost.GetClusterioInventory(bodyName, clusterioInventory));
                    }
                }
            }

            GUILayout.EndVertical();

            // ---
            GUI.DragWindow();
        }