//=============================================================================
// FILE: app.cpp
//
//                  Copyright (c) 2017 QUALCOMM Technologies Inc.
//                              All Rights Reserved.
//
//=============================================================================

#include "app.h"

#include "../../common/spatialaudio_utility.h"
#include "glm/gtx/transform.hpp"
#include "svrConfig.h"
#include "svrGeometry.h"
#include "svrUtil.h"


using namespace Svr;
using namespace glm;


EXTERN_VAR(bool, gLooping);
EXTERN_VAR(bool, gLogObjectTransform);

EXTERN_VAR(int, gRolloffA_Type);
EXTERN_VAR(float, gRolloffA_MinDistance);
EXTERN_VAR(float, gRolloffA_MaxDistance);
EXTERN_VAR(bool, gRolloffBugWorkaround);

VAR(float, gObjectDistance, 5.f, kVariableNonpersistent);

void ObjectStatic::Initialize()
{
	SpatialAudioCommon::Initialize();

	InitializeModelAndTexture(
		&mpFloorModel,
		&mFloorShader,
		&mFloorTexture,
		mAppContext.externalPath,
		"models/grounddisc.obj",
		"shaders/floor_v.glsl",
		"shaders/floor_f.glsl",
		"floorModelVs",
		"floorModelFs",
		"textures/grounddisc_gradient.ktx");

	InitializeModelAndTexture(
		&mpSkyModel,
		&mSkyShader,
		&mSkyTexture,
		mAppContext.externalPath,
		"models/skyBall.obj",
		"shaders/sky_v.glsl",
		"shaders/sky_f.glsl",
		"skyModelVs",
		"skyModelFs",
		"textures/testSkyBox_hdri_sgs.ktx");

	InitializeModel(
		&mpSphereModel,
		&mSphereShader,
		mAppContext.externalPath,
		"models/sphere.obj",
		"shaders/model_v.glsl",
		"shaders/model_f.glsl",
		"modelVs",
		"modelFs");

	//zero out mId to show this sound is not created yet
	memset(mSoundObject, 0, sizeof(mSoundObject[0])*SPATIAL_AUDIO_SOUND_OBJECT_NUM);
	int fileNameIndex = 0;
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/0_Bass Guitar.wav");
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/1_KurzweilPC3K8_Program_Hotrod Dyno Rhd.wav");
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/2_ChurchOrgn.wav");
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/3_Hubshraube.wav");
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/4_Fluffy in Heaven.wav");
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/5_Tuba.wav");
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/6_English Combined Choir.wav");
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/7_Rotor Mallet.wav");
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/8_Steam Pipe Flute.wav");
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/9_TubularBell.wav");
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/10_Purple.wav");
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/11_VSL Performance Harp_ HA_NORMAL.wav");
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/12_ChucK.wav");
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/13_Post Musical Instruments Post Historic Keyboard Flemish Harpsichord NR 1 Soft Release.wav");
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/14_Violins.wav");
	snprintf(mSoundObject[fileNameIndex++].mFilename, Q3D_SOUND_FILENAME_MAX_LEN, "%s/%s", mAppContext.externalPath, "audio/15_Italian Grand 12 Level II.wav");

	Q3DAudioPreloadSoundFiles(GetSpatialAudio(), mSoundObject, SPATIAL_AUDIO_SOUND_OBJECT_NUM);

	for (int i = 0; i < SPATIAL_AUDIO_SOUND_OBJECT_NUM; ++i)
	{
		Q3DSound& sound = mSoundObject[i];
		GetRolloffSettings(
			&sound.mRolloffType,
			&sound.mRolloffMinDistance,
			&sound.mRolloffMaxDistance,
			GetUserConfigRolloffConfigurationIndex());
	}

	PositionsOnSphereCalculate(mSoundObjectPosition, gObjectDistance);

	InitializeGazeBoxes(GetGazeSphereManager(), kGazeBoxNum);
	char texturePaths[kGazeBoxNum][SPATIAL_AUDIO_TEXTURE_PATH_MAX_LEN];
	GetDefaultGazeBoxTexturePaths(texturePaths, kGazeBoxNum);
	InitializeGazeBoxRenderResources(
		&mpGazeBoxModel,
		&mGazeBoxShader,
		mGazeBoxTexture,
        &mGazeBoxTextureSampler,
		mGazeBoxLastHsvSaturation,
		mGazeBoxLastHsvValue,
		texturePaths,
		mAppContext.externalPath,
		kGazeBoxNum);

	GazeSphere soundObjectGazeSphereInitializers[SPATIAL_AUDIO_SOUND_OBJECT_NUM];
	for (int i = 0; i < SPATIAL_AUDIO_SOUND_OBJECT_NUM; ++i)
	{
		const vec3 position = mSoundObjectPosition[i];
		mSoundObjectLocalToWorld[i] = translate(glm::mat4(1.0f), position);
		
		const vec3 positionUnitLength = normalize(position);
		vec3& soundObjectColorDefault = mSoundObjectColorDefault[i];
		soundObjectColorDefault = ColorCode(positionUnitLength);
		InitializeLastSaturationAndValue(
			&mSoundObjectSphereLastHsvSaturation[i], 
			&mSoundObjectSphereLastHsvValue[i], 
			soundObjectColorDefault);

		const float gazeSphereRadius = 1.f;
		soundObjectGazeSphereInitializers[i].Initialize(gazeSphereRadius, position);
		
	}
	GetGazeSphereManager()->AddGazeSpheres(soundObjectGazeSphereInitializers, SPATIAL_AUDIO_SOUND_OBJECT_NUM);
	SetGazeBoxActiveDefaultsReverbRolloff(mGazeBoxActive, gLooping);
}

