// Copyright © 2019 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto";
import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto";
import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
import "lorawan-stack/api/end_device.proto";
import "lorawan-stack/api/identifiers.proto";
import "lorawan-stack/api/messages.proto";
import "lorawan-stack/api/lorawan.proto";
import "protoc-gen-openapiv2/options/annotations.proto";

package ttn.lorawan.v3;

option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb";

// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798).
option (gogoproto.goproto_registration) = true;

// Response of GenerateDevAddr.
message GenerateDevAddrResponse {
  bytes dev_addr = 1 [
    (validate.rules).bytes = { len: 4, ignore_empty: true },
    (thethings.json.field) = {
      marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes",
      unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal4Bytes"
    },
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      type: STRING, format: "string", example: "\"2600ABCD\""
    }
  ];
}

// Request of GetDefaultMACSettings.
message GetDefaultMACSettingsRequest {
  option (thethings.flags.message) = { select: false, set: true };
  string frequency_plan_id = 1 [(validate.rules).string.max_len = 64];
  PHYVersion lorawan_phy_version = 2 [(validate.rules).enum.defined_only = true];
}

message GetNetIDResponse {
  bytes net_id = 1 [
    (validate.rules).bytes = { len: 3, ignore_empty: true },
    (thethings.json.field) = {
      marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes",
      unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes"
    },
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      type: STRING, format: "string", example: "\"000013\""
    }
  ];
}

message GetDeviceAdressPrefixesResponse {
  repeated bytes dev_addr_prefixes = 1 [
    (validate.rules).repeated = {
      items: {
        bytes: { len: 5 }
      }
    },
    (thethings.json.field) = {
      marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalDevAddrPrefixSlice",
      unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.UnmarshalDevAddrPrefixSlice"
    },
    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
      example: "[\"2600AB00/24\"]" // NOTE: openapiv2 generator does not support customizing array items.
    }
  ];
}

// The Ns service manages the Network Server.
service Ns {
  // GenerateDevAddr requests a device address assignment from the Network Server.
  rpc GenerateDevAddr(google.protobuf.Empty) returns (GenerateDevAddrResponse) {
    option (google.api.http) = {
      get: "/ns/dev_addr"
    };
  };
  // GetDefaultMACSettings retrieves the default MAC settings for a frequency plan.
  rpc GetDefaultMACSettings(GetDefaultMACSettingsRequest) returns (MACSettings) {
    option (google.api.http) = {
      get: "/ns/default_mac_settings/{frequency_plan_id}/{lorawan_phy_version}"
    };
  };

  rpc GetNetID(google.protobuf.Empty) returns (GetNetIDResponse) {
    option (google.api.http) = {
      get: "/ns/net_id"
    };
  };

  rpc GetDeviceAddressPrefixes(google.protobuf.Empty) returns (GetDeviceAdressPrefixesResponse) {
    option (google.api.http) = {
      get: "/ns/dev_addr_prefixes"
    };
  };
}

// The AsNs service connects an Application Server to a Network Server.
service AsNs {
  // Replace the entire downlink queue with the specified messages.
  // This can also be used to empty the queue by specifying no messages.
  // Note that this will trigger an immediate downlink if a downlink slot is available.
  rpc DownlinkQueueReplace(DownlinkQueueRequest) returns (google.protobuf.Empty);
  // Push downlink messages to the end of the downlink queue.
  // Note that this will trigger an immediate downlink if a downlink slot is available.
  rpc DownlinkQueuePush(DownlinkQueueRequest) returns (google.protobuf.Empty);
  // List the items currently in the downlink queue.
  rpc DownlinkQueueList(EndDeviceIdentifiers) returns (ApplicationDownlinks);
}

// The GsNs service connects a Gateway Server to a Network Server.
service GsNs {
  // Called by the Gateway Server when an uplink message arrives.
  rpc HandleUplink(UplinkMessage) returns (google.protobuf.Empty);
  // Called by the Gateway Server when a Tx acknowledgment arrives.
  rpc ReportTxAcknowledgment(GatewayTxAcknowledgment) returns (google.protobuf.Empty);
}

// The NsEndDeviceRegistry service allows clients to manage their end devices on the Network Server.
service NsEndDeviceRegistry {
  // Get returns the device that matches the given identifiers.
  // If there are multiple matches, an error will be returned.
  rpc Get(GetEndDeviceRequest) returns (EndDevice) {
    option (google.api.http) = {
      get: "/ns/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}"
    };
  };

  // Set creates or updates the device.
  rpc Set(SetEndDeviceRequest) returns (EndDevice) {
    option (google.api.http) = {
      put: "/ns/applications/{end_device.ids.application_ids.application_id}/devices/{end_device.ids.device_id}"
      body: "*"
      additional_bindings {
        post: "/ns/applications/{end_device.ids.application_ids.application_id}/devices"
        body: "*"
      };
    };
  };

  // ResetFactoryDefaults resets device state to factory defaults.
  rpc ResetFactoryDefaults(ResetAndGetEndDeviceRequest) returns (EndDevice) {
    option (google.api.http) = {
      patch: "/ns/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}"
      body: "*"
    };
  };

  // Delete deletes the device that matches the given identifiers.
  // If there are multiple matches, an error will be returned.
  rpc Delete(EndDeviceIdentifiers) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/ns/applications/{application_ids.application_id}/devices/{device_id}"
    };
  };
}
