#include "fmod_settings.h"

#ifdef FMOD_SUPPORT_OPENAL
#ifdef FMOD_SUPPORT_EAX

#include "fmod_output_openal.h"
#include "fmod_eax4.h"

namespace FMOD
{

bool gEAX4LockedOALFxSlot2 = false;
bool gEAX4LockedOALFxSlot3 = false;

/*
[
	[DESCRIPTION]
    Attempt to lock an EAX slot

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]

    [PLATFORMS]
    Win32, Win64

	[SEE_ALSO]
]
*/
FMOD_RESULT OutputOpenAL::lockSlotEAX4(GUID &slotGUID)
{
    int                  slotIsLocked;
    EAXFXSLOTPROPERTIES  fxSlot;

    FMOD_memcpy(&fxSlot.guidLoadEffect, &FMOD_EAX_REVERB_EFFECT, sizeof(GUID));

    /*
        Check if this slot is locked
    */
    mEAXGet(&slotGUID, EAXFXSLOT_LOCK, 0, &slotIsLocked, sizeof(int));
    if (mOALFnTable.alGetError() != AL_NO_ERROR)
    {
        return FMOD_ERR_REVERB_INSTANCE;
    }

    if (slotIsLocked)
    {
        GUID effect;

        // Find out what type of effect is in the locked slot
        mEAXGet(&slotGUID, EAXFXSLOT_LOADEFFECT, 0, &effect, sizeof(GUID));
        if (mOALFnTable.alGetError() != AL_NO_ERROR)
        {
            return FMOD_ERR_REVERB_INSTANCE;
        }
        
        // Check if the slot contains a reverb, if so we can still use that slot
        if (memcmp(&effect, &fxSlot.guidLoadEffect, sizeof(GUID)))
        {
            return FMOD_ERR_REVERB_INSTANCE;
        }
    }
    else    // Slot isn't locked
    {       
        fxSlot.lLock    = EAXFXSLOT_LOCKED;
        fxSlot.lVolume  = EAXFXSLOT_DEFAULTVOLUME;
        fxSlot.ulFlags  = EAXFXSLOT_DEFAULTFLAGS;

        // Lock the slot
        mEAXSet(&slotGUID, EAXFXSLOT_ALLPARAMETERS, 0, &fxSlot, sizeof(EAXFXSLOTPROPERTIES));
        if (mOALFnTable.alGetError() != AL_NO_ERROR)
        {
            return FMOD_ERR_REVERB_INSTANCE;
        }
    }

    return FMOD_OK;
}


/*
[
	[DESCRIPTION]
    Set properties for EAX4

	[PARAMETERS]
 
	[RETURN_VALUE]
    FMOD_OK

	[REMARKS]
    With EAX4 the first slot is locked as reverb so we can still use it,
    the second slot is locked as chorus, so it is no use to us, slots
    3 and 4 are free to be tasked as reverb

    [PLATFORMS]
    Win32, Win64

	[SEE_ALSO]
]
*/
FMOD_RESULT OutputOpenAL::setPropertiesEAX4(const FMOD_REVERB_PROPERTIES *prop)
{
    GUID                 slotGUID;
    GUID                 sourceGUID;
    EAXREVERBPROPERTIES  eaxProperties;
    FMOD_RESULT          result;
    int                  room;

    if (!prop)
    {
        return FMOD_ERR_INVALID_PARAM;
    }

    FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "OutputOpenAL::setPropertiesEAX4", "Setting reverb properties for instance: %d\n", prop->Instance));

    /*
        Make sure all reverb effects are disabled on the mixer sources
    */
    if (!mMixerReverbDisabled)
    {
        room = -10000;
        FMOD_memcpy(&sourceGUID, &FMOD_EAXPROPERTYID_EAX40_Source, sizeof(GUID));

        // Only the first mixer source is technically used (multichannel source), but set all anyway
        for (int i = mMixerSourceOffset; i < mNumSources; i++)
        {
            mEAXSet(&sourceGUID, EAXSOURCE_ROOM, mSources[i].sid, &room, sizeof(int));
            if (mOALFnTable.alGetError() != AL_NO_ERROR)
            {
		        return FMOD_ERR_INTERNAL;
            }
        }
        
        mMixerReverbDisabled = true;
    }

    /*
        Each reverb instance is equivalent to a hardware effect slot,
        attempt to lock that slot for use by FMOD
    */
    switch (prop->Instance)
    {
        case 1:
        {
            FMOD_memcpy(&slotGUID, &FMOD_EAXPROPERTYID_EAX40_FXSlot2, sizeof(GUID));
            if (!gEAX4LockedOALFxSlot2)
            {
                result = lockSlotEAX4(slotGUID);
                if (result != FMOD_OK)
                {
                    return result;
                }

                gEAX4LockedOALFxSlot2 = true;
            }
            break;
        }
        case 2:
        {
            FMOD_memcpy(&slotGUID, &FMOD_EAXPROPERTYID_EAX40_FXSlot3, sizeof(GUID));
            if (!gEAX4LockedOALFxSlot3)
            {
                result = lockSlotEAX4(slotGUID);
                if (result != FMOD_OK)
                {
                    return result;
                }

                gEAX4LockedOALFxSlot3 = true;
            }
            break;
        }
        default:
        {
            FMOD_memcpy(&slotGUID, &FMOD_EAXPROPERTYID_EAX40_FXSlot0, sizeof(GUID));
        }
    }

    eaxProperties.ulEnvironment           = prop->Environment < 0 ? 0 : prop->Environment;
    eaxProperties.flEnvironmentSize       = prop->EnvSize;           
    eaxProperties.flEnvironmentDiffusion  = prop->EnvDiffusion;      
    eaxProperties.lRoom                   = prop->Room;              
    eaxProperties.lRoomHF                 = prop->RoomHF;            
    eaxProperties.lRoomLF                 = prop->RoomLF;            
    eaxProperties.flDecayTime             = prop->DecayTime;         
    eaxProperties.flDecayHFRatio          = prop->DecayHFRatio;      
    eaxProperties.flDecayLFRatio          = prop->DecayLFRatio;      
    eaxProperties.lReflections            = prop->Reflections;       
    eaxProperties.flReflectionsDelay      = prop->ReflectionsDelay;  
    eaxProperties.vReflectionsPan.x       = prop->ReflectionsPan[0]; 
    eaxProperties.vReflectionsPan.y       = prop->ReflectionsPan[1]; 
    eaxProperties.vReflectionsPan.z       = prop->ReflectionsPan[2]; 
    eaxProperties.lReverb                 = prop->Reverb;            
    eaxProperties.flReverbDelay           = prop->ReverbDelay;       
    eaxProperties.vReverbPan.x            = prop->ReverbPan[0];
    eaxProperties.vReverbPan.y            = prop->ReverbPan[1];
    eaxProperties.vReverbPan.z            = prop->ReverbPan[2];
    eaxProperties.flEchoTime              = prop->EchoTime;          
    eaxProperties.flEchoDepth             = prop->EchoDepth;         
    eaxProperties.flModulationTime        = prop->ModulationTime;    
    eaxProperties.flModulationDepth       = prop->ModulationDepth;   
    eaxProperties.flAirAbsorptionHF       = prop->AirAbsorptionHF;   
    eaxProperties.flHFReference           = prop->HFReference;       
    eaxProperties.flLFReference           = prop->LFReference;       
    eaxProperties.flRoomRolloffFactor     = prop->RoomRolloffFactor; 
    eaxProperties.ulFlags                 = prop->Flags & 0xFF;     // Mask out FMOD specific flags, leaving only EAX flags

    mEAXSet(&slotGUID, EAXREVERB_ALLPARAMETERS, 0, &eaxProperties, sizeof(EAXREVERBPROPERTIES));
    if (mOALFnTable.alGetError() != AL_NO_ERROR)
    {
        FLOG((FMOD_DEBUG_LEVEL_ERROR, __FILE__, __LINE__, "OutputOpenAL::setPropertiesEAX4", "Error setting reverb properties\n"));
        return FMOD_ERR_OUTPUT_DRIVERCALL;
    }

	return FMOD_OK;
}

}       /* namespace FMOD */

#endif  /* FMOD_SUPPORT_EAX */
#endif  /* FMOD_SUPPORT_OPENAL */
