/*
 * Copyright 2018 The Cartographer 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 "cartographer/io/proto_stream_deserializer.h"

#include <memory>

#include "cartographer/io/internal/in_memory_proto_stream.h"
#include "cartographer/io/internal/testing/test_helpers.h"
#include "glog/logging.h"
#include "gmock/gmock.h"
#include "google/protobuf/text_format.h"
#include "google/protobuf/util/message_differencer.h"
#include "gtest/gtest.h"

namespace cartographer {
namespace io {
namespace {

using ::cartographer::io::testing::ProtoFromStringOrDie;
using ::cartographer::io::testing::ProtoReaderFromStrings;
using ::cartographer::mapping::proto::SerializationHeader;
using ::cartographer::mapping::proto::SerializedData;
using ::google::protobuf::Message;
using ::google::protobuf::util::MessageDifferencer;
using ::testing::Eq;
using ::testing::Not;

constexpr char kSerializationHeaderProtoString[] = "format_version: 1";
constexpr char kUnsupportedSerializationHeaderProtoString[] =
    "format_version: 123";
constexpr char kPoseGraphProtoString[] = "pose_graph {}";
constexpr char kAllTrajectoryBuilderOptionsProtoString[] =
    "all_trajectory_builder_options {}";
constexpr char kSubmapProtoString[] = "submap {}";
constexpr char kNodeProtoString[] = "node {}";
constexpr char kTrajectoryDataProtoString[] = "trajectory_data {}";
constexpr char kImuDataProtoString[] = "imu_data {}";
constexpr char kOdometryDataProtoString[] = "odometry_data {}";
constexpr char kFixedFramePoseDataProtoString[] = "fixed_frame_pose_data {}";
constexpr char kLandmarkDataProtoString[] = "landmark_data {}";

class ProtoStreamDeserializerTest : public ::testing::Test {
 protected:
  std::unique_ptr<InMemoryProtoStreamReader> reader_;
};

// This test checks if the serialization works.
TEST_F(ProtoStreamDeserializerTest, WorksOnGoldenTextStream) {
  // Load text proto into in_memory_reader.
  reader_ = ProtoReaderFromStrings(kSerializationHeaderProtoString,
                                   {
                                       kPoseGraphProtoString,
                                       kAllTrajectoryBuilderOptionsProtoString,
                                       kSubmapProtoString,
                                       kNodeProtoString,
                                       kTrajectoryDataProtoString,
                                       kImuDataProtoString,
                                       kOdometryDataProtoString,
                                       kFixedFramePoseDataProtoString,
                                       kLandmarkDataProtoString,
                                   });

  io::ProtoStreamDeserializer deserializer(reader_.get());

  EXPECT_TRUE(MessageDifferencer::Equals(
      deserializer.header(), ProtoFromStringOrDie<SerializationHeader>(
                                 kSerializationHeaderProtoString)));

  EXPECT_TRUE(MessageDifferencer::Equals(
      deserializer.pose_graph(),
      ProtoFromStringOrDie<SerializedData>(kPoseGraphProtoString)
          .pose_graph()));

  EXPECT_TRUE(
      MessageDifferencer::Equals(deserializer.all_trajectory_builder_options(),
                                 ProtoFromStringOrDie<SerializedData>(
                                     kAllTrajectoryBuilderOptionsProtoString)
                                     .all_trajectory_builder_options()));

  SerializedData serialized_data;
  EXPECT_TRUE(deserializer.ReadNextSerializedData(&serialized_data));
  // TODO(sebastianklose): Add matcher for protos in common place and use here.
  EXPECT_TRUE(MessageDifferencer::Equals(
      serialized_data,
      ProtoFromStringOrDie<SerializedData>(kSubmapProtoString)));

  EXPECT_TRUE(deserializer.ReadNextSerializedData(&serialized_data));
  EXPECT_TRUE(MessageDifferencer::Equals(
      serialized_data, ProtoFromStringOrDie<SerializedData>(kNodeProtoString)));

  EXPECT_TRUE(deserializer.ReadNextSerializedData(&serialized_data));
  EXPECT_TRUE(MessageDifferencer::Equals(
      serialized_data,
      ProtoFromStringOrDie<SerializedData>(kTrajectoryDataProtoString)));

  EXPECT_TRUE(deserializer.ReadNextSerializedData(&serialized_data));
  EXPECT_TRUE(MessageDifferencer::Equals(
      serialized_data,
      ProtoFromStringOrDie<SerializedData>(kImuDataProtoString)));

  EXPECT_TRUE(deserializer.ReadNextSerializedData(&serialized_data));
  EXPECT_TRUE(MessageDifferencer::Equals(
      serialized_data,
      ProtoFromStringOrDie<SerializedData>(kOdometryDataProtoString)));

  EXPECT_TRUE(deserializer.ReadNextSerializedData(&serialized_data));
  EXPECT_TRUE(MessageDifferencer::Equals(
      serialized_data,
      ProtoFromStringOrDie<SerializedData>(kFixedFramePoseDataProtoString)));

  EXPECT_TRUE(deserializer.ReadNextSerializedData(&serialized_data));
  EXPECT_TRUE(MessageDifferencer::Equals(
      serialized_data,
      ProtoFromStringOrDie<SerializedData>(kLandmarkDataProtoString)));

  EXPECT_FALSE(deserializer.ReadNextSerializedData(&serialized_data));
  EXPECT_TRUE(reader_->eof());
}

TEST_F(ProtoStreamDeserializerTest, FailsIfVersionNotSupported) {
  reader_ =
      ProtoReaderFromStrings(kUnsupportedSerializationHeaderProtoString, {});
  EXPECT_DEATH(absl::make_unique<ProtoStreamDeserializer>(reader_.get()),
               "Unsupported serialization format");
}

}  // namespace
}  // namespace io
}  // namespace cartographer
