using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

// 발판으로서 필요한 동작을 담은 스크립트
public class Platform : MonoBehaviour {
    public enum PlatformPattern {
        Random,
        Empty,
        LowLeft,
        LowMid,
        LowRight,
        Slide,
        ForceHit
    }

    public enum MileagePattern {
        Random,
        None,
        SafeLine,
        JumpArc,
        SlideLine
    }

    public enum ItemPattern {
        None,
        HealthSushi,
        InvincibleRamen,
        Shield,
        SpeedShoes,
        MileageCard
    }

    public GameObject[] obstacles; // 장애물 오브젝트들
    public int emptyPatternWeight = 2; // 장애물이 없는 패턴이 선택될 가중치
    public Sprite mileageSprite; // 이 발판에서 사용할 마일리지 토큰 스프라이트
    public int mileagePatternChance = 50; // 마일리지 패턴 등장 확률
    public int smallMileageValue = 10; // 일반 토큰 가치
    public int bonusMileageValue = 30; // 어려운 루트 보너스 토큰 가치

    private bool stepped = false; // 플레이어 캐릭터가 밟았었는가
    private MileagePickup[] mileagePickups; // 런타임에 생성하는 마일리지 토큰 풀
    private ItemPickup[] itemPickups; // 런타임에 생성하는 튜토리얼 아이템 풀
    private PlatformPattern reservedPattern = PlatformPattern.Random; // 다음 활성화 때 사용할 수동 패턴
    private MileagePattern reservedMileagePattern = MileagePattern.Random; // 다음 활성화 때 사용할 마일리지 패턴
    private SlideObstaclePattern.SlideLayout reservedSlideLayout = SlideObstaclePattern.SlideLayout.Random; // 다음 슬라이딩 장애물 배치
    private ItemPattern reservedItemPattern = ItemPattern.None; // 다음 활성화 때 사용할 아이템 배치
    private const int MileagePoolSize = 5;
    private const int ItemPoolSize = 1;
    private const float MileageTokenScale = 0.55f;
    private const float MileageColliderRadius = 0.4f;
    private const float ItemTokenScale = 0.68f;
    private const float ItemColliderRadius = 0.46f;

    public void ConfigureForTutorial(
        PlatformPattern pattern,
        MileagePattern mileagePattern,
        SlideObstaclePattern.SlideLayout slideLayout = SlideObstaclePattern.SlideLayout.Random,
        ItemPattern itemPattern = ItemPattern.None) {
        reservedPattern = pattern;
        reservedMileagePattern = mileagePattern;
        reservedSlideLayout = slideLayout;
        reservedItemPattern = itemPattern;
    }

    public void ConfigureForRandom() {
        reservedPattern = PlatformPattern.Random;
        reservedMileagePattern = MileagePattern.Random;
        reservedSlideLayout = SlideObstaclePattern.SlideLayout.Random;
        reservedItemPattern = ItemPattern.None;
    }

    // 컴포넌트가 활성화될때 마다 매번 실행되는 메서드
    private void OnEnable() {
        // 발판을 리셋하는 처리

        stepped = false;
        EnsureMileagePickups();
        EnsureItemPickups();

        for(int i = 0; i < obstacles.Length; i++)
        {
            obstacles[i].SetActive(false);
        }

        HideMileagePickups();
        HideItemPickups();

        if(reservedPattern != PlatformPattern.Random)
        {
            ApplyReservedPattern();
            return;
        }

        if(obstacles.Length == 0)
        {
            TryPlaceMileagePattern(null);
            return;
        }

        // 낮은 장애물과 높은 장애물이 동시에 켜져 불가능한 배치가 되지 않도록 하나만 선택한다.
        int patternIndex = Random.Range(0, obstacles.Length + emptyPatternWeight);
        GameObject activeObstacle = null;

        if(patternIndex < obstacles.Length)
        {
            activeObstacle = obstacles[patternIndex];
            ConfigureSlideObstacle(activeObstacle);
            activeObstacle.SetActive(true);
        }

        TryPlaceMileagePattern(activeObstacle);
    }

