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

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

#include "control_delegate_interface_mock.h"
#include "control_options_interface_mock.h"
#include "metadata_common.h"
#include "test_common.h"

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

namespace v4l2_camera_hal {

class ControlTest : public Test {
 protected:
  virtual void SetUp() {
    mock_delegate_.reset(new ControlDelegateInterfaceMock<uint8_t>());
    mock_options_.reset(new ControlOptionsInterfaceMock<uint8_t>());
    // Nullify control so an error will be thrown if a test doesn't call
    // PrepareControl.
    control_.reset();
  }

  virtual void PrepareControl() {
    // Use this method after all the EXPECT_CALLs to pass ownership of the mocks
    // to the device.
    std::unique_ptr<TaggedControlDelegate<uint8_t>> delegate =
        std::make_unique<TaggedControlDelegate<uint8_t>>(
            delegate_tag_, std::move(mock_delegate_));
    std::unique_ptr<TaggedControlOptions<uint8_t>> options =
        std::make_unique<TaggedControlOptions<uint8_t>>(
            report_options_ ? options_tag_ : DO_NOT_REPORT_OPTIONS,
            std::move(mock_options_));
    if (use_options_) {
      control_.reset(
          new Control<uint8_t>(std::move(delegate), std::move(options)));
    } else {
      control_.reset(new Control<uint8_t>(std::move(delegate)));
    }
  }

  virtual void ExpectTags() {
    if (use_options_ && report_options_) {
      ASSERT_EQ(control_->StaticTags().size(), 1u);
      EXPECT_EQ(control_->StaticTags()[0], options_tag_);
    } else {
      EXPECT_TRUE(control_->StaticTags().empty());
    }
    // Controls use the same delgate, and thus tag, for getting and setting.
    ASSERT_EQ(control_->ControlTags().size(), 1u);
    EXPECT_EQ(control_->ControlTags()[0], delegate_tag_);
    ASSERT_EQ(control_->DynamicTags().size(), 1u);
    EXPECT_EQ(control_->DynamicTags()[0], delegate_tag_);
  }

  virtual void ExpectOptions(const std::vector<uint8_t>& options) {
    // Options should be available.
    android::CameraMetadata metadata;
    ASSERT_EQ(control_->PopulateStaticFields(&metadata), 0);
    if (use_options_ && report_options_) {
      EXPECT_EQ(metadata.entryCount(), 1u);
      ExpectMetadataEq(metadata, options_tag_, options);
    } else {
      EXPECT_EQ(metadata.entryCount(), 0u);
      // Shouldn't be expecting any options.
      EXPECT_TRUE(options.empty());
    }
  }

  virtual void ExpectValue(uint8_t value) {
    android::CameraMetadata metadata;
    ASSERT_EQ(control_->PopulateDynamicFields(&metadata), 0);
    EXPECT_EQ(metadata.entryCount(), 1u);
    ExpectMetadataEq(metadata, delegate_tag_, value);
  }

  std::unique_ptr<Control<uint8_t>> control_;
  std::unique_ptr<ControlDelegateInterfaceMock<uint8_t>> mock_delegate_;
  std::unique_ptr<ControlOptionsInterfaceMock<uint8_t>> mock_options_;
  bool use_options_ = true;
  bool report_options_ = true;

