using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Decal.Interop.Filters;

namespace Decal.DecalFilters
{
    [ComVisible(true)]
    [Guid("50A7E9EC-AB12-4484-9C28-C2A39274A636")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("DecalFilters.WorldObject")]
    public class WorldObjectImpl : IWorldObject
    {
        private readonly Dictionary<int, int> _longs = new Dictionary<int, int>();
        private readonly Dictionary<int, double> _doubles = new Dictionary<int, double>();
        private readonly Dictionary<int, bool> _bools = new Dictionary<int, bool>();
        private readonly Dictionary<int, string> _strings = new Dictionary<int, string>();
        private readonly List<int> _spells = new List<int>();
        private readonly List<int> _activeSpells = new List<int>();

        // Position data
        private double _x, _y, _z;
        private double _ow, _ox, _oy, _oz;
        private int _landcell;

        public int GUID { get; internal set; }
        public string Name { get; internal set; } = string.Empty;
        public eObjectClass ObjectClass { get; internal set; }
        public bool HasIdData { get; internal set; }
        public int LastIdTime { get; internal set; }
        public int PhysicsDataFlags { get; internal set; }
        public int GameDataFlags1 { get; internal set; }
        public int Type { get; internal set; }
        public int Icon { get; internal set; }
        public int Category { get; internal set; }
        public int Behavior { get; internal set; }
        public int Container { get; internal set; }

        public bool Offset(out double x, out double y, out double z)
        {
            x = _x; y = _y; z = _z;
            return _landcell != 0;
        }

        public bool Coordinates(ref double NorthSouth, ref double EastWest)
        {
            // Convert raw coordinates to map coordinates
            NorthSouth = _y; EastWest = _x;
            return _landcell != 0;
        }

        public bool RawCoordinates(ref double pX, ref double pY, ref double pZ)
        {
            pX = _x; pY = _y; pZ = _z;
            return _landcell != 0;
        }

        public bool Orientation(out double w, out double x, out double y, out double z)
        {
            w = _ow; x = _ox; y = _oy; z = _oz;
            return true;
        }

        public int Longs(LongValueKey index, int defVal = 0) =>
            _longs.TryGetValue((int)index, out int v) ? v : defVal;

        public double Doubles(DoubleValueKey index, double defVal = 0.0) =>
            _doubles.TryGetValue((int)index, out double v) ? v : defVal;

        public bool Bools(BoolValueKey index, bool defVal = false) =>
            _bools.TryGetValue((int)index, out bool v) ? v : defVal;

        public string Strings(StringValueKey index, string defVal = "") =>
            _strings.TryGetValue((int)index, out string v) ? v : defVal;

        public bool LongExists(LongValueKey index, out int pValue) =>
            _longs.TryGetValue((int)index, out pValue);

        public bool DoubleExists(DoubleValueKey index, out double pValue) =>
            _doubles.TryGetValue((int)index, out pValue);

        public bool BoolExists(BoolValueKey index, out bool pValue) =>
            _bools.TryGetValue((int)index, out pValue);

        public bool StringExists(StringValueKey index, out string pValue) =>
            _strings.TryGetValue((int)index, out pValue);

        public int Spell(int index) => index >= 0 && index < _spells.Count ? _spells[index] : 0;
        public int ActiveSpell(int index) => index >= 0 && index < _activeSpells.Count ? _activeSpells[index] : 0;

        public int EnumBoolKey(int index)
        {
            int i = 0;
            foreach (var key in _bools.Keys)
                if (i++ == index) return key;
            return -1;
        }

        public int EnumLongKey(int index)
        {
            int i = 0;
            foreach (var key in _longs.Keys)
                if (i++ == index) return key;
            return -1;
        }

        public int EnumDoubleKey(int index)
        {
            int i = 0;
            foreach (var key in _doubles.Keys)
                if (i++ == index) return key;
            return -1;
        }

        public int EnumStringKey(int index)
        {
            int i = 0;
            foreach (var key in _strings.Keys)
                if (i++ == index) return key;
            return -1;
        }

        // Internal setters
        internal void SetLong(int key, int value) => _longs[key] = value;
        internal void SetDouble(int key, double value) => _doubles[key] = value;
        internal void SetBool(int key, bool value) => _bools[key] = value;
        internal void SetString(int key, string value) => _strings[key] = value;
        internal void SetPosition(double x, double y, double z, int landcell)
        {
            _x = x; _y = y; _z = z; _landcell = landcell;
        }
        internal void SetOrientation(double w, double x, double y, double z)
        {
            _ow = w; _ox = x; _oy = y; _oz = z;
        }
        internal void AddSpell(int spellId) => _spells.Add(spellId);
        internal void AddActiveSpell(int spellId) => _activeSpells.Add(spellId);
    }
}
