using System;
using KitsuneCafe.Extension;
using KitsuneCafe.Input;
using KitsuneCafe.ItemSystem;
using KitsuneCafe.SOAP;
using R3;
using UnityEngine;

namespace KitsuneCafe.Entities
{
  public class AimFeature : MonoBehaviour
  {
    [SerializeField]
    private new Camera camera;

    [SerializeField]
    private EquipmentInstance equipment;

    [SerializeField]
    private Motor motor;

    [SerializeField]
    private ControlSchemeValue controlScheme;

    [SerializeField]
    private Vector2Value lookValue;

    private void OnValidate()
    {
      if (camera == null)
      {
        camera = Camera.main;
      }

      if (equipment == null)
      {
        equipment = GetComponent<EquipmentInstance>();
      }

      if (motor == null)
      {
        motor = GetComponent<Motor>();
      }
    }

    private void Start()
    {
      var d = Disposable.CreateBuilder();

      var readied = Observable.FromEventHandler<bool>(
        e => equipment.Readied += e,
        e => equipment.Readied -= e
      )
        .Select(static eh => eh.e);

      readied
        .Subscribe(isReady =>
        {
          motor.enabled = !isReady;
        })
        .AddTo(ref d);

      readied
        .WhereTrue()
        .SelectMany(_ =>
          controlScheme.AsObservable()
            .CombineLatest(
              Observable.IntervalFrame(1),
              static (scheme, _) => scheme
            ).TakeUntil(readied.WhereFalse())
        )
        .Subscribe(Aim)
        .AddTo(ref d);

      d.RegisterTo(destroyCancellationToken);
    }

    private void Aim(ControlScheme scheme)
    {
      switch (scheme)
      {
        case ControlScheme.Gamepad:
          LookInDirection(lookValue.Value);
          break;
        default:
          LookAtPosition(lookValue.Value);
          break;
      }
    }

    private void LookInDirection(Vector2 value)
    {
      transform.LookAt(transform.position + new Vector3(value.x, 0, value.y));
    }

    private void LookAtPosition(Vector2 value)
    {
      var screenPosition = camera.WorldToViewportPoint(transform.position);
      var mouseOnScreen = camera.ScreenToViewportPoint(value);

      float angle = AngleBetween(screenPosition, mouseOnScreen);
      transform.rotation = Quaternion.Euler(new Vector3(0, -angle + 90, 0));
    }

    static float AngleBetween(Vector3 a, Vector3 b)
    {
      return Mathf.Atan2(b.y - a.y, b.x - a.x) * Mathf.Rad2Deg;
    }
  }
}