  // Need tags that match the data type (uint8_t) being passed.
  const int32_t delegate_tag_ = ANDROID_COLOR_CORRECTION_ABERRATION_MODE;
  const int32_t options_tag_ =
      ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES;
};

TEST_F(ControlTest, Tags) {
  PrepareControl();
  ExpectTags();
}

TEST_F(ControlTest, TagsNoOptions) {
  use_options_ = false;
  PrepareControl();
  ExpectTags();
}

TEST_F(ControlTest, TagsUnreportedOptions) {
  report_options_ = false;
  PrepareControl();
  ExpectTags();
}

TEST_F(ControlTest, PopulateStatic) {
  std::vector<uint8_t> expected{1, 10, 20};
  EXPECT_CALL(*mock_options_, MetadataRepresentation())
      .WillOnce(Return(expected));
  PrepareControl();
  ExpectOptions(expected);
}

TEST_F(ControlTest, PopulateStaticNoOptions) {
  use_options_ = false;
  PrepareControl();
  ExpectOptions({});
}

TEST_F(ControlTest, PopulateStaticUnreportedOptions) {
  report_options_ = false;
  PrepareControl();
  ExpectOptions({});
}

TEST_F(ControlTest, PopulateDynamic) {
  uint8_t test_option = 99;
  EXPECT_CALL(*mock_delegate_, GetValue(_))
      .WillOnce(DoAll(SetArgPointee<0>(test_option), Return(0)));
  PrepareControl();
  ExpectValue(test_option);
}

TEST_F(ControlTest, PopulateDynamicNoOptions) {
  // Lack of options shouldn't change anything for PopulateDynamic.
  use_options_ = false;
  uint8_t test_option = 99;
  EXPECT_CALL(*mock_delegate_, GetValue(_))
      .WillOnce(DoAll(SetArgPointee<0>(test_option), Return(0)));
  PrepareControl();
  ExpectValue(test_option);
}

TEST_F(ControlTest, PopulateDynamicUnreportedOptions) {
  // Lack of reported options shouldn't change anything for PopulateDynamic.
  report_options_ = false;
  uint8_t test_option = 99;
  EXPECT_CALL(*mock_delegate_, GetValue(_))
      .WillOnce(DoAll(SetArgPointee<0>(test_option), Return(0)));
  PrepareControl();
  ExpectValue(test_option);
}

TEST_F(ControlTest, PopulateDynamicFail) {
  int err = -99;
  EXPECT_CALL(*mock_delegate_, GetValue(_)).WillOnce(Return(err));
  PrepareControl();

  android::CameraMetadata metadata;
  EXPECT_EQ(control_->PopulateDynamicFields(&metadata), err);

  // Should not have added an entry.
  EXPECT_TRUE(metadata.isEmpty());
}

TEST_F(ControlTest, PopulateTemplate) {
  int template_type = 3;
  uint8_t default_value = 123;
  EXPECT_CALL(*mock_options_, DefaultValueForTemplate(template_type, _))
      .WillOnce(DoAll(SetArgPointee<1>(default_value), Return(0)));
  PrepareControl();

  android::CameraMetadata metadata;
  EXPECT_EQ(control_->PopulateTemplateRequest(template_type, &metadata), 0);
  ExpectMetadataEq(metadata, delegate_tag_, default_value);
}

TEST_F(ControlTest, PopulateTemplateFail) {
  int template_type = 3;
  int err = 10;
  EXPECT_CALL(*mock_options_, DefaultValueForTemplate(template_type, _))
      .WillOnce(Return(err));
  PrepareControl();

  android::CameraMetadata metadata;
  EXPECT_EQ(control_->PopulateTemplateRequest(template_type, &metadata), err);
}

TEST_F(ControlTest, PopulateTemplateOptionless) {
  use_options_ = false;
  int template_type = 3;
  uint8_t value = 12;
  // Should use delegate instead of options if no options.
  EXPECT_CALL(*mock_delegate_, GetValue(_))
      .WillOnce(DoAll(SetArgPointee<0>(value), Return(0)));
  PrepareControl();

  android::CameraMetadata metadata;
  EXPECT_EQ(control_->PopulateTemplateRequest(template_type, &metadata), 0);
  ExpectMetadataEq(metadata, delegate_tag_, value);
}

TEST_F(ControlTest, PopulateTemplateOptionlessFail) {
  use_options_ = false;
  int template_type = 3;
  int err = 10;
  // Should use delegate instead of options if no options.
  EXPECT_CALL(*mock_delegate_, GetValue(_)).WillOnce(Return(err));
  PrepareControl();

  android::CameraMetadata metadata;
  EXPECT_EQ(control_->PopulateTemplateRequest(template_type, &metadata), err);
}

TEST_F(ControlTest, PopulateTemplateUnreportedOptions) {
  report_options_ = false;
  int template_type = 3;
  uint8_t default_value = 123;
  // Unreported options should behave just like reported ones for templating.
  EXPECT_CALL(*mock_options_, DefaultValueForTemplate(template_type, _))
      .WillOnce(DoAll(SetArgPointee<1>(default_value), Return(0)));
  PrepareControl();

  android::CameraMetadata metadata;
  EXPECT_EQ(control_->PopulateTemplateRequest(template_type, &metadata), 0);
  ExpectMetadataEq(metadata, delegate_tag_, default_value);
}

TEST_F(ControlTest, PopulateTemplateUnreportedOptionsFail) {
  report_options_ = false;
  int template_type = 3;
  int err = 10;
  // Unreported options should behave just like reported ones for templating.
  EXPECT_CALL(*mock_options_, DefaultValueForTemplate(template_type, _))
      .WillOnce(Return(err));
  PrepareControl();

  android::CameraMetadata metadata;
  EXPECT_EQ(control_->PopulateTemplateRequest(template_type, &metadata), err);
}

TEST_F(ControlTest, SupportsRequest) {
  android::CameraMetadata metadata;
  uint8_t test_option = 123;
  ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);

