/*
 * 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 "format_metadata_factory.h"

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

#include "metadata/test_common.h"
#include "v4l2_wrapper_mock.h"

using testing::AtLeast;
using testing::DoAll;
using testing::Expectation;
using testing::Return;
using testing::SetArgPointee;
using testing::Test;
using testing::_;

namespace v4l2_camera_hal {

class FormatMetadataFactoryTest : public Test {
 protected:
  virtual void SetUp() { mock_device_.reset(new V4L2WrapperMock()); }

  virtual void ExpectMetadataTagCount(const android::CameraMetadata& metadata,
                                      uint32_t tag,
                                      size_t count) {
    camera_metadata_ro_entry_t entry = metadata.find(tag);
    EXPECT_EQ(entry.count, count);
  }

  std::shared_ptr<V4L2WrapperMock> mock_device_;
};

TEST_F(FormatMetadataFactoryTest, GetFormatMetadata) {
  std::set<uint32_t> formats{V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420,
                             V4L2_PIX_FMT_YUYV};
  std::map<uint32_t, std::set<std::array<int32_t, 2>>> sizes{
      {V4L2_PIX_FMT_JPEG, {{{10, 20}}, {{30, 60}}, {{120, 240}}}},
      {V4L2_PIX_FMT_YUV420, {{{1, 2}}, {{3, 6}}, {{12, 24}}}},
      {V4L2_PIX_FMT_YUYV, {{{20, 40}}, {{80, 160}}, {{320, 640}}}}};
  // These need to be on the correct order of magnitude,
  // as there is a check for min fps > 15.
  std::map<uint32_t, std::map<std::array<int32_t, 2>, std::array<int64_t, 2>>>
      durations{{V4L2_PIX_FMT_JPEG,
                 {{{{10, 20}}, {{100000000, 200000000}}},
                  {{{30, 60}}, {{1000000000, 2000000000}}},
                  {{{120, 240}}, {{700000000, 1200000000}}}}},
                {V4L2_PIX_FMT_YUV420,
                 {{{{1, 2}}, {{10000000000, 20000000000}}},
                  {{{3, 6}}, {{11000000000, 21000000000}}},
                  {{{12, 24}}, {{10500000000, 19000000000}}}}},
                {V4L2_PIX_FMT_YUYV,
                 {{{{20, 40}}, {{11000000000, 22000000000}}},
                  {{{80, 160}}, {{13000000000, 25000000000}}},
                  {{{320, 640}}, {{10100000000, 19000000000}}}}}};
  // The camera must report at least one qualified format.
  std::vector<uint32_t> qualified_formats = {V4L2_PIX_FMT_YUYV};

  // Device must support IMPLEMENTATION_DEFINED (as well as JPEG & YUV).
  // For USB cameras, we assume that this format will not be present, and it
  // will default to a qualified format or one of the other required formats.

  EXPECT_CALL(*mock_device_, GetFormats(_))
      .WillOnce(DoAll(SetArgPointee<0>(formats), Return(0)));

  EXPECT_CALL(*mock_device_, GetQualifiedFormats(_))
      .WillOnce(DoAll(SetArgPointee<0>(qualified_formats), Return(0)));

  for (auto format : formats) {
    std::set<std::array<int32_t, 2>> format_sizes = sizes[format];
    EXPECT_CALL(*mock_device_, GetFormatFrameSizes(format, _))
        .Times(AtLeast(1))
        .WillRepeatedly(DoAll(SetArgPointee<1>(format_sizes), Return(0)));
    for (auto size : format_sizes) {
      EXPECT_CALL(*mock_device_, GetFormatFrameDurationRange(format, size, _))
          .Times(AtLeast(1))
          .WillRepeatedly(
              DoAll(SetArgPointee<2>(durations[format][size]), Return(0)));
    }
  }

  PartialMetadataSet components;
  ASSERT_EQ(AddFormatComponents(mock_device_,
                                std::inserter(components, components.end())),
            0);

  for (auto& component : components) {
    android::CameraMetadata metadata;
    component->PopulateStaticFields(&metadata);
    ASSERT_EQ(metadata.entryCount(), 1u);
    int32_t tag = component->StaticTags()[0];
    switch (tag) {
      case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS:  // Fall through.
      case ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS:    // Fall through.
      case ANDROID_SCALER_AVAILABLE_STALL_DURATIONS:        // Fall through.
        // 3 sizes per format, 4 elements per config.
        // # formats + 1 for IMPLEMENTATION_DEFINED.
        ExpectMetadataTagCount(metadata, tag, (formats.size() + 1) * 3 * 4);
        break;
      case ANDROID_SENSOR_INFO_MAX_FRAME_DURATION:
        // The lowest max duration from above.
        ExpectMetadataEq(metadata, tag, 200000000);
        break;
      case ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES:
        // 2 ranges ({min, max} and {max, max}), each with a min and max.
        ExpectMetadataTagCount(metadata, tag, 4);
        break;
      default:
        FAIL() << "Unexpected component created.";
        break;
    }
  }
}

TEST_F(FormatMetadataFactoryTest, GetFormatMetadataMissingRequired) {
  std::set<uint32_t> formats{V4L2_PIX_FMT_YUYV};
  std::map<uint32_t, std::set<std::array<int32_t, 2>>> sizes{
      {V4L2_PIX_FMT_YUYV, {{{640, 480}}, {{320, 240}}}}};
  std::map<uint32_t, std::map<std::array<int32_t, 2>, std::array<int64_t, 2>>>
      durations{{V4L2_PIX_FMT_YUYV,
                 {{{{640, 480}}, {{100000000, 200000000}}},
                  {{{320, 240}}, {{100000000, 200000000}}}}}};

  EXPECT_CALL(*mock_device_, GetFormats(_))
      .WillOnce(DoAll(SetArgPointee<0>(formats), Return(0)));
  // If a qualified format is present, we expect that required fields are
  // populated as if they are supported.
  std::vector<uint32_t> qualified_formats = {V4L2_PIX_FMT_YUYV};

  EXPECT_CALL(*mock_device_, GetQualifiedFormats(_))
      .WillOnce(DoAll(SetArgPointee<0>(qualified_formats), Return(0)));

  for (auto format : formats) {
    std::set<std::array<int32_t, 2>> format_sizes = sizes[format];
    EXPECT_CALL(*mock_device_, GetFormatFrameSizes(format, _))
        .Times(AtLeast(1))
        .WillRepeatedly(DoAll(SetArgPointee<1>(format_sizes), Return(0)));
    for (auto size : format_sizes) {
      EXPECT_CALL(*mock_device_, GetFormatFrameDurationRange(format, size, _))
          .Times(AtLeast(1))
          .WillRepeatedly(
              DoAll(SetArgPointee<2>(durations[format][size]), Return(0)));
    }
  }

  // Check that all required formats are present.
  PartialMetadataSet components;
  ASSERT_EQ(AddFormatComponents(mock_device_,
                                std::inserter(components, components.end())),
            0);

  std::vector<std::array<int32_t, 2>> target_fps_ranges{{{5, 10}}, {{10, 10}}};
  for (auto& component : components) {
    android::CameraMetadata metadata;
    component->PopulateStaticFields(&metadata);
    ASSERT_EQ(metadata.entryCount(), 1u);
    int32_t tag = component->StaticTags()[0];
    switch (tag) {
      case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS:  // Fall through.
      case ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS:    // Fall through.
      case ANDROID_SCALER_AVAILABLE_STALL_DURATIONS:        // Fall through.
        // Two sizes per format, four elements per config.
        // # formats + 3 for YUV420, JPEG, IMPLEMENTATION_DEFINED.
        ExpectMetadataTagCount(metadata, tag, (formats.size() + 3) * 2 * 4);
        break;
      case ANDROID_SENSOR_INFO_MAX_FRAME_DURATION:
        // The lowest max duration from above.
        ExpectMetadataEq(metadata, tag, 200000000);
        break;
      case ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES:
        // 2 ranges ({min, max} and {max, max}), each with a min and max.
        ExpectMetadataTagCount(metadata, tag, 4);
        ExpectMetadataEq(metadata, tag, target_fps_ranges);
        break;
      default:
        FAIL() << "Unexpected component created.";
        break;
    }
  }
}

}  // namespace v4l2_camera_hal
