// Copyright 2020 The MediaPipe Authors.
//
// 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.

#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/port/status.h"

namespace mediapipe {

constexpr char kPresenceTag[] = "PRESENCE";
constexpr char kPacketTag[] = "PACKET";

// For each non empty input packet, emits a single output packet containing a
// boolean value "true", "false" in response to empty packets (a.k.a. timestamp
// bound updates) This can be used to "flag" the presence of an arbitrary packet
// type as input into a downstream calculator.
//
// Inputs:
//   PACKET - any type.
//
// Outputs:
//   PRESENCE - bool.
//     "true" if packet is not empty, "false" if there's timestamp bound update
//     instead.
//
// Examples:
// node: {
//   calculator: "PacketPresenceCalculator"
//   input_stream: "PACKET:packet"
//   output_stream: "PRESENCE:presence"
// }
//
// This calculator can be used in conjuction with GateCalculator in order to
// allow/disallow processing. For instance:
// node: {
//   calculator: "PacketPresenceCalculator"
//   input_stream: "PACKET:value"
//   output_stream: "PRESENCE:disallow_if_present"
// }
// node {
//   calculator: "GateCalculator"
//   input_stream: "image"
//   input_stream: "DISALLOW:disallow_if_present"
//   output_stream: "image_for_processing"
//   options: {
//     [mediapipe.GateCalculatorOptions.ext] {
//       empty_packets_as_allow: true
//     }
//   }
// }
class PacketPresenceCalculator : public CalculatorBase {
 public:
  static absl::Status GetContract(CalculatorContract* cc) {
    cc->Inputs().Tag(kPacketTag).SetAny();
    cc->Outputs().Tag(kPresenceTag).Set<bool>();
    // Process() function is invoked in response to input stream timestamp
    // bound updates.
    cc->SetProcessTimestampBounds(true);
    return absl::OkStatus();
  }

  absl::Status Open(CalculatorContext* cc) override {
    cc->SetOffset(TimestampDiff(0));
    return absl::OkStatus();
  }

  absl::Status Process(CalculatorContext* cc) final {
    cc->Outputs()
        .Tag(kPresenceTag)
        .AddPacket(MakePacket<bool>(!cc->Inputs().Tag(kPacketTag).IsEmpty())
                       .At(cc->InputTimestamp()));
    return absl::OkStatus();
  }
};
REGISTER_CALCULATOR(PacketPresenceCalculator);

}  // namespace mediapipe