  EXPECT_CALL(*mock_options_, IsSupported(test_option)).WillOnce(Return(true));
  PrepareControl();

  EXPECT_EQ(control_->SupportsRequestValues(metadata), true);
}

TEST_F(ControlTest, SupportsRequestNoOptions) {
  use_options_ = false;
  android::CameraMetadata metadata;
  uint8_t test_option = 123;
  ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);
  PrepareControl();

  EXPECT_EQ(control_->SupportsRequestValues(metadata), true);
}

TEST_F(ControlTest, SupportsRequestUnreportedOptions) {
  report_options_ = false;
  android::CameraMetadata metadata;
  uint8_t test_option = 123;
  ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);

  EXPECT_CALL(*mock_options_, IsSupported(test_option)).WillOnce(Return(true));
  PrepareControl();

  EXPECT_EQ(control_->SupportsRequestValues(metadata), true);
}

TEST_F(ControlTest, SupportsRequestFail) {
  android::CameraMetadata metadata;
  uint8_t test_option = 123;
  ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);

  EXPECT_CALL(*mock_options_, IsSupported(test_option)).WillOnce(Return(false));
  PrepareControl();

  EXPECT_EQ(control_->SupportsRequestValues(metadata), false);
}

TEST_F(ControlTest, SupportsRequestUnreportedOptionsFail) {
  report_options_ = false;
  android::CameraMetadata metadata;
  uint8_t test_option = 123;
  ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);

  // Unreported options should still be checked against.
  EXPECT_CALL(*mock_options_, IsSupported(test_option)).WillOnce(Return(false));
  PrepareControl();

  EXPECT_EQ(control_->SupportsRequestValues(metadata), false);
}

TEST_F(ControlTest, SupportsRequestInvalidNumber) {
  // Start with a request for multiple values.
  android::CameraMetadata metadata;
  std::vector<uint8_t> test_data = {1, 2, 3};
  ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_data), 0);
  PrepareControl();
  EXPECT_EQ(control_->SupportsRequestValues(metadata), false);
}

TEST_F(ControlTest, SupportsRequestInvalidNumberNoOptions) {
  use_options_ = false;
  // Start with a request for multiple values.
  android::CameraMetadata metadata;
  std::vector<uint8_t> test_data = {1, 2, 3};
  ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_data), 0);
  PrepareControl();
  // Not having any explicit options does not exempt a control
  // from requiring the right number of values.
  EXPECT_EQ(control_->SupportsRequestValues(metadata), false);
}

