using System;
using System.Collections.Generic;
using UniRx;
using UniRx.Triggers;
using UnityEngine;
using Random = UnityEngine.Random;

public enum CustomerMovingType
{
    WalkSide,
    WalkSideEat,
    WalkCenter,
    StayBackOrder,
    StayBack,
    WalkBack,
    WalkBackHalf,
    WalkFront,
    WalkFrontEat,
}

public enum CustomerState
{
    Walk,
    WalkShop,
    Wait,
    Order,
    Leave,
    SadLeave,
    EatingLeave,
}

public class CustomerController : MonoBehaviour
{
    private static readonly int WalkFront = Animator.StringToHash("WalkFront");
    private static readonly int WalkSide = Animator.StringToHash("WalkSide");
    private static readonly int WalkBack = Animator.StringToHash("WalkBack");
    private static readonly int StayBack = Animator.StringToHash("StayBack");
    private static readonly int WalkFrontEat = Animator.StringToHash("WalkFrontEat");
    private static readonly int WalkSideEat = Animator.StringToHash("WalkSideEat");
    
    private static readonly int Complain = Animator.StringToHash("ComplainTrigger");
    
    [SerializeField] private Animator animator;
    [SerializeField] private CustomerDirection customerDirection;
    [SerializeField] private Animator orderAnimator;

    [SerializeField] private GameObject tapReaction;
    [SerializeField] private GameObject orderPopup;
    [SerializeField] private SpriteRenderer leftPopcorn;
    [SerializeField] private SpriteRenderer rightPopcorn;
    [SerializeField] private SpriteRenderer frontPopcorn;
    [SerializeField] private SpriteRenderer wantFlavorSprite;
    [SerializeField] private Sprite defaultSprite;
    [SerializeField] private Sprite caramelSprite;
    
    // 歩道の幅(min,max)
    [SerializeField] private float walkSideTopPosition;
    [SerializeField] private float walkSideMidPos;
    [SerializeField] private float walkSideBottomPos;
    [SerializeField] private float walkSideLeavePos;
    // 立ち止まる範囲
    [SerializeField] private float stopPosision = 0f;
    [SerializeField] private float stopPositionRange = 2f;
    // オーダー待ちは複数箇所用意
    private List<Transform> orderPositions = new List<Transform>();
    [SerializeField] private List<Transform> waitPositions = new List<Transform>();
    private int orderPositionIndex;
    // 購入ポジ
    [SerializeField] private float orderPosision = .5f;
    [SerializeField] private float orderPositionRange = 1f;
    [SerializeField] private float waitOrderPosision = .2f;
    // 歩行速度
    [SerializeField] private float walkSideSpeed = 1.3f; // 1.3f
    [SerializeField] private float walkFrontBackSpeed = 1.0f; // 1.0f
    private static readonly float leftEndPosision = -10f;
    private Vector3 beginPos;
    private Vector3 wayPoint;
    private CustomerMovingType currentMovingType;
    private bool isMoveOverride;
    private List<(CustomerMovingType type, float duration)> moves = new List<(CustomerMovingType, float)>();
    private float speed = 0f;
    private float completedDuration;
    private float durationDelta;
    // 左右どちらから出るか
    private float walkSideDirection;
    public IReadOnlyReactiveProperty<CustomerMovingType> MoveEndObservable => moveEndSubject;
    private readonly ReactiveProperty<CustomerMovingType> moveEndSubject = new ReactiveProperty<CustomerMovingType>();
    public IReadOnlyReactiveProperty<CustomerState> State => state;
    private readonly ReactiveProperty<CustomerState> state = new ReactiveProperty<CustomerState>();
    
    private void Start()
    {
        state.AddTo(this);
        durationDelta = 0f;
        this.UpdateAsObservable().Subscribe(_ =>
        {
            var localPosition = transform.localPosition;
            localPosition = Vector2.MoveTowards(localPosition, wayPoint, speed * Time.deltaTime);
            localPosition = localPosition + Vector3.forward * (localPosition.y + Mathf.Abs(walkSideBottomPos));
            transform.localPosition = localPosition;
            if (Vector2.Distance(localPosition, wayPoint) < .01f)
            {
                if (durationDelta < completedDuration)
                {
                    durationDelta += Time.deltaTime;
                    return;
                }
                moveEndSubject.Value = currentMovingType;
                if (isMoveOverride)
                {
                    isMoveOverride = false;
                    return;
                }
                if (moves.Count > 0)
                {
                    var move = moves[0];
                    moves.RemoveAt(0);
                    SetMove(move.type, move.duration);
                }
            }
        }).AddTo(this);
    }

    public void Setup(List<Transform> newOrderPositions)
    {
        orderPositions = newOrderPositions;
        walkSideDirection = Mathf.Sign(Random.value - .5f);
        beginPos = Vector3.zero;
        beginPos.x = leftEndPosision * walkSideDirection;
        beginPos.y = Random.Range(walkSideMidPos, walkSideBottomPos);
        transform.localPosition = beginPos;
        currentMovingType = CustomerMovingType.StayBack;
        moveEndSubject.Value = CustomerMovingType.StayBack;
        isMoveOverride = false;
        SetWayPoint(beginPos);
    }