void ObjectStatic::Shutdown()
{
    if (GetSpatialAudio())
    {
        for (int i = 0; i < SPATIAL_AUDIO_SOUND_OBJECT_NUM; ++i)
        {
            Q3DAudioSoundStopAndInvalidateHandle(GetSpatialAudio(), mSoundObject[i].mId);
        }
        Q3DAudioUnloadSoundFiles(GetSpatialAudio(), mSoundObject, SPATIAL_AUDIO_SOUND_OBJECT_NUM);
    }

    DestroyGazeBoxRenderResources(&mpGazeBoxModel, &mGazeBoxShader, mGazeBoxTexture, &mGazeBoxTextureSampler, kGazeBoxNum);

    if (mpSkyModel)
    {
        DestroyGeometry(&mpSkyModel);
    }
    mSkyShader.Destroy();

    if (mSkyTexture)
    {
        DestroyTexture(&mSkyTexture);
    }

    if (mpFloorModel)
    {
        DestroyGeometry(&mpFloorModel);
    }

    mFloorShader.Destroy();

    if (mFloorTexture)
    {
        DestroyTexture(&mFloorTexture);
    }

    if (mpSphereModel)
    {
        DestroyGeometry(&mpSphereModel);
    }

	mSphereShader.Destroy();
    
	SpatialAudioCommon::Shutdown();
}


void ObjectStatic::OnTouchScreen()
{
}

static int GazeSphereSoundObjectIndexStart()
{
	return SpatialAudioCommon::kGazeBoxNum;
}
static int GazeSphereSoundObjectIndexEnd()
{
	return GazeSphereSoundObjectIndexStart() + SPATIAL_AUDIO_SOUND_OBJECT_NUM;
}
static bool GazeSphereIndexIsSoundObject(const int gazeSphereIndex)
{
	return gazeSphereIndex >= GazeSphereSoundObjectIndexStart() && gazeSphereIndex < GazeSphereSoundObjectIndexEnd();
}