    void OnCollisionEnter2D(Collision2D collision) {
        // 플레이어 캐릭터가 자신을 밟았을때 점수를 추가하는 처리
        if(collision.collider.tag == "Player" && !stepped && GameFlowManager.IsGameplayActive)
        {
            stepped = true;
            GameManager.instance.AddScore(1);
        }
    }

    private void EnsureMileagePickups() {
        if(mileagePickups != null && mileagePickups.Length == MileagePoolSize)
        {
            return;
        }

        mileagePickups = new MileagePickup[MileagePoolSize];

        for(int i = 0; i < MileagePoolSize; i++)
        {
            GameObject pickupObject = new GameObject("Mileage Pickup " + (i + 1));
            pickupObject.transform.SetParent(transform);
            pickupObject.transform.localScale = GetParentScaleCompensatedVector(MileageTokenScale);

            SpriteRenderer spriteRenderer = pickupObject.AddComponent<SpriteRenderer>();
            spriteRenderer.sprite = mileageSprite;
            spriteRenderer.sortingLayerName = "Foreground";
            spriteRenderer.sortingOrder = 1;

            CircleCollider2D pickupCollider = pickupObject.AddComponent<CircleCollider2D>();
            pickupCollider.isTrigger = true;
            pickupCollider.radius = MileageColliderRadius;

            mileagePickups[i] = pickupObject.AddComponent<MileagePickup>();
            mileagePickups[i].Setup(mileageSprite, smallMileageValue);

            pickupObject.SetActive(false);
        }
    }

    private void EnsureItemPickups() {
        if(itemPickups != null && itemPickups.Length == ItemPoolSize)
        {
            return;
        }

        itemPickups = new ItemPickup[ItemPoolSize];

        for(int i = 0; i < ItemPoolSize; i++)
        {
            GameObject pickupObject = new GameObject("Tutorial Item Pickup " + (i + 1));
            pickupObject.transform.SetParent(transform);
            pickupObject.transform.localScale = GetParentScaleCompensatedVector(ItemTokenScale);

            SpriteRenderer spriteRenderer = pickupObject.AddComponent<SpriteRenderer>();
            spriteRenderer.sortingLayerName = "Foreground";
            spriteRenderer.sortingOrder = 2;

            CircleCollider2D pickupCollider = pickupObject.AddComponent<CircleCollider2D>();
            pickupCollider.isTrigger = true;
            pickupCollider.radius = ItemColliderRadius;

            itemPickups[i] = pickupObject.AddComponent<ItemPickup>();
            pickupObject.SetActive(false);
        }
    }

    private void HideMileagePickups() {
        if(mileagePickups == null)
        {
            return;
        }

        for(int i = 0; i < mileagePickups.Length; i++)
        {
            if(mileagePickups[i] != null)
            {
                mileagePickups[i].gameObject.SetActive(false);
            }
        }
    }

    private void HideItemPickups() {
        if(itemPickups == null)
        {
            return;
        }

        for(int i = 0; i < itemPickups.Length; i++)
        {
            if(itemPickups[i] != null)
            {
                itemPickups[i].gameObject.SetActive(false);
            }
        }
    }

    private void TryPlaceMileagePattern(GameObject activeObstacle) {
        if(mileageSprite == null || mileagePickups == null || Random.Range(0, 100) >= mileagePatternChance)
        {
            return;
        }

        if(activeObstacle == null)
        {
            PlaceSafeMileageLine();
            return;
        }

        if(activeObstacle.name.Contains("Slide"))
        {
            PlaceSlideMileageLine();
        }
        else
        {
            PlaceJumpMileageArc(GetWorldRelativeX(activeObstacle.transform));
        }
    }

