#include "soundmanager.h"

#include "SySoundDevice.h"
#include "SyScene.h"
#include "SySoundSprite.h"

#include "titan.h"
#include "gameerror.h"


typedef SyListI GameSoundHandle;


cSoundManager::cSoundManager()
: mpTitan(NULL)
{
}


cSoundManager::~cSoundManager()
{
}

bool cSoundManager::Init(Titan* pTitan)
{
  SyAssert(pTitan);

  mpTitan = pTitan;

  return true;
}

void cSoundManager::Update(float time)
{
  SyScene* pScene = mpTitan->GetScene();
  SySoundDevice* pSoundDev = pScene->GetSoundDev();
  SySprite* pSprite;

  SyListI i = mSounds.Begin();
  
  while (i != mSounds.End())
  {
    SoundInfo& info = mSounds(i);
    if (SyActorNull != info.mActorHandle)
    {
      pSprite = pScene->GetActorSpritePtr(info.mActorHandle);

      GAME_ASSERT(ERROR_CODE, pSprite && pSprite->GetType() == SYSPRITETYPE_SOUNDSPRITE, "Trying to playback invalid sound sprite - deleting");

      if (pSprite && pSprite->GetType() == SYSPRITETYPE_SOUNDSPRITE && static_cast<SySoundSprite*>(pSprite)->mPlaybackHandle != -1)
      {
        if (!pSoundDev->IsPlaying(static_cast<SySoundSprite*>(pSprite)->mPlaybackHandle))
        {
          pScene->ReleaseActor(info.mActorHandle);
          i = mSounds.Erase(i);
          continue;
        }
      }
      else
      {
        pScene->ReleaseActor(info.mActorHandle);
        i = mSounds.Erase(i);
        continue;
      }
    }
    else if (-1 != info.mPlaybackHandle)
    {
      if (!pSoundDev->IsPlaying(info.mPlaybackHandle))
      {
        i = mSounds.Erase(i);
        continue;
      }
    }
    else
    {
      GAME_ASSERT(ERROR_CODE, false, "Sound with no actor or handle - deleting");
      i = mSounds.Erase(i);
      continue;
    }

    info.mLifetime += time;

    i = mSounds.Next(i);
  }
}

bool cSoundManager::Play(tGameObjectID objID, const char* soundFileName, int priority)
{
  if (!mpTitan)
  {
    return false;
  }

  int assetHandle = mpTitan->LoadAssetSound(mpTitan->GetTitanUI()->GetESFParser(), soundFileName);

  if (-1 == assetHandle)
  {
    return false;
  }

  int32 playbackHandle = mpTitan->GetScene()->GetSoundDev()->Play(assetHandle, priority, SySoundDevice::kSoundClassFx);
  if (-1 == playbackHandle)
  {
    return false;
  }

  GameSoundHandle handle = mSounds.Add();

  if(handle == SyListNull)
  {
    return false;
  }

  mSounds(handle).mObjID = objID;
  mSounds(handle).mActorHandle = SyActorNull;
  mSounds(handle).mPlaybackHandle = playbackHandle;
  mSounds(handle).mAssetHandle = assetHandle;
  mSounds(handle).mLifetime = 0.0f;

  return true;
}

bool cSoundManager::Play3D(tGameObjectID objID, const char* soundFileName, int priority, const SyVect3& loc, float minRadius)
{
  if (!mpTitan)
  {
    return false;
  }

  SyScene* pScene = mpTitan->GetScene();
  int assetHandle = mpTitan->LoadAssetSprite(mpTitan->GetTitanUI()->GetESFParser(), soundFileName, true);
  SyActorHandle actorHandle = SyActorNull;
  
  if (-1 == assetHandle || 
      (pScene->Sprite(assetHandle) && pScene->Sprite(assetHandle)->GetType() != SYSPRITETYPE_SOUNDSPRITE))
  {
    assetHandle = mpTitan->LoadAssetSound(mpTitan->GetTitanUI()->GetESFParser(), soundFileName);

    if (-1 == assetHandle)
    {
      return false;
    }
  }
  else
  {
    actorHandle = pScene->CreateActor(loc, SyVect3(0.0f, 0.0f, 0.f), minRadius, assetHandle);
    if (SyActorNull == actorHandle)
    {
      return false;
    }
  }

  int32 playbackHandle = -1;

  if (SyActorNull == actorHandle)
  {
    playbackHandle = pScene->GetSoundDev()->Play3d(assetHandle, priority, SySoundDevice::kSoundClassFx, loc, minRadius);

    if (playbackHandle == -1)
    {
      if (SyActorNull != actorHandle)
      {
        pScene->ReleaseActor(actorHandle);
      }

      return false;
    }
  }
  else
  {
    SySprite* pSprite = pScene->GetActorSpritePtr(actorHandle);
    GAME_ASSERT(ERROR_CODE, pSprite && pSprite->GetType() == SYSPRITETYPE_SOUNDSPRITE, "Bad sprite in SoundManager::Play3D - '%s'", soundFileName);

    if (!pSprite || pSprite->GetType() != SYSPRITETYPE_SOUNDSPRITE)
    {
      if (SyActorNull != actorHandle)
      {
        pScene->ReleaseActor(actorHandle);
      }

      return false;
    }
  }

  GameSoundHandle handle = mSounds.Add();

  if(handle == SyListNull)
  {
    if (SyActorNull != actorHandle)
    {
      pScene->ReleaseActor(actorHandle);
    }

    return false;
  }

  mSounds(handle).mPlaybackHandle = playbackHandle;
  mSounds(handle).mObjID = objID;
  mSounds(handle).mActorHandle = actorHandle;
  mSounds(handle).mAssetHandle = assetHandle;
  mSounds(handle).mLifetime = 0.0f;
  return true;
}