    public void ChangeCustomerState(CustomerState state)
    {
        this.state.Value = state;
        Debug.Log(state);
        switch (state)
        {
            case CustomerState.Walk:
                ForceSetMove(CustomerMovingType.WalkCenter);
                // 数秒立ち止まる
                AddMove(CustomerMovingType.StayBack, 2f);
                AddMove(CustomerMovingType.WalkSide);
                break;
            case CustomerState.WalkShop:
                ForceSetMove(CustomerMovingType.WalkCenter);
                break;
            case CustomerState.Wait:
                // 店に向かう&順番待ち
                // 近くの場合その場に待機
                // まだ遠い場合待機場所に移動
                ForceSetMove(CustomerMovingType.WalkBackHalf);
                AddMove(CustomerMovingType.StayBack);
                break;
            case CustomerState.Order:
                // 受付まで行く
                AddMove(CustomerMovingType.WalkBack);
                AddMove(CustomerMovingType.StayBackOrder);
                break;
            case CustomerState.Leave:
                ForceSetMove(CustomerMovingType.WalkSide);
                break;
            case CustomerState.SadLeave:
                AddMove(CustomerMovingType.WalkFront);
                AddMove(CustomerMovingType.WalkSide);
                break;
            case CustomerState.EatingLeave:
                AddMove(CustomerMovingType.WalkFrontEat);
                AddMove(CustomerMovingType.WalkSideEat);
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(state), state, null);
        }
    }

    private void AddMove(CustomerMovingType type, float duration = 0f)
    {
        moves.Add((type, duration));
    }

    private void ForceSetMove(CustomerMovingType type, float duration = 0f)
    {
        moves.Clear();
        isMoveOverride = true;
        SetMove(type, duration);
    }

    // キャラの奥行き描画順のため、y方向に対応したz軸を設定
    private void SetMove(CustomerMovingType type, float duration = 0f)
    {
        if (currentMovingType == type)
        {
            return;
        }
        completedDuration = duration;
        durationDelta = 0f;
        switch (type)
        {
            case CustomerMovingType.WalkSide:
                SetWayPoint(new Vector3(-leftEndPosision * walkSideDirection, transform.localPosition.y));
                speed = walkSideSpeed;
                // アニメーション違和感回避
                if (currentMovingType == CustomerMovingType.WalkCenter)
                {
                    break;
                }
                animator.SetTrigger(WalkSide);
                break;
            case CustomerMovingType.WalkSideEat:
                SetWayPoint(new Vector3(-leftEndPosision * walkSideDirection, transform.localPosition.y));
                speed = walkSideSpeed;
                animator.SetTrigger(WalkSideEat);
                break;
            case CustomerMovingType.WalkCenter:
                if (Mathf.Abs(transform.localPosition.x - stopPosision) <= stopPositionRange)
                {
                    SetWayPoint(new Vector3(transform.localPosition.x, beginPos.y));
                }
                else
                {
                    SetWayPoint(new Vector3(stopPosision - Random.value * stopPositionRange * walkSideDirection, beginPos.y));
                }
                speed = walkSideSpeed;
                // アニメーション違和感回避
                if (currentMovingType == CustomerMovingType.WalkSide)
                {
                    break;
                }
                animator.SetTrigger(WalkSide);
                break;
            case CustomerMovingType.StayBackOrder:
                // オーダー
                speed = 0f;
                animator.SetTrigger(StayBack);
                break;
            case CustomerMovingType.StayBack:
                speed = 0f;
                animator.SetTrigger(StayBack);
                break;
            case CustomerMovingType.WalkBack:
                // 購入場所に行く
                SetWayPoint(orderPositions[orderPositionIndex].localPosition);
                speed = walkFrontBackSpeed;
                animator.SetTrigger(WalkBack);
                break;
            case CustomerMovingType.WalkBackHalf:
                // 待機列に入る
                if (Mathf.Abs(transform.localPosition.x - stopPosision) <= stopPositionRange)
                {
                    SetWayPoint(new Vector3(transform.localPosition.x, waitOrderPosision));
                }
                else
                {
                    SetWayPoint(new Vector3(stopPosision - Random.value * stopPositionRange * walkSideDirection, waitOrderPosision));
                }
                speed = walkFrontBackSpeed;
                animator.SetTrigger(WalkBack);
                break;
            case CustomerMovingType.WalkFront:
                SetWayPoint(new Vector3(wayPoint.x, Random.Range(walkSideLeavePos, walkSideTopPosition)));
                speed = walkFrontBackSpeed;
                animator.SetTrigger(WalkFront);
                break;
            case CustomerMovingType.WalkFrontEat:
                SetWayPoint(new Vector3(wayPoint.x, Random.Range(walkSideLeavePos, walkSideTopPosition)));
                speed = walkFrontBackSpeed;
                animator.SetTrigger(WalkFrontEat);
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(type), type, null);
        }
        currentMovingType = type;
    }

    private void SetWayPoint(Vector3 point)
    {
        wayPoint = point;
        if ((wayPoint - transform.localPosition).x >= 0)
        {
            customerDirection.SetDefaultSide();
        }
        else
        {
            customerDirection.SetFlipSide();
        }
    }

    public void SetOrderPosition(int index)
    {
        orderPositionIndex = index;
    }

    public void ShowWantFlavor(int flavor)
    {
        if (flavor == 2)
        {
            leftPopcorn.sprite = caramelSprite;
            rightPopcorn.sprite = caramelSprite;
            frontPopcorn.sprite = caramelSprite;
            wantFlavorSprite.sprite = caramelSprite;
        }
        else
        {
            leftPopcorn.sprite = defaultSprite;
            rightPopcorn.sprite = defaultSprite;
            frontPopcorn.sprite = defaultSprite;
            wantFlavorSprite.sprite = defaultSprite;
        }
        orderPopup.SetActive(true);
    }

    public void ShowComplain()
    {
        orderPopup.SetActive(true);
        orderAnimator.SetTrigger(Complain);
        this.CallWaitForSeconds(1.5f, () =>
        {
            HideOrderPopup();
        });
    }

    public void HideOrderPopup()
    {
        orderPopup.SetActive(false);
    }

    public void ShowTapReaction()
    {
        tapReaction.SetActive(true);
    }
}