text: /*----------------------------------------------------------------------------*/
  /* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
  /* Open Source Software - may be modified and shared by FRC teams. The code   */
  /* must be accompanied by the FIRST BSD license file in the root directory of */
  /* the project.                                                               */
  /*----------------------------------------------------------------------------*/
  
  #include "ConfigurableSourceImpl.h"
  
  #include <wpi/timestamp.h>
  
  #include "Handle.h"
  #include "Instance.h"
  #include "Log.h"
  #include "Notifier.h"
  
  using namespace cs;
  
  ConfigurableSourceImpl::ConfigurableSourceImpl(const wpi::Twine& name,
                                                 wpi::Logger& logger,
                                                 Notifier& notifier,
                                                 Telemetry& telemetry,
                                                 const VideoMode& mode)
      : SourceImpl{name, logger, notifier, telemetry} {
    m_mode = mode;
    m_videoModes.push_back(m_mode);
  }
  
  ConfigurableSourceImpl::~ConfigurableSourceImpl() {}
  
  void ConfigurableSourceImpl::Start() {
    m_notifier.NotifySource(*this, CS_SOURCE_CONNECTED);
    m_notifier.NotifySource(*this, CS_SOURCE_VIDEOMODES_UPDATED);
    m_notifier.NotifySourceVideoMode(*this, m_mode);
  }
  
  bool ConfigurableSourceImpl::SetVideoMode(const VideoMode& mode,
                                            CS_Status* status) {
    {
      std::lock_guard<wpi::mutex> lock(m_mutex);
      m_mode = mode;
      m_videoModes[0] = mode;
    }
    m_notifier.NotifySourceVideoMode(*this, mode);
    return true;
  }
  
  void ConfigurableSourceImpl::NumSinksChanged() {
    // ignore
  }
  
  void ConfigurableSourceImpl::NumSinksEnabledChanged() {
    // ignore
  }
  
  void ConfigurableSourceImpl::NotifyError(const wpi::Twine& msg) {
    PutError(msg, wpi::Now());
  }
  
  int ConfigurableSourceImpl::CreateProperty(const wpi::Twine& name,
                                             CS_PropertyKind kind, int minimum,
                                             int maximum, int step,
                                             int defaultValue, int value) {
    std::lock_guard<wpi::mutex> lock(m_mutex);
    int ndx = CreateOrUpdateProperty(name,
                                     [=] {
                                       return wpi::make_unique<PropertyImpl>(
                                           name, kind, minimum, maximum, step,
                                           defaultValue, value);
                                     },
                                     [&](PropertyImpl& prop) {
                                       // update all but value
                                       prop.propKind = kind;
                                       prop.minimum = minimum;
                                       prop.maximum = maximum;
                                       prop.step = step;
                                       prop.defaultValue = defaultValue;
                                       value = prop.value;
                                     });
    m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CREATED, name, ndx,
                                    kind, value, wpi::Twine{});
    return ndx;
  }
  
  int ConfigurableSourceImpl::CreateProperty(
      const wpi::Twine& name, CS_PropertyKind kind, int minimum, int maximum,
      int step, int defaultValue, int value,
      std::function<void(CS_Property property)> onChange) {
    // TODO
    return 0;
  }
  
  void ConfigurableSourceImpl::SetEnumPropertyChoices(
      int property, wpi::ArrayRef<std::string> choices, CS_Status* status) {
    std::lock_guard<wpi::mutex> lock(m_mutex);
    auto prop = GetProperty(property);
    if (!prop) {
      *status = CS_INVALID_PROPERTY;
      return;
    }
    if (prop->propKind != CS_PROP_ENUM) {
      *status = CS_WRONG_PROPERTY_TYPE;
      return;
    }
    prop->enumChoices = choices;
    m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CHOICES_UPDATED,
                                    prop->name, property, CS_PROP_ENUM,
                                    prop->value, wpi::Twine{});
  }
  