/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * 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 "metadata_reader.h"

#include <camera/CameraMetadata.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <system/camera.h>

#include "array_vector.h"
#include "metadata_common.h"

using testing::AtMost;
using testing::Expectation;
using testing::Return;
using testing::Test;

namespace default_camera_hal {

class MetadataReaderTest : public Test {
 protected:
  void SetUp() {
    ResetMetadata();
    // FillDUT should be called before using the device under test.
    dut_.reset();
  }

  void ResetMetadata() {
    metadata_ = std::make_unique<android::CameraMetadata>();
  }

  void FillDUT() {
    dut_ = std::make_unique<MetadataReader>(std::move(metadata_));
    ResetMetadata();
  }

  std::unique_ptr<MetadataReader> dut_;
  std::unique_ptr<android::CameraMetadata> metadata_;

  const int32_t facing_tag_ = ANDROID_LENS_FACING;
  const int32_t orientation_tag_ = ANDROID_SENSOR_ORIENTATION;
  const int32_t max_inputs_tag_ = ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS;
  const int32_t max_outputs_tag_ = ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS;
  const int32_t configs_tag_ = ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
  const int32_t stalls_tag_ = ANDROID_SCALER_AVAILABLE_STALL_DURATIONS;
  const int32_t reprocess_formats_tag_ =
      ANDROID_SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP;

  const std::vector<int32_t> valid_orientations_ = {0, 90, 180, 270};
  // TODO(b/31384253): check for required configs/reprocess formats.
};

TEST_F(MetadataReaderTest, FacingTranslations) {
  // Check that the enums are converting properly.
  std::map<uint8_t, int> translations{
      {ANDROID_LENS_FACING_FRONT, CAMERA_FACING_FRONT},
      {ANDROID_LENS_FACING_BACK, CAMERA_FACING_BACK},
      {ANDROID_LENS_FACING_EXTERNAL, CAMERA_FACING_EXTERNAL}};
  for (const auto& translation : translations) {
    ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
                  metadata_.get(), facing_tag_, translation.first),
              0);
    FillDUT();

    int expected = translation.second;
    int actual = expected + 1;
    EXPECT_EQ(dut_->Facing(&actual), 0);
    EXPECT_EQ(actual, expected);
  }
}

TEST_F(MetadataReaderTest, InvalidFacing) {
  uint8_t invalid = 99;
  ASSERT_EQ(
      v4l2_camera_hal::UpdateMetadata(metadata_.get(), facing_tag_, invalid),
      0);
  FillDUT();
  int actual = 0;
  EXPECT_EQ(dut_->Facing(&actual), -EINVAL);
}

TEST_F(MetadataReaderTest, EmptyFacing) {
  FillDUT();
  int actual = 0;
  EXPECT_EQ(dut_->Facing(&actual), -ENOENT);
}

TEST_F(MetadataReaderTest, ValidOrientations) {
  for (int32_t orientation : valid_orientations_) {
    ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
                  metadata_.get(), orientation_tag_, orientation),
              0);
    FillDUT();

    int actual = orientation + 1;
    EXPECT_EQ(dut_->Orientation(&actual), 0);
    EXPECT_EQ(actual, orientation);
  }
}

TEST_F(MetadataReaderTest, InvalidOrientations) {
  // High.
  for (int32_t orientation : valid_orientations_) {
    ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
                  metadata_.get(), orientation_tag_, orientation + 1),
              0);
    FillDUT();
    int actual = 0;
    EXPECT_EQ(dut_->Orientation(&actual), -EINVAL);
  }
  // Low.
  for (int32_t orientation : valid_orientations_) {
    ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
                  metadata_.get(), orientation_tag_, orientation - 1),
              0);
    FillDUT();
    int actual = 0;
    EXPECT_EQ(dut_->Orientation(&actual), -EINVAL);
  }
}

TEST_F(MetadataReaderTest, EmptyOrientation) {
  FillDUT();
  int actual = 0;
  EXPECT_EQ(dut_->Orientation(&actual), -ENOENT);
}

TEST_F(MetadataReaderTest, MaxInputs) {
  int32_t expected = 12;
  ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
                metadata_.get(), max_inputs_tag_, expected),
            0);
  FillDUT();
  int32_t actual = expected + 1;
  ASSERT_EQ(dut_->MaxInputStreams(&actual), 0);
  EXPECT_EQ(actual, expected);
}

TEST_F(MetadataReaderTest, EmptyMaxInputs) {
  FillDUT();
  // Max inputs is an optional key; if not present the default is 0.
  int32_t expected = 0;
  int32_t actual = expected + 1;
  ASSERT_EQ(dut_->MaxInputStreams(&actual), 0);
  EXPECT_EQ(actual, expected);
}