void ObjectStatic::RenderCallback(float elapsedTime)
{
	//sound object sphere local rotation
	float rotateAmount = elapsedTime * (360.0f / 5.0f);   // Rotating at 5 sec/rotation
	for (int i = 0; i < SPATIAL_AUDIO_SOUND_OBJECT_NUM; i++)
	{
		mSoundObjectLocalToWorld[i] = rotate(mSoundObjectLocalToWorld[i], glm::radians(rotateAmount), glm::vec3(1.0f, 0.0f, 0.0f));
	}


	//gaze box processing
	int gazeSphereActivatedIndex;
	if (GetGazeSphereManager()->GetGazeSphereActivatedThisUpdateIndex(&gazeSphereActivatedIndex))
	{
		if(	GazeSphereIndexIsSoundObject(gazeSphereActivatedIndex) &&
			!IsPaused()/*don't (de)activate any sound objects while in paused mode*/)
		{ 
			const int gazeSphereArrayIndex = gazeSphereActivatedIndex - SpatialAudioCommon::kGazeBoxNum;
			Q3DSound& sound = mSoundObject[gazeSphereArrayIndex];
			if (!Q3DAudioIsSoundPlaying(GetSpatialAudio(), sound.mId))
			{
				Q3DAudioObjectCreate(GetSpatialAudio(), &sound);
				Q3DAudioSetObjectPosition(GetSpatialAudio(), sound.mId, mSoundObjectPosition[gazeSphereArrayIndex], gLogObjectTransform);
				Q3DAudioSoundPlay(GetSpatialAudio(), sound.mId, gLooping);

				if (gRolloffBugWorkaround)
				{
					//need to do this here since rolloff can't be consistently set immediately after creation
					Q3DAudioObjectSetRolloff(
						GetSpatialAudio(),
						sound.mId,
						sound.mRolloffType,
						sound.mRolloffMinDistance,
						sound.mRolloffMaxDistance);
				}
			}
			else
			{
				Q3DAudioSoundStopAndInvalidateHandle(GetSpatialAudio(), sound.mId);
			}
		}
		else if (gazeSphereActivatedIndex == kGazeBoxPause)
		{
			const int soundObjectsToPauseNum = SPATIAL_AUDIO_SOUND_OBJECT_NUM;
			if (IsPaused())
			{
				for (int i = 0; i < soundObjectsToPauseNum; ++i)
				{
					Q3DAudioResumeSound(GetSpatialAudio(), mSoundObject[i].mId);
				}
			}
			else
			{
				for (int i = 0; i < soundObjectsToPauseNum; ++i)
				{
					Q3DAudioPauseSound(GetSpatialAudio(), mSoundObject[i].mId);
				}
			}
			PausedInvertAndReflectInGazeBox(mGazeBoxActive);
		}
		else if (gazeSphereActivatedIndex == kGazeBoxLoop)
		{
			gLooping = !gLooping;
			mGazeBoxActive[kGazeBoxLoop] = gLooping;
		}
		else if (ConfigurationIndexIsReverb(gazeSphereActivatedIndex))
		{
			SetReverbAndReflectInBoolArray(mGazeBoxActive, gazeSphereActivatedIndex);
		}
		else if(ConfigurationIndexIsRolloff(gazeSphereActivatedIndex))
		{
			q3d_audio_distance_rolloff_type type;
			float minDistance;
			float maxDistance;
			GetRolloffSettings(&type, &minDistance, &maxDistance, gazeSphereActivatedIndex);

			for (int i = 0; i < SPATIAL_AUDIO_SOUND_OBJECT_NUM; ++i)
			{
				Q3DSound& sound = mSoundObject[i];
				sound.mRolloffType = (q3d_audio_distance_rolloff_type)type;
				sound.mRolloffMinDistance = minDistance;
				sound.mRolloffMaxDistance = maxDistance;

				Q3DAudioObjectSetRolloff(
					GetSpatialAudio(), 
					sound.mId, 
					sound.mRolloffType, 
					sound.mRolloffMinDistance, 
					sound.mRolloffMaxDistance);
			}

			SetBoolsToFalse(&mGazeBoxActive[kGazeBoxRolloffA], kGazeBoxRolloffNum);
			mGazeBoxActive[gazeSphereActivatedIndex] = true;
		}
	}
}

