# Copyright 2025 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.
"""Verify Night Mode Indicator works correctly during camera use."""

import logging
import os.path
import time

from mobly import test_runner
import camera_properties_utils
import preview_processing_utils
import gen2_rig_controller_utils
import its_base_test
import its_session_utils
import lighting_control_utils
import numpy as np
import cv2

_TEST_NAME = os.path.splitext(os.path.basename(__file__))[0]
_EXTENSION_NONE = -1
_EXTENSION_NIGHT = 4  # CameraExtensionCharacteristics.EXTENSION_NIGHT
_SESSION_TYPE_CAMERA2 = 'camera2'
_SESSION_TYPE_CAMERA_EXTENSION = 'cameraExtension'
_CAMERA_SESSION_TYPES = (_SESSION_TYPE_CAMERA2, _SESSION_TYPE_CAMERA_EXTENSION)
_NUM_FRAMES_TO_WAIT = 10
_CONTROL_MODE_AUTO = 1
_CONTROL_VIDEO_STABILIZATION_MODE_OFF = 0
_CAPTURE_REQUEST = {
    'android.control.mode': _CONTROL_MODE_AUTO,
    'android.control.videoStabilizationMode':
        _CONTROL_VIDEO_STABILIZATION_MODE_OFF,
}
_CAPTURE_RESULT_KEY_NIGHT_MODE_INDICATOR = 'android.extension.nightModeIndicator'
_BRIGHTNESS_SETTING_CHANGE_WAIT_SEC = 5  # Seconds
_NIGHT_MODE_INDICATOR_OFF = 1
_NIGHT_MODE_INDICATOR_ON = 2


def _start_preview(cam, file_stem, camera_id, target_preview_size,
                   camera_session_type):
  """Start a preview capture session and verify the Night Mode Indicator state.

  Args:
    cam: ItsSession object
    file_stem: string, prefix for the output image file
    camera_id: string, camera ID to use for capture
    target_preview_size: tuple, (width, height) of the preview size to use
    camera_session_type: string, either 'camera2' or 'cameraExtension'
  """
  extension = _EXTENSION_NONE
  if camera_session_type == _SESSION_TYPE_CAMERA_EXTENSION:
    extension = _EXTENSION_NIGHT

  metadata, frame_bytes = cam.do_capture_preview_frame(
      camera_id, target_preview_size, _NUM_FRAMES_TO_WAIT, extension,
      _CAPTURE_REQUEST
  )
  np_array = np.frombuffer(frame_bytes, dtype=np.uint8)
  img_rgb = cv2.imdecode(np_array, cv2.IMREAD_COLOR)
  cv2.imwrite(f'{file_stem}_capture.jpg', img_rgb)
  if metadata is None:
    raise AssertionError('No metadata returned from capture')
  if metadata[_CAPTURE_RESULT_KEY_NIGHT_MODE_INDICATOR] is None:
    raise AssertionError('No Night Mode Indicator value in metadata')
  result = metadata[_CAPTURE_RESULT_KEY_NIGHT_MODE_INDICATOR]
  logging.debug('Night Mode Indicator value: %s', result)
  return result


class NightModeIndicatorTest(its_base_test.ItsBaseTest):
  """Test that Night Mode Indicator works as intended.

  This feature is only available on devices that also support Night Mode Camera
  Extensions. If Night Mode is supported and the feature is present in the
  available capture result keys, then the this test will setup a repeating
  capture request for the preview stream in both a Camera2 session and a Camera
  Extensions session. The test will then verify that the Night Mode Indicator
  changes state as expected during the capture session -- when the light is
  turned off, the indicator should change to the "ON" state; when the light is
  turned back on, the indicator should change to the "OFF" state.
  """

  def setup_class(self):
    super().setup_class()
    # establish connection with lighting controller
    self.use_gen2 = (self.lighting_cntl ==
                gen2_rig_controller_utils.DEFAULT_GEN2_LIGHTS_NAME)
    self.lighting_control_port = lighting_control_utils.lighting_control(
        self.lighting_cntl, self.lighting_ch, self.use_gen2)

  def teardown_test(self):
    if self.lighting_control_port:
      self.lighting_control_port.close()

  def test_night_mode_indicator(self):
    with its_session_utils.ItsSession(
        device_id=self.dut.serial,
        camera_id=self.camera_id,
        hidden_physical_id=self.hidden_physical_id) as cam:
      props = cam.get_camera_properties()
      cam.override_with_hidden_physical_camera_props(props)

      # check SKIP conditions
      first_api_level = its_session_utils.get_first_api_level(self.dut.serial)
      camera_properties_utils.skip_unless(
          first_api_level >= its_session_utils.ANDROID16_API_LEVEL and
          cam.is_night_mode_indicator_supported(self.camera_id)
      )

      for session_type in _CAMERA_SESSION_TYPES:
        logging.debug('scenario: %s', session_type)
        file_stem = f'{_TEST_NAME}_{session_type}'

        target_preview_size = None
        if session_type == _SESSION_TYPE_CAMERA2:
          target_preview_size = (
              preview_processing_utils.get_max_preview_test_size(
                  cam, self.camera_id
              )
          )
        elif session_type == _SESSION_TYPE_CAMERA_EXTENSION:
          target_preview_size = (
              preview_processing_utils.get_max_extension_preview_test_size(
                  cam, self.camera_id, _EXTENSION_NIGHT
              )
          )
        else:
          logging.debug('Unknown scenario: %s', session_type)
          continue

        # turn lights ON
        lighting_control_utils.set_lighting_state(
            self.lighting_control_port, self.lighting_ch,
            lighting_control_utils.LIGHT_ON, self.use_gen2
            )
        time.sleep(_BRIGHTNESS_SETTING_CHANGE_WAIT_SEC)
        result = _start_preview(
            cam, file_stem, self.camera_id, target_preview_size, session_type)

        if (result != _NIGHT_MODE_INDICATOR_OFF):
          raise AssertionError('Lighting state ON did not result in Night Mode '
                               'Indicator state OFF.')

        # turn OFF lights
        lighting_control_utils.set_lighting_state(
            self.lighting_control_port, self.lighting_ch,
            lighting_control_utils.LIGHT_OFF, self.use_gen2)
        time.sleep(_BRIGHTNESS_SETTING_CHANGE_WAIT_SEC)
        result = _start_preview(
            cam, file_stem, self.camera_id, target_preview_size, session_type)
        if (result != _NIGHT_MODE_INDICATOR_ON):
          raise AssertionError('Lighting state OFF did not result in Night '
                               'Mode Indicator state ON.')

if __name__ == '__main__':
  test_runner.main()
