public override bool Write(GH_IWriter writer)
        {
            writer.SetBoolean("UseDefaultCache", UseDefaultCache);
            writer.SetString("KitName", Kit.Name);
            var owSer = string.Join("\n", OutputWrappers.Select(ow => $"{ow.ServerUrl}\t{ow.StreamId}\t{ow.CommitId}"));

            writer.SetString("OutputWrappers", owSer);
            return(base.Write(writer));
        }
        public override bool Read(GH_IReader reader)
        {
            UseDefaultCache = reader.GetBoolean("UseDefaultCache");

            var wrappersRaw  = reader.GetString("OutputWrappers");
            var wrapperLines = wrappersRaw.Split('\n');

            if (wrapperLines != null && wrapperLines.Length != 0 && wrappersRaw != "")
            {
                foreach (var line in wrapperLines)
                {
                    var pieces = line.Split('\t');
                    OutputWrappers.Add(new StreamWrapper {
                        ServerUrl = pieces[0], StreamId = pieces[1], CommitId = pieces[2]
                    });
                }
            }

            var kitName = "";

            reader.TryGetString("KitName", ref kitName);

            if (kitName != "")
            {
                try
                {
                    SetConverterFromKit(kitName);
                }
                catch (Exception)
                {
                    AddRuntimeMessage(GH_RuntimeMessageLevel.Warning,
                                      $"Could not find the {kitName} kit on this machine. Do you have it installed? \n Will fallback to the default one.");
                    SetDefaultKitAndConverter();
                }
            }
            else
            {
                SetDefaultKitAndConverter();
            }

            return(base.Read(reader));
        }
        //GH_Structure<IGH_Goo> transportsInput;
        /// <summary>
        /// Registers all the output parameters for this component.
        /// </summary>
        /// <summary>
        /// This is the method that actually does the work.
        /// </summary>
        /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param>
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            DA.DisableGapLogic();

            if (!foundKit)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "No kit found on this machine.");
                return;
            }

            if (RunCount == 1)
            {
                CreateCancelationToken();
                OutputWrappers = new List <StreamWrapper>();
                DA.GetDataTree(0, out dataInput);

                //the active document may have changed
                Converter.SetContextDocument(RhinoDoc.ActiveDoc);

                // Note: this method actually converts the objects to speckle too
                converted = Extras.Utilities.DataTreeToNestedLists(dataInput, Converter, source.Token, () =>
                {
                    //ReportProgress("Conversion", Math.Round(convertedCount++ / (double)DataInput.DataCount, 2));
                });
            }

            //if (RunCount > 1)
            //  return;

            if (InPreSolve)
            {
                string messageInput = "";

                IGH_Goo transportInput = null;
                DA.GetData(1, ref transportInput);
                DA.GetData(2, ref messageInput);
                var transportsInput = new List <IGH_Goo> {
                    transportInput
                };
                //var transportsInput = Params.Input[1].VolatileData.AllData(true).Select(x => x).ToList();
                Tracker.TrackPageview("send", "sync");

                var task = Task.Run(async() =>
                {
                    if (converted.Count == 0)
                    {
                        AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Zero objects converted successfully. Send stopped.");
                        return(null);
                    }

                    var ObjectToSend      = new Base();
                    ObjectToSend["@data"] = converted;
                    var TotalObjectCount  = ObjectToSend.GetTotalChildrenCount();

                    if (source.Token.IsCancellationRequested)
                    {
                        Message = "Out of time";
                        return(null);
                    }

                    // Part 2: create transports
                    var Transports = new List <ITransport>();

                    if (transportsInput.Count() == 0)
                    {
                        // TODO: Set default account + "default" user stream
                    }

                    var transportBranches = new Dictionary <ITransport, string>();
                    int t = 0;
                    foreach (var data in transportsInput)
                    {
                        var transport = data.GetType().GetProperty("Value").GetValue(data);

                        if (transport is string s)
                        {
                            try
                            {
                                transport = new StreamWrapper(s);
                            }
                            catch (Exception e)
                            {
                                // TODO: Check this with team.
                                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, e.Message);
                            }
                        }

                        if (transport is StreamWrapper sw)
                        {
                            if (sw.Type == StreamWrapperType.Undefined)
                            {
                                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Input stream is invalid.");
                                continue;
                            }

                            if (sw.Type == StreamWrapperType.Commit)
                            {
                                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Cannot push to a specific commit stream url.");
                                continue;
                            }

                            if (sw.Type == StreamWrapperType.Object)
                            {
                                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Cannot push to a specific object stream url.");
                                continue;
                            }

                            Account acc;
                            try
                            {
                                acc = sw.GetAccount().Result;
                            }
                            catch (Exception e)
                            {
                                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, e.InnerException?.Message ?? e.Message);
                                continue;
                            }

                            var serverTransport = new ServerTransport(acc, sw.StreamId)
                            {
                                TransportName = $"T{t}"
                            };
                            transportBranches.Add(serverTransport, sw.BranchName ?? "main");
                            Transports.Add(serverTransport);
                        }
                        else if (transport is ITransport otherTransport)
                        {
                            otherTransport.TransportName = $"T{t}";
                            Transports.Add(otherTransport);
                        }

                        t++;
                    }

                    if (Transports.Count == 0)
                    {
                        AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Could not identify any valid transports to send to.");
                        return(null);
                    }

                    // Part 3: actually send stuff!
                    if (source.Token.IsCancellationRequested)
                    {
                        Message = "Out of time";
                        return(null);
                    }

                    // Part 3.1: persist the objects
                    var BaseId = await Operations.Send(
                        ObjectToSend,
                        source.Token,
                        Transports,
                        useDefaultCache: UseDefaultCache,
                        onProgressAction: y => { },
                        onErrorAction: (x, z) => { },
                        disposeTransports: true);

                    var message = messageInput;//.get_FirstItem(true).Value;
                    if (message == "")
                    {
                        message = $"Pushed {TotalObjectCount} elements from Grasshopper.";
                    }


                    var prevCommits = new List <StreamWrapper>();

                    foreach (var transport in Transports)
                    {
                        if (source.Token.IsCancellationRequested)
                        {
                            Message = "Out of time";
                            return(null);
                        }

                        if (!(transport is ServerTransport))
                        {
                            continue; // skip non-server transports (for now)
                        }

                        try
                        {
                            var client = new Client(((ServerTransport)transport).Account);
                            var branch = transportBranches.ContainsKey(transport) ? transportBranches[transport] : "main";

                            var commitCreateInput = new CommitCreateInput
                            {
                                branchName        = branch,
                                message           = message,
                                objectId          = BaseId,
                                streamId          = ((ServerTransport)transport).StreamId,
                                sourceApplication = Applications.Grasshopper
                            };

                            // Check to see if we have a previous commit; if so set it.
                            var prevCommit = prevCommits.FirstOrDefault(c =>
                                                                        c.ServerUrl == client.ServerUrl && c.StreamId == ((ServerTransport)transport).StreamId);
                            if (prevCommit != null)
                            {
                                commitCreateInput.parents = new List <string>()
                                {
                                    prevCommit.CommitId
                                };
                            }

                            var commitId = await client.CommitCreate(source.Token, commitCreateInput);

                            var wrapper = new StreamWrapper($"{client.Account.serverInfo.url}/streams/{((ServerTransport)transport).StreamId}/commits/{commitId}?u={client.Account.userInfo.id}");
                            prevCommits.Add(wrapper);
                        }
                        catch (Exception e)
                        {
                            AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.Message);
                            return(null);
                        }
                    }

                    if (source.Token.IsCancellationRequested)
                    {
                        Message = "Out of time";
                        return(null);
                    }

                    return(prevCommits);
                }, source.Token);

                TaskList.Add(task);
                return;
            }

            if (source.IsCancellationRequested)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Run out of time!");
            }
            else if (!GetSolveResults(DA, out List <StreamWrapper> outputWrappers))
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Not running multithread");
            }
            else
            {
                OutputWrappers.AddRange(outputWrappers);
                DA.SetDataList(0, outputWrappers);
                return;
            }
        }