    private void ApplyReservedPattern() {
        GameObject activeObstacle = null;

        switch(reservedPattern)
        {
            case PlatformPattern.LowLeft:
                activeObstacle = FindObstacle("Obstacle Left");
                break;
            case PlatformPattern.LowMid:
                activeObstacle = FindObstacle("Obstacle Mid");
                break;
            case PlatformPattern.LowRight:
                activeObstacle = FindObstacle("Obstacle Right");
                break;
            case PlatformPattern.Slide:
                activeObstacle = FindObstacle("Slide Obstacle Pattern");
                break;
            case PlatformPattern.ForceHit:
                activeObstacle = FindObstacle("Obstacle Mid");
                ActivateObstacle(activeObstacle);

                GameObject slideObstacle = FindObstacle("Slide Obstacle Pattern");
                reservedSlideLayout = SlideObstaclePattern.SlideLayout.WidePair;
                ActivateObstacle(slideObstacle);
                break;
        }

        if(reservedPattern != PlatformPattern.ForceHit && activeObstacle != null)
        {
            ActivateObstacle(activeObstacle);
        }

        PlaceReservedMileagePattern(activeObstacle);
        PlaceReservedItemPattern();
    }

    private void ActivateObstacle(GameObject obstacle) {
        if(obstacle == null)
        {
            return;
        }

        ConfigureSlideObstacle(obstacle);
        obstacle.SetActive(true);
    }

    private void ConfigureSlideObstacle(GameObject activeObstacle) {
        if(activeObstacle == null || !activeObstacle.name.Contains("Slide"))
        {
            return;
        }

        SlideObstaclePattern slideObstaclePattern = activeObstacle.GetComponent<SlideObstaclePattern>();
        if(slideObstaclePattern != null)
        {
            slideObstaclePattern.ConfigureLayout(reservedSlideLayout);
        }
    }

    private GameObject FindObstacle(string obstacleName) {
        for(int i = 0; i < obstacles.Length; i++)
        {
            if(obstacles[i] != null && obstacles[i].name == obstacleName)
            {
                return obstacles[i];
            }
        }

        return null;
    }

    private void PlaceReservedMileagePattern(GameObject activeObstacle) {
        if(mileageSprite == null || mileagePickups == null || reservedMileagePattern == MileagePattern.None)
        {
            return;
        }

        if(reservedMileagePattern == MileagePattern.SafeLine)
        {
            PlaceSafeMileageLine();
            return;
        }

        if(reservedMileagePattern == MileagePattern.JumpArc)
        {
            float obstacleX = activeObstacle != null ? GetWorldRelativeX(activeObstacle.transform) : 0f;
            PlaceJumpMileageArc(obstacleX);
            return;
        }

        if(reservedMileagePattern == MileagePattern.SlideLine)
        {
            PlaceSlideMileageLine();
            return;
        }

        TryPlaceMileagePattern(activeObstacle);
    }

    private void PlaceReservedItemPattern() {
        if(reservedItemPattern == ItemPattern.None || itemPickups == null || itemPickups.Length == 0)
        {
            return;
        }

        GameManager.TutorialItemType itemType = GetTutorialItemType(reservedItemPattern);
        ItemPickup pickup = itemPickups[0];
        pickup.Setup(LoadItemSprite(itemType), itemType);
        pickup.transform.localPosition = GetParentScaleCompensatedPosition(GetItemPosition(reservedItemPattern));
        pickup.ResetPickup();
    }

    private GameManager.TutorialItemType GetTutorialItemType(ItemPattern itemPattern) {
        switch(itemPattern)
        {
            case ItemPattern.InvincibleRamen:
                return GameManager.TutorialItemType.InvincibleRamen;
            case ItemPattern.Shield:
                return GameManager.TutorialItemType.Shield;
            case ItemPattern.SpeedShoes:
                return GameManager.TutorialItemType.SpeedShoes;
            case ItemPattern.MileageCard:
                return GameManager.TutorialItemType.MileageCard;
            case ItemPattern.HealthSushi:
            default:
                return GameManager.TutorialItemType.HealthSushi;
        }
    }

    private Vector2 GetItemPosition(ItemPattern itemPattern) {
        switch(itemPattern)
        {
            case ItemPattern.HealthSushi:
                return new Vector2(-1.6f, 1.55f);
            case ItemPattern.InvincibleRamen:
                return new Vector2(-0.8f, 1.55f);
            case ItemPattern.Shield:
                return new Vector2(-0.2f, 1.55f);
            case ItemPattern.SpeedShoes:
                return new Vector2(0.7f, 1.55f);
            case ItemPattern.MileageCard:
                return new Vector2(1.4f, 1.55f);
            default:
                return new Vector2(0f, 1.55f);
        }
    }

