import type { CameraPosition } from '../specs/common-types/CameraPosition'
import type { DeviceType } from '../specs/common-types/DeviceType'
import type { CameraDevice } from '../specs/inputs/CameraDevice.nitro'

export type PhysicalDeviceType = Extract<
  DeviceType,
  | 'ultra-wide-angle'
  | 'wide-angle'
  | 'telephoto'
  | 'true-depth'
  | 'lidar-depth'
  | 'time-of-flight-depth'
  | 'external'
  | 'continuity'
>

export interface DeviceFilter {
  /**
   * The desired physical devices your camera device should have.
   *
   * Many modern phones have multiple Camera devices on one side and can combine those physical camera devices to one logical camera device.
   * For example, the iPhone 11 has two physical camera devices, the `ultra-wide-angle` ("fish-eye") and the normal `wide-angle`.
   * You can either use one of those devices individually, or use a combined logical camera device which can smoothly switch over between the two physical cameras depending on the current `zoom` level.
   * When the user is at 0.5x-1x zoom, the `ultra-wide-angle` can be used to offer a fish-eye zoom-out effect, and anything above 1x will smoothly switch over to the `wide-angle`.
   *
   * @note Devices with fewer physical devices (`['wide-angle']`) are usually faster to start up than more complex
   * devices (`['ultra-wide-angle', 'wide-angle', 'telephoto']`), but don't offer zoom switch-over capabilities.
   *
   * @example
   * ```ts
   * // This device is simpler, so it starts up faster.
   * getCameraDevice({ physicalDevices: ['wide-angle'] })
   * // This device is more complex, so it starts up slower, but you can switch between devices on 0.5x, 1x and 2x zoom.
   * getCameraDevice({ physicalDevices: ['ultra-wide-angle', 'wide-angle', 'telephoto'] })
   * ```
   */
  physicalDevices?: PhysicalDeviceType[]
}

/**
 * Get the best matching Camera device that best satisfies your requirements using a sorting filter,
 * or `undefined` if not Cameras are available on this platform.
 *
 * If this platform has any Cameras at the given {@linkcode position}, this method will always return
 * a Camera device, so {@linkcode filter} never excludes cameras.
 *
 * @param position The position of the Camera device relative to the phone.
 * @param filter The filter you want to use. The Camera device that matches your filter the closest will be returned
 * @returns The Camera device that matches your filter the closest, or `undefined` if no Camera Device exists on this platform.
 * @example
 * ```ts
 * const device = getCameraDevice(devices, 'back', {
 *    physicalDevices: ['wide-angle']
 * })
 * ```
 */
export function getCameraDevice(
  devices: CameraDevice[],
  position: CameraPosition,
  filter: DeviceFilter = {},
): CameraDevice | undefined {
  return devices
    .filter((d) => d.position === position)
    .reduce<CameraDevice | undefined>((prev, curr) => {
      if (prev == null) return curr

      let prevPoints = 0
      let currPoints = 0

      const physicalDevicesFilter = (filter.physicalDevices ?? [
        'wide-angle',
      ]) as string[]
      // user did pass a physical device filter, two possible scenarios:
      // 1. user wants all cameras ([ultra-wide, wide, tele]) to zoom. prefer those devices that have all 3 cameras.
      // 2. user wants only one ([wide]) for faster performance. prefer those devices that only have one camera, if they have more, we rank them lower.
      for (const physicalCamera of prev.physicalDevices) {
        if (physicalDevicesFilter.includes(physicalCamera.type)) prevPoints += 1
        else prevPoints -= 1
      }
      for (const physicalCamera of curr.physicalDevices) {
        if (physicalDevicesFilter.includes(physicalCamera.type)) currPoints += 1
        else currPoints -= 1
      }

      if (currPoints > prevPoints) return curr
      else return prev
    }, undefined)
}
