{ lib, config, ... }:
let
  # Returns the uppercased first label or number of a string
  firstUpperAlnum =
    str:
      lib.mapNullable
        lib.head (builtins.match "[^A-Z0-9]*([A-Z0-9]).*" (lib.toUpper str));

  userType = lib.types.submodule (
    { name, ... }: {
      options = {
        departure = lib.mkOption {
          type = markerType;
          default = {};
        };
      };

      config = {
        departure.style.label = lib.mkDefault (firstUpperAlnum name);
      };
    });

  markerType = lib.types.submodule {
    options = {
      location = lib.mkOption {
        type = lib.types.nullOr lib.types.str;
        default = null;
      };

      style.label = lib.mkOption {
        type = lib.types.nullOr
          (lib.types.strMatching "[0-9A-Z]");
        default = null;
      };
    };
  };
in
{
  options = {
    users = lib.mkOption {
      type = lib.types.attrsOf userType;
    };

    map.markers = lib.mkOption {
      type = lib.types.listOf markerType;
    };
  };

  config = {
    users = {
      manuel.departure = {
        location = "Switzerland";
      };

      ganondorf.departure =  {
        location = "Argentinia";
      };
    };

    map = {
      markers = lib.filter
        (marker: marker.location != null)
        (
          lib.concatMap (user: [
            user.departure
          ]) (lib.attrValues config.users));

      center = lib.mkIf
        (lib.length config.map.markers >= 1)
        null;

      zoom = lib.mkIf
        (lib.length config.map.markers >= 2)
        null;
    };

    requestParams =
      let
        paramForMarker =
          marker:
            let
              attributes =
                lib.optional (marker.style.label != null)
                "label:${marker.style.label}"
                ++ [
                  "$(${config.scripts.geocode}/bin/geocode ${
                    lib.escapeShellArg marker.location
                  })"
                ];
            in
              "markers=\"${lib.concatStringsSep "|" attributes}\"";
      in builtins.map paramForMarker config.map.markers;
  };
}