TEST_F(MetadataReaderTest, MaxOutputs) {
  std::array<int32_t, 3> expected = {{12, 34, 56}};
  ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
                metadata_.get(), max_outputs_tag_, expected),
            0);
  FillDUT();
  std::array<int32_t, 3> actual;
  ASSERT_EQ(dut_->MaxOutputStreams(&actual[0], &actual[1], &actual[2]), 0);
  EXPECT_EQ(actual, expected);
}

TEST_F(MetadataReaderTest, InvalidMaxOutputs) {
  // Must be a 3-tuple to be valid.
  std::array<int32_t, 4> invalid = {{12, 34, 56, 78}};
  ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
                metadata_.get(), max_outputs_tag_, invalid),
            0);
  FillDUT();
  int32_t actual;
  // Don't mind the aliasing since we don't care about the value.
  ASSERT_EQ(dut_->MaxOutputStreams(&actual, &actual, &actual), -EINVAL);
}

TEST_F(MetadataReaderTest, EmptyMaxOutputs) {
  FillDUT();
  int32_t actual;
  // Don't mind the aliasing since we don't care about the value.
  ASSERT_EQ(dut_->MaxOutputStreams(&actual, &actual, &actual), -ENOENT);
}

TEST_F(MetadataReaderTest, StreamConfigurations) {
  v4l2_camera_hal::ArrayVector<int32_t, 4> configs;
  std::array<int32_t, 4> config1{
      {1, 2, 3, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT}};
  std::array<int32_t, 4> config2{
      {5, 6, 7, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT}};
  configs.push_back(config1);
  configs.push_back(config2);
  ASSERT_EQ(
      v4l2_camera_hal::UpdateMetadata(metadata_.get(), configs_tag_, configs),
      0);
  FillDUT();
  std::vector<StreamConfiguration> actual;
  ASSERT_EQ(dut_->StreamConfigurations(&actual), 0);
  ASSERT_EQ(actual.size(), configs.num_arrays());
  EXPECT_EQ(actual[0].spec.format, config1[0]);
  EXPECT_EQ(actual[0].spec.width, config1[1]);
  EXPECT_EQ(actual[0].spec.height, config1[2]);
  EXPECT_EQ(actual[0].direction, config1[3]);
  EXPECT_EQ(actual[1].spec.format, config2[0]);
  EXPECT_EQ(actual[1].spec.width, config2[1]);
  EXPECT_EQ(actual[1].spec.height, config2[2]);
  EXPECT_EQ(actual[1].direction, config2[3]);
}

TEST_F(MetadataReaderTest, InvalidStreamConfigurationDirection) {
  // -1 is not a valid direction.
  std::array<int32_t, 4> config{{1, 2, 3, -1}};
  ASSERT_EQ(
      v4l2_camera_hal::UpdateMetadata(metadata_.get(), configs_tag_, config),
      0);
  FillDUT();
  std::vector<StreamConfiguration> actual;
  ASSERT_EQ(dut_->StreamConfigurations(&actual), -EINVAL);
}

TEST_F(MetadataReaderTest, InvalidStreamConfigurationSize) {
  // Both size dimensions must be > 0.
  std::array<int32_t, 4> config{
      {1, 2, 0, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT}};
  ASSERT_EQ(
      v4l2_camera_hal::UpdateMetadata(metadata_.get(), configs_tag_, config),
      0);
  FillDUT();
  std::vector<StreamConfiguration> actual;
  ASSERT_EQ(dut_->StreamConfigurations(&actual), -EINVAL);
}

TEST_F(MetadataReaderTest, InvalidStreamConfigurationNumElements) {
  // Should be a multiple of 4.
  std::array<int32_t, 5> config{
      {1, 2, 3, ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT, 5}};
  ASSERT_EQ(
      v4l2_camera_hal::UpdateMetadata(metadata_.get(), configs_tag_, config),
      0);
  FillDUT();
  std::vector<StreamConfiguration> actual;
  ASSERT_EQ(dut_->StreamConfigurations(&actual), -EINVAL);
}

// TODO(b/31384253): Test that failure occurs if
// required configurations are not present.

TEST_F(MetadataReaderTest, NoStreamConfigurations) {
  FillDUT();
  std::vector<StreamConfiguration> actual;
  ASSERT_EQ(dut_->StreamConfigurations(&actual), -ENOENT);
}