void ObjectStatic::RenderModelCallback(int whichEye, float elapsedTime)
{
	GazeBoxRender(
		mpGazeBoxModel,
		&mGazeBoxShader,
		mGazeBoxTexture,
        mGazeBoxTextureSampler,
		GetGazeSphereManager()->GetGazeSpheres(),
		kGazeBoxNum,
		mGazeBoxActive,
		mGazeBoxLastHsvSaturation,
		mGazeBoxLastHsvValue,
		elapsedTime,
		whichEye,
		GetProjMtx(),
		GetViewMtx());

	//floor model render
	glm::mat4 floorModelTranslate = glm::translate(glm::mat4(1.0f), glm::vec3(0.0, -200.0f, 0.0f));
	glm::mat4 floorRotateForward = glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f));
	glm::mat4 floorRotateUp = glm::rotate(glm::mat4(1.0f), glm::radians(135.0f), glm::vec3(0.0f, 1.0f, 0.0f));

	glm::mat4 floorScale = glm::mat4(1.0f);
	floorScale[3][3] = 1.0f;

	glm::mat4 floorModelLocalToWorld = floorModelTranslate*floorScale*floorRotateUp*floorRotateForward;

	mFloorShader.Bind();
	mFloorShader.SetUniformMat4("projMtx", GetProjMtx());
	mFloorShader.SetUniformMat4("viewMtx", GetViewMtx()[whichEye]);
	mFloorShader.SetUniformMat4("mdlMtx", floorModelLocalToWorld);

	mpFloorModel->Submit();


	//sky model render
	glm::mat4 skyRotateRight = glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f));
	glm::mat4 skyRotateUp = glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(0.0f, 1.0f, 0.0f));
	glm::mat4 skyModelLocalToWorld = skyRotateUp*skyRotateRight;

	mSkyShader.Bind();
	mSkyShader.SetUniformMat4("projMtx", GetProjMtx());
	mSkyShader.SetUniformMat4("viewMtx", GetViewMtx()[whichEye]);
	mSkyShader.SetUniformMat4("mdlMtx", skyModelLocalToWorld);
	mpSkyModel->Submit();


	
	//sound object visualization render
	mSphereShader.Bind();

	const GazeSphere*const gazeSpheres = GetGazeSphereManager()->GetGazeSpheres();
	const int gazeSphereSoundObjectIndexStart = GazeSphereSoundObjectIndexStart();
	const int gazeSphereSoundObjectIndexEnd = GazeSphereSoundObjectIndexEnd();
	for(int gazeSphereIndex = gazeSphereSoundObjectIndexStart; gazeSphereIndex < gazeSphereSoundObjectIndexEnd; ++gazeSphereIndex)
	{
		const int soundObjectIndex = gazeSphereIndex - gazeSphereSoundObjectIndexStart;

		mSphereShader.SetUniformMat4("projMtx", GetProjMtx());
		mSphereShader.SetUniformMat4("viewMtx", GetViewMtx()[whichEye]);
		mSphereShader.SetUniformMat4("mdlMtx", mSoundObjectLocalToWorld[soundObjectIndex]);

		const GazeSphere& gazeSphere = gazeSpheres[gazeSphereIndex];
		vec3 sphereRGB = mSoundObjectColorDefault[soundObjectIndex];

		Q3DSound& sound = mSoundObject[soundObjectIndex];
		const bool sphereActive = Q3DAudioIsSoundPlaying(GetSpatialAudio(), sound.mId);
		if (!IsPaused() && !sphereActive)
		{
			sound.mId = 0;//sounds that aren't playing clear their ids so each id remains unique when new ids are generated
		}
		const vec3 rgbNew = GazeSphereColorProcess(
			&mSoundObjectSphereLastHsvSaturation[soundObjectIndex],
			&mSoundObjectSphereLastHsvValue[soundObjectIndex],
			gazeSphere,
			sphereRGB,
			sphereActive,
			elapsedTime);
		vec4 rgbNew4(rgbNew.x, rgbNew.y, rgbNew.z, 1.f);
		mSphereShader.SetUniformVec4("modelColor", rgbNew4);

		mpSphereModel->Submit();
	}
}

namespace Svr
{
    Svr::SvrApplication* CreateApplication()
    {
        return new ObjectStatic();
    }
}