TEST_F(ControlTest, SupportsRequestEmpty) {
  android::CameraMetadata metadata;
  PrepareControl();
  EXPECT_EQ(control_->SupportsRequestValues(metadata), true);
}

TEST_F(ControlTest, SetRequest) {
  android::CameraMetadata metadata;
  uint8_t test_option = 123;
  ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);

  Expectation validation_check =
      EXPECT_CALL(*mock_options_, IsSupported(test_option))
          .WillOnce(Return(true));
  EXPECT_CALL(*mock_delegate_, SetValue(test_option))
      .After(validation_check)
      .WillOnce(Return(0));
  PrepareControl();

  // Make the request.
  ASSERT_EQ(control_->SetRequestValues(metadata), 0);
}

TEST_F(ControlTest, SetRequestNoOptions) {
  use_options_ = false;
  android::CameraMetadata metadata;
  uint8_t test_option = 123;
  ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);

  // No options, no validation check.
  EXPECT_CALL(*mock_delegate_, SetValue(test_option)).WillOnce(Return(0));
  PrepareControl();

  // Make the request.
  ASSERT_EQ(control_->SetRequestValues(metadata), 0);
}

TEST_F(ControlTest, SetRequestUnreportedOptions) {
  report_options_ = false;
  android::CameraMetadata metadata;
  uint8_t test_option = 123;
  ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);

  // Unreported options still get a validation check.
  Expectation validation_check =
      EXPECT_CALL(*mock_options_, IsSupported(test_option))
          .WillOnce(Return(true));
  EXPECT_CALL(*mock_delegate_, SetValue(test_option))
      .After(validation_check)
      .WillOnce(Return(0));
  PrepareControl();

  // Make the request.
  ASSERT_EQ(control_->SetRequestValues(metadata), 0);
}

TEST_F(ControlTest, SetRequestSettingFail) {
  android::CameraMetadata metadata;
  uint8_t test_option = 123;
  ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);

  int err = 99;
  Expectation validation_check =
      EXPECT_CALL(*mock_options_, IsSupported(test_option))
          .WillOnce(Return(true));
  EXPECT_CALL(*mock_delegate_, SetValue(test_option))
      .After(validation_check)
      .WillOnce(Return(err));
  PrepareControl();

  EXPECT_EQ(control_->SetRequestValues(metadata), err);
}

TEST_F(ControlTest, SetRequestValidationFail) {
  android::CameraMetadata metadata;
  uint8_t test_option = 123;
  ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_option), 0);

  EXPECT_CALL(*mock_options_, IsSupported(test_option)).WillOnce(Return(false));
  PrepareControl();

  EXPECT_EQ(control_->SetRequestValues(metadata), -EINVAL);
}

TEST_F(ControlTest, SetRequestInvalidNumber) {
  // Start with a request for multiple values.
  android::CameraMetadata metadata;
  std::vector<uint8_t> test_data = {1, 2, 3};
  ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_data), 0);

  PrepareControl();
  EXPECT_EQ(control_->SetRequestValues(metadata), -EINVAL);
}

TEST_F(ControlTest, SetRequestInvalidNumberNoOptions) {
  use_options_ = false;
  // Start with a request for multiple values.
  android::CameraMetadata metadata;
  std::vector<uint8_t> test_data = {1, 2, 3};
  ASSERT_EQ(UpdateMetadata(&metadata, delegate_tag_, test_data), 0);

  PrepareControl();
  // Not having explicit options does not change that an incorrect
  // number of values is invalid.
  EXPECT_EQ(control_->SetRequestValues(metadata), -EINVAL);
}

TEST_F(ControlTest, SetRequestEmpty) {
  // Should do nothing.
  android::CameraMetadata metadata;
  PrepareControl();
  EXPECT_EQ(control_->SetRequestValues(metadata), 0);
}

}  // namespace v4l2_camera_hal