TEST_F(MetadataReaderTest, StreamStallDurations) {
  v4l2_camera_hal::ArrayVector<int64_t, 4> stalls;
  std::array<int64_t, 4> stall1{{1, 2, 3, 4}};
  std::array<int64_t, 4> stall2{{5, 6, 7, 8}};
  stalls.push_back(stall1);
  stalls.push_back(stall2);
  ASSERT_EQ(
      v4l2_camera_hal::UpdateMetadata(metadata_.get(), stalls_tag_, stalls), 0);
  FillDUT();
  std::vector<StreamStallDuration> actual;
  ASSERT_EQ(dut_->StreamStallDurations(&actual), 0);
  ASSERT_EQ(actual.size(), stalls.num_arrays());
  EXPECT_EQ(actual[0].spec.format, stall1[0]);
  EXPECT_EQ(actual[0].spec.width, stall1[1]);
  EXPECT_EQ(actual[0].spec.height, stall1[2]);
  EXPECT_EQ(actual[0].duration, stall1[3]);
  EXPECT_EQ(actual[1].spec.format, stall2[0]);
  EXPECT_EQ(actual[1].spec.width, stall2[1]);
  EXPECT_EQ(actual[1].spec.height, stall2[2]);
  EXPECT_EQ(actual[1].duration, stall2[3]);
}

TEST_F(MetadataReaderTest, InvalidStreamStallDurationDuration) {
  // -1 is not a valid duration.
  std::array<int64_t, 4> stall{{1, 2, 3, -1}};
  ASSERT_EQ(
      v4l2_camera_hal::UpdateMetadata(metadata_.get(), stalls_tag_, stall), 0);
  FillDUT();
  std::vector<StreamStallDuration> actual;
  ASSERT_EQ(dut_->StreamStallDurations(&actual), -EINVAL);
}

TEST_F(MetadataReaderTest, InvalidStreamStallDurationSize) {
  // Both size dimensions must be > 0.
  std::array<int64_t, 4> stall{{1, 2, 0, 3}};
  ASSERT_EQ(
      v4l2_camera_hal::UpdateMetadata(metadata_.get(), stalls_tag_, stall), 0);
  FillDUT();
  std::vector<StreamStallDuration> actual;
  ASSERT_EQ(dut_->StreamStallDurations(&actual), -EINVAL);
}

TEST_F(MetadataReaderTest, InvalidStreamStallDurationNumElements) {
  // Should be a multiple of 4.
  std::array<int64_t, 5> stall{{1, 2, 3, 4, 5}};
  ASSERT_EQ(
      v4l2_camera_hal::UpdateMetadata(metadata_.get(), stalls_tag_, stall), 0);
  FillDUT();
  std::vector<StreamStallDuration> actual;
  ASSERT_EQ(dut_->StreamStallDurations(&actual), -EINVAL);
}

// TODO(b/31384253): Test that failure occurs if
// YUV_420_888, RAW10, RAW12, RAW_OPAQUE, or IMPLEMENTATION_DEFINED
// formats have stall durations > 0.

TEST_F(MetadataReaderTest, NoStreamStallDurations) {
  FillDUT();
  std::vector<StreamStallDuration> actual;
  ASSERT_EQ(dut_->StreamStallDurations(&actual), -ENOENT);
}

TEST_F(MetadataReaderTest, ReprocessFormats) {
  ReprocessFormatMap expected{{1, {4}}, {2, {5, 6}}, {3, {7, 8, 9}}};
  std::vector<int32_t> raw;
  for (const auto& input_outputs : expected) {
    raw.push_back(input_outputs.first);
    raw.push_back(input_outputs.second.size());
    raw.insert(
        raw.end(), input_outputs.second.begin(), input_outputs.second.end());
  }
  ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
                metadata_.get(), reprocess_formats_tag_, raw),
            0);
  FillDUT();
  ReprocessFormatMap actual;
  ASSERT_EQ(dut_->ReprocessFormats(&actual), 0);
  EXPECT_EQ(actual, expected);
}

TEST_F(MetadataReaderTest, ReprocessFormatsNoOutputs) {
  // 0 indicates that there are 0 output formats for input format 1,
  // which is not ok.
  std::vector<int32_t> raw{1, 0};
  ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
                metadata_.get(), reprocess_formats_tag_, raw),
            0);
  FillDUT();
  ReprocessFormatMap actual;
  ASSERT_EQ(dut_->ReprocessFormats(&actual), -EINVAL);
}

TEST_F(MetadataReaderTest, ReprocessFormatsPastEnd) {
  // 3 indicates that there are 3 output formats for input format 1,
  // which is not ok since there are only 2 here.
  std::vector<int32_t> raw{1, 3, 0, 0};
  ASSERT_EQ(v4l2_camera_hal::UpdateMetadata(
                metadata_.get(), reprocess_formats_tag_, raw),
            0);
  FillDUT();
  ReprocessFormatMap actual;
  ASSERT_EQ(dut_->ReprocessFormats(&actual), -EINVAL);
}

TEST_F(MetadataReaderTest, EmptyReprocessFormats) {
  FillDUT();
  ReprocessFormatMap actual;
  ASSERT_EQ(dut_->ReprocessFormats(&actual), -ENOENT);
}

}  // namespace default_camera_hal