    private Sprite LoadItemSprite(GameManager.TutorialItemType itemType) {
#if UNITY_EDITOR
        string assetPath = "";

        switch(itemType)
        {
            case GameManager.TutorialItemType.InvincibleRamen:
                assetPath = "Assets/Sprites/Items/Item_Invincible_Ramen.png";
                break;
            case GameManager.TutorialItemType.Shield:
                assetPath = "Assets/Sprites/Items/Item_Shield.png";
                break;
            case GameManager.TutorialItemType.SpeedShoes:
                assetPath = "Assets/Sprites/Items/Item_Speed_Shoes.png";
                break;
            case GameManager.TutorialItemType.MileageCard:
                assetPath = "Assets/Sprites/Items/Item_Mileage_Card.png";
                break;
            case GameManager.TutorialItemType.HealthSushi:
            default:
                assetPath = "Assets/Sprites/Items/Item_Health_Sushi.png";
                break;
        }

        return AssetDatabase.LoadAssetAtPath<Sprite>(assetPath);
#else
        return null;
#endif
    }

    private void PlaceSafeMileageLine() {
        Vector2[] positions = {
            new Vector2(-2f, 1.35f),
            new Vector2(-1f, 1.35f),
            new Vector2(0f, 1.35f),
            new Vector2(1f, 1.35f),
            new Vector2(2f, 1.35f)
        };

        PlaceMileageTokens(positions, smallMileageValue);
    }

    private void PlaceJumpMileageArc(float obstacleX) {
        Vector2[] positions = {
            new Vector2(obstacleX - 1.2f, 1.65f),
            new Vector2(obstacleX, 2.25f),
            new Vector2(obstacleX + 1.2f, 1.65f),
            new Vector2(obstacleX + 1.9f, 1.35f)
        };

        PlaceMileageTokens(positions, smallMileageValue);
        SetPickupValue(1, bonusMileageValue);
    }

    private void PlaceSlideMileageLine() {
        Vector2[] positions = {
            new Vector2(-1.5f, 1.05f),
            new Vector2(-0.5f, 1.05f),
            new Vector2(0.5f, 1.05f),
            new Vector2(1.5f, 1.05f)
        };

        PlaceMileageTokens(positions, smallMileageValue);
        SetPickupValue(2, bonusMileageValue);
    }

    private void PlaceMileageTokens(Vector2[] positions, int value) {
        int count = Mathf.Min(positions.Length, mileagePickups.Length);

        for(int i = 0; i < count; i++)
        {
            mileagePickups[i].Setup(mileageSprite, value);
            mileagePickups[i].transform.localPosition = GetParentScaleCompensatedPosition(positions[i]);
            mileagePickups[i].ResetPickup();
        }
    }

    private Vector3 GetParentScaleCompensatedVector(float scale) {
        float parentScaleX = Mathf.Approximately(transform.localScale.x, 0f) ? 1f : transform.localScale.x;
        float parentScaleY = Mathf.Approximately(transform.localScale.y, 0f) ? 1f : transform.localScale.y;
        return new Vector3(scale / parentScaleX, scale / parentScaleY, 1f);
    }

    private Vector3 GetParentScaleCompensatedPosition(Vector2 position) {
        float parentScaleX = Mathf.Approximately(transform.localScale.x, 0f) ? 1f : transform.localScale.x;
        float parentScaleY = Mathf.Approximately(transform.localScale.y, 0f) ? 1f : transform.localScale.y;
        return new Vector3(position.x / parentScaleX, position.y / parentScaleY, 0f);
    }

    private float GetWorldRelativeX(Transform child) {
        return child.localPosition.x * transform.localScale.x;
    }

    private void SetPickupValue(int index, int value) {
        if(index < 0 || index >= mileagePickups.Length || !mileagePickups[index].gameObject.activeSelf)
        {
            return;
        }

        mileagePickups[index].Setup(mileageSprite, value);
    }
}
