context: using kOS.Module;
  using kOS.Safe.Encapsulation;
  using kOS.Suffixed.Part;
  using kOS.Suffixed.PartModuleField;
  using KSP.UI.Screens;
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text.RegularExpressions;
  
  namespace kOS.Suffixed
  {
      partial class VesselTarget
      {
          public StageValues StageValues { get; private set; }
  
          //TODO: share these between all CPUs (or maybe have one VesselTarget instance?)
          //TODO: create single list of parts and _slices_ for `children`
          //..... [root, child1, child2, ..., part1-1, part1-2, ..., part2-1, ... heap ;)
          private PartValue rootPart;
          private DecouplerValue nextDecoupler;
          private ListValue<PartValue> allParts;
          private ListValue<DockingPortValue> dockingPorts;
          private ListValue<DecouplerValue> decouplers;
          private Dictionary<global::Part, PartValue> partCache;
  
          private void InvalidateParts()
          {
              StageValues.stale = true;
  
              rootPart = null;
              nextDecoupler = null;
              allParts = null;
              dockingPorts = null;
              decouplers = null;
              partCache = null;
          }
          public PartValue Root
          {
              get
              {
                  if (allParts == null)
                      ConstructParts();
                  return rootPart;
              }
          }
          public DecouplerValue NextDecoupler
          {
              get
              {
                  if (allParts == null)
                      ConstructParts();
                  return nextDecoupler;
              }
          }
          public ListValue<PartValue> Parts
          {
              get
              {
                  if (allParts == null)
                      ConstructParts();
                  return allParts;
              }
          }
          public ListValue<DockingPortValue> DockingPorts
          {
              get
              {
                  if (dockingPorts == null)
                      ConstructParts();
                  return dockingPorts;
              }
          }
          public ListValue<DecouplerValue> Decouplers
          {
              get
              {
                  if (decouplers == null)
                      ConstructParts();
                  return decouplers;
              }
          }
          public PartValue this[global::Part part]
          {
              get
              {
                  if (allParts == null)
                      ConstructParts();
                  if (partCache.ContainsKey(part)) {
                      return partCache[part];
                  }
                  // returning null is very dangerous and must be guarded in other kOS code,
                  // because kOS itself has no ability to return a null value
                  return null;
              }
          }
          private void ConstructParts()
          {
              rootPart = null;
              nextDecoupler = null;
              allParts = new ListValue<PartValue>();
              dockingPorts = new ListValue<DockingPortValue>();
              decouplers = new ListValue<DecouplerValue>();
              partCache = new Dictionary<global::Part, PartValue>();
  
              ConstructPart(Vessel.rootPart, null, null);
  
              allParts.IsReadOnly = true;
              dockingPorts.IsReadOnly = true;
              decouplers.IsReadOnly = true;
          }
          private void ConstructPart(global::Part part, PartValue parent, DecouplerValue decoupler)
          {
              if (part.State == PartStates.DEAD || part.transform == null)
                  return;
  
              PartValue self = null;
              foreach (var module in part.Modules)
              {
                  var engine = module as IEngineStatus;
                  if (engine != null)
                  {
                      self = new EngineValue(Shared, part, parent, decoupler);
                      break;
                  }
                  if (module is IStageSeparator)
                  {
                      var dock = module as ModuleDockingNode;
                      if (dock != null)
                      {
                          var port = new DockingPortValue(Shared, part, parent, decoupler, dock);
                          self = port;
                          dockingPorts.Add(port);
                          if (!module.StagingEnabled())
                              break;
                          decoupler = port;
                          decouplers.Add(decoupler);
                      }
                      else
                      {
                          // ignore anything with staging disabled and continue the search
                          // this can e.g. be heat shield or some sensor with integrated decoupler
                          if (!module.StagingEnabled())
                              continue;
                          if (module is LaunchClamp)
                              self = decoupler = new LaunchClampValue(Shared, part, parent, decoupler);
                          else if (module is ModuleDecouple || module is ModuleAnchoredDecoupler)
                              self = decoupler = new DecouplerValue(Shared, part, parent, decoupler);
                          else // ModuleServiceModule ?
                              continue; // rather continue the search
                          decouplers.Add(decoupler);
                      }
                      // ignore leftover decouplers
                      if (decoupler == null || decoupler.Part.inverseStage >= StageManager.CurrentStage)
                          break;
                      // check if we just created closer decoupler (see StageValues.CreatePartSet)
                      if (nextDecoupler == null || decoupler.Part.inverseStage > nextDecoupler.Part.inverseStage)
                          nextDecoupler = decoupler;
                      break;
                  }
                  var sensor = module as ModuleEnviroSensor;
                  if (sensor != null)
                  {
                      self = new SensorValue(Shared, part, parent, decoupler, sensor);
                      break;
                  }
              }
              if (self == null)
                  self = new PartValue(Shared, part, parent, decoupler);
              if (rootPart == null)
                  rootPart = self;
              partCache[part] = self;
              allParts.Add(self);
              foreach (var child in part.children)
                  ConstructPart(child, self, decoupler);
              self.Children.IsReadOnly = true;
          }
  
          private ListValue GetPartsDubbed(StringValue searchTerm)
          {
              // Get the list of all the parts where the part's API name OR its GUI title or its tag name matches.
              List<global::Part> kspParts = new List<global::Part>();
              kspParts.AddRange(GetRawPartsNamed(searchTerm));
              kspParts.AddRange(GetRawPartsTitled(searchTerm));
              kspParts.AddRange(GetRawPartsTagged(searchTerm));
  
              // The "Distinct" operation is there because it's possible for someone to use a tag name that matches the part name.
              return PartValueFactory.Construct(kspParts.Distinct(), Shared);
          }
  
          private ListValue GetPartsDubbedPattern(StringValue searchPattern)
          {
              // Prepare case-insensivie regex.
              Regex r = new Regex(searchPattern, RegexOptions.IgnoreCase);
              // Get the list of all the parts where the part's API name OR its GUI title or its tag name matches the pattern.
              List<global::Part> kspParts = new List<global::Part>();
              kspParts.AddRange(GetRawPartsNamedPattern(r));
              kspParts.AddRange(GetRawPartsTitledPattern(r));
              kspParts.AddRange(GetRawPartsTaggedPattern(r));
  
              // The "Distinct" operation is there because it's possible for someone to use a tag name that matches the part name.
              return PartValueFactory.Construct(kspParts.Distinct(), Shared);
          }
  
          private ListValue GetPartsNamed(StringValue partName)
          {
              return PartValueFactory.Construct(GetRawPartsNamed(partName), Shared);
          }
  
          private IEnumerable<global::Part> GetRawPartsNamed(string partName)
          {
              // Get the list of all the parts where the part's KSP API title matches:
              return Vessel.parts.FindAll(
                  part => String.Equals(part.name, partName, StringComparison.CurrentCultureIgnoreCase));
          }
  
          private ListValue GetPartsNamedPattern(StringValue partNamePattern)
          {
              // Prepare case-insensivie regex.
  
gt:             Regex r = new Regex(partNamePattern, RegexOptions.IgnoreCase);