# Configuration API

Configuration options are settings that are exposed via a network interface from DV runtime. The way configuration
options are implemented, the user can change the options dynamically at runtime, either via the DV gui, or with the
command line utility.

## Definition of configuration options

### Static vs Runtime definition

Configuration options should be added in the static function `static void initConfigOptions(dv::RuntimeConfig &config)`
in your module. This static function gets executed upon adding the module to the project, but *before* it has started.
The function is used to populate the configuration before starting the module.

One can add configuration options at runtime, by accessing the modules `config.add` function at runtime. This makes
sense, for example, in the constructor.

### Selection of config option types

The following configuration option types are available

| Name      | Value type    | Description                      | GUI representation                                           |
| --------- | ------------- | -------------------------------- | ------------------------------------------------------------ |
| int       | 32bit integer | Accepts a range                  | ![numeric option](/assets/dv-screenshots/numeric-option.png) |
| long      | 64bit integer | Accepts a range                  | ![numeric option](/assets/dv-screenshots/numeric-option.png) |
| float     | 32bit float   | Accepts a range                  | ![numeric option](/assets/dv-screenshots/numeric-option.png) |
| double    | 64bit float   | Accepts a range                  | ![numeric option](/assets/dv-screenshots/numeric-option.png) |
| bool      | boolean       | Displays a checkbok              | ![checkbox option](/assets/dv-screenshots/bool-option.png)   |
| string    | string        | Displays an edit field           | ![string option](/assets/dv-screenshots/string-option.png)   |
| button    | boolean       | Displays a button                | ![button option](/assets/dv-screenshots/button-option.png)   |
| fileSave  | string        | Shows a file save dialog         | ![file option](/assets/dv-screenshots/file-option.png)       |
| fileOpen  | string        | Shows a file open dialog         | ![file option](/assets/dv-screenshots/file-option.png)       |
| directory | string        | Shows a directory chooser dialog | ![file option](/assets/dv-screenshots/file-option.png)       |
| list      | string        | Shows a dropdown menu            | ![list option](/assets/dv-screenshots/list-option.png)       |

### Adding a numerical option (int, long, float, double)

A numerical option can be added as follows

```C++
config.add(<name>, dv::ConfigOption::intOption(<description>, <defaultValue>, <minValue>, <maxValue>))
config.add(<name>, dv::ConfigOption::longOption(<description>, <defaultValue>, <minValue>, <maxValue>))
config.add(<name>, dv::ConfigOption::floatOption(<description>, <defaultValue>, <minValue>, <maxValue>))
config.add(<name>, dv::ConfigOption::doubleOption(<description>, <defaultValue>, <minValue>, <maxValue>))
```

- `name` The name of the configuration option. Should be a camel cased variable compliant name
- `description` A description of the purpose of the config option
- `defaultValue` The value this config option should get initialized with
- `minValue` The minimum allowed value for this config option. This is optional. If not set, the range defaults to the
  order of magnitude of the `defaultValue`
- `maxValue` The maximum allowed value for this config option. This is optional. If not set, the range defaults to the
  order of magnitude of the `defaultValue`

### Adding a string option

```C++
config.add(<name>, dv::ConfigOption::stringOption(<description>, <defaultValue>))
```

- `name` The name of the configuration option. Should be a camel cased variable compliant name
- `defaultValue` The default string to be used in this option

### Adding a file option

A file option without a default value can be added as follows:

```C++
config.add(<name>, dv::ConfigOption::fileOpenOption(<description>, <allowedExtensions>))
config.add(<name>, dv::ConfigOption::fileSaveOption(<description>, <allowedExtensions>))
```

A file option with a default value can be added as follows:

```C++
config.add(<name>, dv::ConfigOption::fileOpenOption(<description>, <defaultValue>, <allowedExtensions>))
config.add(<name>, dv::ConfigOption::fileSaveOption(<description>, <defaultValue>, <allowedExtensions>))
```

A directory option can be added with:

```C++
config.add(<name>, dv::ConfigOption::directoryOption(<description>, <defaultValue>))
```

- `name` The name of the configuration option. Should be a camel cased variable compliant name
- `defaultValue` The default file path for the option. This is optional. If not set, the default is an empty string.
- `description` A description of the purpose of the config option
- `allowedExtensions` A comma separated list of the allowed extensions. Extensions should just be the ending, without
  dot. E.g. `jpg,jpeg,png`

### Adding a button option

```C++
config.add(<name>, dv::ConfigOption::buttonOption(<description>, <buttonLabel>))
```

- `name` The name of the configuration option. Should be a camel cased variable compliant name
- `description` A description of the purpose of the config option
- `buttonLabel` The string that should be displayed on the button

A button option has a boolean value type. By default, the value is false. As soon as a user clicks the button, the
button gets disabled and the value set to true to indicate the request. To re-arm the button, reset the value back to
false.

### Adding a list option

```C++
config.add(<name>, dv::ConfigOption::listOption(<description>, <defaultChoice>, {<choice1>, <choice2>, ...}, <allowMultipleSelection>))
```

- `name` The name of the configuration option. Should be a camel cased variable compliant name
- `description` A description of the purpose of the config option
- `defaultChoice` The default choice for this option. This can either be a string or the index of the choice in the list
  of choices. In case of a string, the string has to be present in the list of choices as well.
- `{<choice1>, <choice2>, ...}` A vector of strings, denoting all possible selections for this option

## Reading config options

Config values can be read easily with

```C++
bool value = config.getBool(<name>)
int value = config.getInt(<name>)
long value = config.getLong(<name>)
float value = config.getFloat(<name>)
double value = config.getDouble(<name>)
std::string value = config.getString(<name>)
```

The get functions are not compile time type checked. Make sure to use the correct getter function for your data type to
prevent a runtime error. The correct value type for the config options can be obtained from the table above.

### Validity of read values

Config option values can be read safely everywhere in the code. Outside changes to the option values are applied in
between calls to the *run* and *configUpdate* functions. During the function calls, the value of config options can be
seen as constant. (Unless explicitly changed in your code).

### Performance considerations

Looking up a config value requies a string key lookup in a hash map. This is a very efficient operation, however, when
used in a hot loop in the code, it can add overhead.

To mitigate this problem, the module api provides the opportunity to override a `void configUpdate()` function. This
function, compared to the run function, only runs when a config value actually has changed. You can then copy the value
of the config variable to an instance member. E.g.

```C++
void configUpdate() override {
    this->myConfigOption = config.getInt("myConfigOption");
}
```

During the `run` function call, one can then just use the instance member integer, rather than querying the value of the
config option over and over again.

## Setting config options

Config options can be set everywhere in the code. The option assumes the new value immediately after setting it. To set
a config option, use the correct corresponding function

```C++
config.setBool(<name>, <value>)
config.setInt(<name>, <value>)
config.setLong(<name>, <value>)
config.setFloat(<name>, <value>)
config.setDouble(<name>, <value>)
config.setString(<name>, <value>)
```

Again, it is important to use the correct function for the options value type. Note that by setting config options from
code, you are overwriting changes the user has made to the options.

### Re-enabling buttons

Buttons have a boolean value type. As long as their config option value is `false`, the button appears armed and can be
clicked. Clicking the button sets the value to `true`, to indicate a click request. After performing the required
action, set the value back to `false` to re-enable the button.

Example:

```C++
void configUpdate() override {
    // check if user has clicked the button
    if(config.getBool("myButton") {

        // do complicated action..

        // re-enable button
        config.setBool("myButton", false);
    }
}
```