bool cSoundManager::PlayImpact(int32 resourceHandle, const SyVect3& loc)
{
  if (!mpTitan || resourceHandle <= 0)
  {
    return false;
  }

  SyScene* pScene = mpTitan->GetScene();
  SyActorHandle actorHandle = SyActorNull;

  static const float radius = 3.0f;
  actorHandle = pScene->CreateActor(loc, SyVect3(0.0f, 0.0f, 0.f), radius, resourceHandle);

  if (SyActorNull == actorHandle)
  {
    return false;
  }

  int32 playbackHandle = -1;

  if (SyActorNull == actorHandle)
  {
    playbackHandle = pScene->GetSoundDev()->Play3d(resourceHandle, 1, SySoundDevice::kSoundClassFx, loc, radius);

    if (playbackHandle == -1)
    {
      if (SyActorNull != actorHandle)
      {
        pScene->ReleaseActor(actorHandle);
      }

      return false;
    }
  }
  else
  {
    SySprite* pSprite = pScene->GetActorSpritePtr(actorHandle);
    GAME_ASSERT(ERROR_CODE, pSprite && pSprite->GetType() == SYSPRITETYPE_SOUNDSPRITE, "Bad sprite in SoundManager::PlayImpact");

    if (!pSprite || pSprite->GetType() != SYSPRITETYPE_SOUNDSPRITE)
    {
      if (SyActorNull != actorHandle)
      {
        pScene->ReleaseActor(actorHandle);
      }

      return false;
    }
  }

  GameSoundHandle handle = mSounds.Add();

  if(handle == SyListNull)
  {
    if (SyActorNull != actorHandle)
    {
      pScene->ReleaseActor(actorHandle);
    }

    return false;
  }

  mSounds(handle).mPlaybackHandle = playbackHandle;
  mSounds(handle).mObjID = ID_NONE;
  mSounds(handle).mActorHandle = actorHandle;
  mSounds(handle).mAssetHandle = resourceHandle;
  mSounds(handle).mLifetime = 0.0f;
  return true;
}

void cSoundManager::Stop(tGameObjectID objID)
{
  if (!mpTitan)
  {
    return;
  }

  SyScene* pScene = mpTitan->GetScene();
  SySoundDevice* pSoundDev = pScene->GetSoundDev();

  SyListI i = mSounds.Begin();

  while (i != mSounds.End())
  {
    SoundInfo& info = mSounds(i);
    if (objID == info.mObjID)
    {
      if (-1 != info.mPlaybackHandle)
      {
        pSoundDev->Stop(info.mPlaybackHandle);
      }

      if (SyActorNull != info.mActorHandle)
      {
        pScene->ReleaseActor(info.mActorHandle);
        info.mActorHandle = SyActorNull;
      }

      i = mSounds.Erase(i);
      continue;
    }
    else
    {
      i = mSounds.Next(i);
    }
  }
}

void cSoundManager::StopAll()
{
  if (!mpTitan)
  {
    return;
  }

  SyScene* pScene = mpTitan->GetScene();
  SySoundDevice* pSoundDev = pScene->GetSoundDev();

  SyListI i = mSounds.Begin();

  while (i != mSounds.End())
  {
    SoundInfo& info = mSounds(i);
    if (-1 != info.mPlaybackHandle)
    {
      pSoundDev->Stop(info.mPlaybackHandle);
    }

    if (SyActorNull != info.mActorHandle)
    {
      pScene->ReleaseActor(info.mActorHandle);
      info.mActorHandle = SyActorNull;
    }

    i = mSounds.Erase(i);
  }
}
