{ 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 = {};
        };

        arrival = lib.mkOption {
          type = markerType;
          default = {};
        };
      };

      config = {
        departure.style.label = lib.mkDefault (firstUpperAlnum name);
        arrival.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;
        };

        color = lib.mkOption {
          type = colorType;
          default = "red";
        };

        size = lib.mkOption {
          type = lib.types.enum [
            "tiny"
            "small"
            "medium"
            "large"
          ];

          default = "medium";
        };
      };
    };
  };

  colorType = lib.types.either
    (lib.types.strMatching "0x[0-9A-F]{3}[0-9A-F]{3}?")
    (lib.types.enum [
      "black"
      "blue"
      "brown"
      "gray"
      "green"
      "orange"
      "purple"
      "red"
      "white"
      "yellow"
    ]);
in
{
  imports = [
    ./path.nix
  ];

  options = {
    users = lib.mkOption {
      type = lib.types.attrsOf userType;
    };

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

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

        arrival = {
          location = "Chile";
        };

        pathStyle = {
          weight = 10;
          color = "purple";
          geodesic = true;
        };
      };

      ganondorf.departure =  {
        location = "Argentinia";

        style = {
          color = "green";
        };
      };
    };

    map = {
      markers = lib.filter
        (marker: marker.location != null)
        (
          lib.concatMap (user: [
            user.departure
            user.arrival
          ]) (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
              size = {
                tiny = "tiny";
                small = "small";
                medium = "mid";
                large = null;
              }.${marker.style.size};
              attributes =
                lib.optional
                  (marker.style.label != null)
                  "label:${marker.style.label}"
                ++ lib.optional
                  (size != null)
                  "size:${size}"
                ++ [
                  "color:${marker.style.color}"
                  "$(${config.scripts.geocode}/bin/geocode ${
                    lib.escapeShellArg marker.location
                  })"
                ];
            in
              "markers=\"${lib.concatStringsSep "|" attributes}\"";
      in builtins.map paramForMarker config.map.markers;
  };
}