using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Assets.Scripts.Player;
using UnityEngine;

namespace Assets.Scripts.Battle
{
    public class BattleEngine
    {
        private Mob[] _allies;
        private Mob[] _enemies;
        private Mob[] _all;

        public BattleEngine(Mob[] allies, Mob[] enemies) {
            _allies = allies;
            _enemies = enemies;
            _all = allies.Concat(enemies).ToArray();

            foreach (var m in enemies) {
                m.Convert(Allegiance.ENEMY);
                m.Heal(m.GetAttribute(MobAttribute.MaxHealth) / 2);
            }

            UpdateBattlePositions(true);

            int i = 0;
            foreach (var m in _all) {
                m.InitializeBattle(i++);
            }
        }

        public Mob[] AllMobs {
            get { return _all; }
        }

        public bool IsBattleOver() {
            return _enemies.Length == 0 || _allies.Length == 0;
        }

        public void Update(float time) {
            bool everyoneAlive = true;
            foreach (var m in _all) {
                m.UpdateBattle(time, this);
            }

            foreach (var m in _all) {
                bool alive = m.IsAlive();
                everyoneAlive &= alive;
                if (!alive) {
                    FillGap(m.GetTargetBattlePosition());
                }
            }

            if (!everyoneAlive) {
                TrimMobs();
            }
        }

        private static bool InRange(int value, int r0, int r1) {
            int min = Mathf.Min(r0, r1);
            int max = Mathf.Max(r0, r1);
            return value >= min && value <= max;
        }

        public void MoveCharacter(Mob mob, int pos) {
            int prevPos = mob.GetTargetBattlePosition();
            Mob[] array = prevPos < 0 ? _allies : _enemies;
            pos = prevPos < 0 ? Mathf.Clamp(pos, -array.Length, -1) : Mathf.Clamp(pos, 0, array.Length - 1);

            int delta = pos < prevPos ? 1 : -1;
            foreach (var m in array.Where(m => InRange(m.GetTargetBattlePosition(), prevPos - delta, pos))) {
                m.MoveBy(delta);
            }

            mob.SetBattlePosition(pos);
        }

        private void FillGap(int position) {
            Mob[] array = position < 0 ? _allies : _enemies;
            int delta = position < 0 ? 1 : -1;
            foreach (var m in array) {
                if (Mathf.Abs(m.GetTargetBattlePosition()) > Mathf.Abs(position)) {
                    m.SetBattlePosition(m.GetTargetBattlePosition() + delta);
                }
            }
        }

        private void TrimMobs() {
            _allies = _allies.Where(m => m.IsAlive()).ToArray();
            _enemies = _enemies.Where(m => m.IsAlive()).ToArray();
            _all = _allies.Concat(_enemies).ToArray();
        }

        private void UpdateBattlePositions(bool teleport) {
            for (int i = 0; i < _allies.Length; i++) {
                _allies[i].SetBattlePosition(i - _allies.Length, teleport);
            }

            for (int i = 0; i < _enemies.Length; i++) {
                _enemies[i].SetBattlePosition(i, teleport);
            }
        }

        public Mob FindTarget(Mob mob) {
            return FindEnemyTarget(mob);
        }

        private Mob FindEnemyTarget(Mob mob) {
            Mob bestTarget = null;
            float bestDistance = 99999;

            foreach (var m in _all) {
                // Don't target allies
                if (m.GetAllegiance() == mob.GetAllegiance()) {
                    continue;
                }

                float d = Mathf.Abs(m.GetBattlePosition() - mob.GetBattlePosition());
                if (d < bestDistance) {
                    bestDistance = d;
                    bestTarget = m;
                }
            }

            return bestTarget;
        }
    }
}
