using System;
using FMOD.Studio;
using FMODUnity;
using Gamecraft.Wires;
using RobocraftX.Blocks;
using RobocraftX.Common;
using Svelto.ECS;

namespace GamecraftModdingAPI.Blocks
{
    public class SfxBlock : SignalingBlock
    {
        public SfxBlock(EGID id) : base(id)
        {
        }

        public SfxBlock(uint id) : base(new EGID(id, CommonExclusiveGroups.SIMPLESFX_BLOCK_GROUP /* This could also be BUILD_LOOPEDSFX_BLOCK_GROUP */))
        {
        }

        public float Volume
        {
            get
            {
                return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => obj.tweakableVolume);
            }

            set
            {
                BlockEngine.SetBlockInfo(this,
                    (ref SoundSfxBlockDataEntityStruct obj, float val) => obj.tweakableVolume = val, value);
            }
        }
        
        public float Pitch
        {
            get
            {
                return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => obj.tweakablePitch);
            }

            set
            {
                BlockEngine.SetBlockInfo(this,
                    (ref SoundSfxBlockDataEntityStruct obj, float val) => obj.tweakablePitch = val, value);
            }
        }
        
        public bool Is3D
        {
            get
            {
                return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => obj.is3D);
            }
            
            set
            {
                BlockEngine.SetBlockInfo(this,
                    (ref SoundSfxBlockDataEntityStruct obj, bool val) => obj.is3D = val, value);
            }
        }
        
        public ChannelType ChannelType
        {
            get
            {
                return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => (ChannelType)obj.channelType);
            }

            set
            {
                BlockEngine.SetBlockInfo(this,
                    (ref SoundSfxBlockDataEntityStruct obj, ChannelType val) => obj.tweakableVolume = (byte) val, value);
            }
        }
        
        public byte TrackIndex
        {
            get
            {
                return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => obj.soundEffectIndex);
            }
            
            set
            {
                BlockEngine.SetBlockInfo(this,
                    (ref SoundSfxBlockDataEntityStruct obj, byte val) => obj.soundEffectIndex = val, value);
            }
        }
        
        // track
        public Guid Track
        {
            get
            {
                return BlockEngine.GetBlockInfo(this,
                    (SoundSfxBlockDataEntityStruct obj) => obj.is3D ? obj.fmod3DEventPaths.Get<Guid>(obj.soundEffectIndex) : obj.fmod2DEventPaths.Get<Guid>(obj.soundEffectIndex));
            }

            set
            {
                BlockEngine.SetBlockInfo(this, (ref SoundSfxBlockDataEntityStruct obj, Guid val) =>
                {
                    for (byte i = 0; i < obj.fmod2DEventPaths.Count<Guid>(); i++)
                    {
                        Guid track = obj.fmod2DEventPaths.Get<Guid>(i);
                        if (track == val)
                        {
                            obj.soundEffectIndex = i;
                            obj.is3D = false;
                            return;
                        }
                    }
                    for (byte i = 0; i < obj.fmod3DEventPaths.Count<Guid>(); i++)
                    {
                        Guid track = obj.fmod3DEventPaths.Get<Guid>(i);
                        if (track == val)
                        {
                            obj.soundEffectIndex = i;
                            obj.is3D = true;
                            return;
                        }
                    }
                }, value);
            }
        }
        
        // all tracks
        public Guid[] Tracks2D
        {
            get
            {
                return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) =>
                {
                    Guid[] tracks = new Guid[obj.fmod2DEventPaths.Count<Guid>()];
                    for (byte i = 0; i < tracks.Length; i++)
                    {
                        tracks[i] = obj.fmod2DEventPaths.Get<Guid>(i);
                    }
                    return tracks;
                });
            }
        }
        
        public Guid[] Tracks3D
        {
            get
            {
                return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) =>
                {
                    Guid[] tracks = new Guid[obj.fmod3DEventPaths.Count<Guid>()];
                    for (byte i = 0; i < tracks.Length; i++)
                    {
                        tracks[i] = obj.fmod2DEventPaths.Get<Guid>(i);
                    }
                    return tracks;
                });
            }
        }

        public bool IsLooped
        {
            get
            {
                return BlockEngine.GetBlockInfo(this, (SoundSfxBlockDataEntityStruct obj) => obj.isLoopedBlock);
            }
            
            set
            {
                BlockEngine.SetBlockInfo(this,
                    (ref SoundSfxBlockDataEntityStruct obj, bool val) => obj.isLoopedBlock = val, value);
            }
        }
        
        public bool IsPlaying
        {
            get
            {
                return BlockEngine.GetBlockInfo(this,
                    (SoundSfxBlockDataEntityStruct obj) => obj.isPlaying);
            }

            set
            {
                BlockEngine.SetBlockInfo(this, (ref SoundSfxBlockDataEntityStruct obj, bool val) =>
                {
                    if (obj.isPlaying == val) return;
                    if (val)
                    {
                        // start playing
                        EventInstance inst = RuntimeManager.CreateInstance(obj.is3D ? obj.fmod3DEventPaths.Get<Guid>(obj.soundEffectIndex) : obj.fmod2DEventPaths.Get<Guid>(obj.soundEffectIndex));
                        inst.setVolume(obj.tweakableVolume / 100f);
                        inst.start();
                        obj.eventHandle = inst.handle;
                    }
                    else
                    {
                        // stop playing
                        EventInstance inst = default(EventInstance);
                        inst.handle = obj.eventHandle;
                        inst.stop(FMOD.Studio.STOP_MODE.ALLOWFADEOUT);
                        inst.release();
                    }
                    obj.isPlaying = val;
                }, value);
            }
        }
    }
}