{ config, lib, pkgs, ... }:

with lib;

let
  pkg = pkgs.ostinato;
  cfg = config.services.ostinato;
  configFile = pkgs.writeText "drone.ini" ''
    [General]
    RateAccuracy=${cfg.rateAccuracy}

    [RpcServer]
    Address=${cfg.rpcServer.address}

    [PortList]
    Include=${concatStringsSep "," cfg.portList.include}
    Exclude=${concatStringsSep "," cfg.portList.exclude}
  '';

in
{

  ###### interface

  options = {

    services.ostinato = {

      enable = mkEnableOption "Ostinato agent-controller (Drone)";

      port = mkOption {
        type = types.int;
        default = 7878;
        description = ''
          Port to listen on.
        '';
      };

      rateAccuracy = mkOption {
        type = types.enum [ "High" "Low" ];
        default = "High";
        description = ''
          To ensure that the actual transmit rate is as close as possible to
          the configured transmit rate, Drone runs a busy-wait loop.
          While this provides the maximum accuracy possible, the CPU
          utilization is 100% while the transmit is on. You can however,
          sacrifice the accuracy to reduce the CPU load.
        '';
      };

      rpcServer = {
        address = mkOption {
          type = types.str;
          default = "0.0.0.0";
          description = ''
            By default, the Drone RPC server will listen on all interfaces and
            local IPv4 adresses for incoming connections from clients.  Specify
            a single IPv4 or IPv6 address if you want to restrict that.
            To listen on any IPv6 address, use ::
          '';
        };
      };

      portList = {
        include = mkOption {
          type = types.listOf types.str;
          default = [];
          example = ''[ "eth*" "lo*" ]'';
          description = ''
            For a port to pass the filter and appear on the port list managed
            by drone, it be allowed by this include list.
          '';
        };
        exclude = mkOption {
          type = types.listOf types.str;
          default = [];
          example = ''[ "usbmon*" "eth0" ]'';
          description = ''
            A list of ports does not appear on the port list managed by drone.
          '';
        };
      };

    };

  };

  ###### implementation

  config = mkIf cfg.enable {

    environment.systemPackages = [ pkg ];

    systemd.services.drone = {
      description = "Ostinato agent-controller";
      wantedBy = [ "multi-user.target" ];
      script = ''
        ${pkg}/bin/drone ${toString cfg.port} ${configFile}
      '';
    };

  };

}
