/******************************************************************
  
  Module:  levelobj.cpp
  
  Author: Sean Craig
  
  Copyright 2005 Sony Online Entertainment.  All rights reserved.
  
*******************************************************************/

//-------------------------------------------------------- Includes
#include "levelobj.h"
#include "titan.h"
#include "cameracontroller.h"
#include "ai/aiblackboard.h"
#include "registry.h"
#include "script_pawn.h"
#include "SySoundDevice.h"
#include "areaeffect.h"
#include "quest.h"
#include "gameerror.h"
#include "database.h"

//---------------------------------------------- Class Declarations
//----------------------------------------- Functions Declarations
//------------------------------------ Member Functions Definitions

//------------------------------------ cLevelObject

cLevelObject::cLevelObject() :
  mID(LEVEL_OBJECT_GAME_ID),
  mRemote(false),
  mLevelNameHashID(ID_NONE),
  mpTitan(NULL),
  mpLoader(0),
  mpAIBlackboard(NULL),
  mpAreaEffectSys(NULL),
  mpQuestSys(NULL),
  mbScriptCamMode(false),
  mScriptCamSetting(0),
  mScriptCamSourceID(ID_NONE),
  mScriptCamTargetID(ID_NONE),
  mScriptCamSourceYOffset(0.0f),
  mScriptCamTargetYOffset(0.0f),
  mScriptCamViewAngle(0.0f),
  mNumGameObjects(0),
  mbEnableInput(true),
  mMusicID(-1)
{
  InitPropObject( LEVELOBJECT_CLASSID );
}

bool
cLevelObject::IsLocal() 
{
  return !mRemote;
}

cLevelObject::~cLevelObject()
{
  ClearScriptData();

  delete mpAreaEffectSys;
  mpAreaEffectSys = NULL;

  delete mpAIBlackboard;
  mpAIBlackboard = NULL;

  delete mpLoader;
  mpLoader = NULL;

  delete mpQuestSys;
  mpQuestSys = NULL;
}

int           
cLevelObject::InitPropClass()
{
  SyPropClassID ClassID = LEVELOBJECT_CLASSID;

/* Add the class */

  AddClass( ClassID, 
           "cLevelObject", 
            Creator, 
            ClassID, 
            0 ); 
  AddStringProperty(ClassID,PropId_ModelName,SyMemberOffset(cLevelObject,mModelName),"mModelName");
  AddStringProperty(ClassID,PropId_CameraName,SyMemberOffset(cLevelObject,mCameraName),"mCameraName");
  AddSubObjectPtrProperty(ClassID,PropId_AIBlackboard,SyMemberOffset(cLevelObject,mpAIBlackboard),"mpAIBlackboard");
  AddSubObjectPtrProperty(ClassID,PropId_AreaEffectSys,SyMemberOffset(cLevelObject,mpAreaEffectSys),"mpAreaEffectSys");
  AddStringProperty(ClassID,PropId_LevelName,SyMemberOffset(cLevelObject,mLevelName),"mLevelName");
  AddStringProperty(ClassID,PropId_MusicName,SyMemberOffset(cLevelObject,mMusicName),"mMusicName");
  AddSubObjectPtrProperty(ClassID,PropId_QuestSys,SyMemberOffset(cLevelObject,mpQuestSys),"mpQuestSys");

  AddBoolProperty(ClassID,PropId_ScriptCamMode,SyMemberOffset(cLevelObject,mbScriptCamMode),"mbScriptCamMode");
  AddInt32Property(ClassID,PropId_ScriptCamSetting,SyMemberOffset(cLevelObject,mScriptCamSetting),"mScriptCamSetting");
  AddInt32Property(ClassID,PropId_ScriptCamSourceID,SyMemberOffset(cLevelObject,mScriptCamSourceID),"mScriptCamSourceID");
  AddInt32Property(ClassID,PropId_ScriptCamTargetID,SyMemberOffset(cLevelObject,mScriptCamTargetID),"mScriptCamTargetID");
  AddFloat32Property(ClassID,PropId_ScriptCamSourceYOffset,SyMemberOffset(cLevelObject,mScriptCamSourceYOffset),"mScriptCamSourceYOffset");
  AddFloat32Property(ClassID,PropId_ScriptCamTargetYOffset,SyMemberOffset(cLevelObject,mScriptCamTargetYOffset),"mScriptCamTargetYOffset");
  AddFloat32Property(ClassID,PropId_ScriptCamViewAngle,SyMemberOffset(cLevelObject,mScriptCamViewAngle),"mScriptCamViewAngle");
  AddInt32Property(ClassID,PropId_NumGameObjects,SyMemberOffset(cLevelObject,mNumGameObjects),"mNumGameObjects");

  AddBoolProperty(ClassID,PropId_EnableInput,SyMemberOffset(cLevelObject,mbEnableInput),"mbEnableInput");

  AddSubObjectPtrVectorProperty<cScriptInstanceDataProxy>(ClassID,PropId_ScriptDataProxies,SyMemberOffset(cLevelObject,mScriptDataProxies),"mScriptDataProxies");

  return 0;
}

SyPropObject* 
cLevelObject::Creator()
{
  SyPropObject *pObject;

  pObject = SyNew cLevelObject();
  if (pObject == NULL)
  {
    SyAssert(0);
    return(NULL);
  }

  return(pObject);
}

void cLevelObject::SetEnableInput(bool bEnableInput)
{
  if (!IsRemote())
  {
    mbEnableInput = bEnableInput;
  }
}

void cLevelObject::SetNumGameObjects(int num)
{
  if (!IsRemote())
  {
    mNumGameObjects = num;
  }
}

cTreasureSet* cLevelObject::GetDefaultTreasureSet() const
{
  return mpTitan->GetDatabaseSys()->GetDefaultTreasureSetForLevel(mLevelNameHashID);
}

void  
cLevelObject::SetCameraName(const char *name)
{
  mCameraName.Init(name);
}

void  
cLevelObject::SetModelName(const char *name)
{
  mModelName.Clear();
  // pull out all but extension
  const char *end_name = &name[strlen(name)-1];
  while (end_name != name && *(end_name-1) != '/') 
  {
    --end_name;
  }
  // change path
  mModelName.Init("game_assets/art/levels/");
  mModelName.Concat(end_name);
}

void  
cLevelObject::SetLevelName(const char *name)
{
  mLevelName.Init(name);
  UpdateLevelNameHashID();
}

void
cLevelObject::SetRemote(bool bRemote)
{
  mRemote = bRemote;

  if (mpAIBlackboard)
  {
    mpAIBlackboard->SetRemote(bRemote);
  }
}

//#define STREAMING

void ProgressCallback( void* pData );

void
cLevelObject::Init(Titan *titan)
{
  mpTitan = titan;
  SyESFParse& Parser = titan->GetTitanUI()->GetESFParser();
  SyScene *scene = titan->GetScene();

  cCameraController *pCameraController = titan->GetCameraController();
  pCameraController->LoadCameraFile(GetCameraName());

  if (IsRemote() && mbScriptCamMode)
  {
    if (mScriptCamSetting == CAMERA_SCRIPT_LOOKAT1 || 
        mScriptCamSetting == CAMERA_SCRIPT_LOOKAT2 || 
        mScriptCamSetting == CAMERA_SCRIPT_LOOKAT3)
    {
      pCameraController->StartScriptCamLookAt((eCameraSetting)mScriptCamSetting,
                                              mScriptCamSourceID, 
                                              mScriptCamTargetID,
                                              mScriptCamSourceYOffset,
                                              mScriptCamTargetYOffset);
    }

    pCameraController->ScriptCut((eCameraSetting)mScriptCamSetting);

    if (!mbEnableInput)
    {
      mpTitan->StartCutScene(mbEnableInput);
    }
  }

  UpdateLevelNameHashID();

  // hack to change path name

  static const int BUFFER_SIZE = 512;
  char buffer[BUFFER_SIZE];
  strncpy(buffer, GetModelName(), BUFFER_SIZE-1);
  buffer[BUFFER_SIZE-1] = 0;
  SetModelName(buffer);

#ifdef STREAMING
  if ( !(mpLoader = new SyLoader()) )
  {
    SyAssert(!"new SyLoader failed");
  }
  mpLoader->SetProgressCallback( ProgressCallback, titan->GetTitanUI() );

  if ( mpLoader->Init( 
    64 * 1024 * 1024, // buffer size
    1 // number of files
    ) < 0 )
  {
    SyAssert(!"SyLoader::Init failed");
  }

   titan->LoadAssetWorld (Parser, GetModelName(), true/*streaming*/);

  if ( mpLoader->Open( 0, GetModelName() ) < 0 )
  {
    SyAssert(!"SyLoader::Open failed");
  }

  SyVect3 startLocation = pCameraController->GetCamera()->GetLocation();
  // use player start location

  cGameObject *start_marker = titan->GetRegistry()->Fetch("playerSpawn");

  if (start_marker != NULL)
  {
    startLocation = start_marker->GetLocation();
  }

  if ( scene->StreamFillCache( startLocation, *mpLoader, 0 ) < 0 )
  {
    SyAssert(!"SyScene::StreamFillCache failed");
  }
  //SyDebug( "Stream %.2f,%.2f,%.2f\n", startLocation.X, startLocation.Y, startLocation.Z );
#else
  titan->LoadAssetWorld (Parser, GetModelName(), false/*streaming*/);
#endif // STREAMING

  titan->LoadAsset (Parser, "programming/fx/fx.esf");
  scene->SetResources();             // have scene located resource that were in fx.esf

  /* this creates water tiles from water data */
  SyWaterSystem * pWaterSystem = scene->GetWaterSystem();
  if (pWaterSystem) 
  {
    pWaterSystem->MakeWaterTiles(); 
  }


  // load level specific FX file, if it exists
  char fxFileName[BUFFER_SIZE+30];

  strncpy(buffer, GetModelName(), BUFFER_SIZE-1);
  buffer[BUFFER_SIZE-1] = 0;

  // pull out all of the path except for filename itself
  const char *endModelName = &buffer[strlen(buffer)-1];
  while (endModelName != buffer && *(endModelName-1) != '/') 
  {
    --endModelName;
  }

  sprintf(fxFileName, "game_assets/art/fx/levels/fx_%s", endModelName);


  titan->LoadAsset (Parser, "game_assets/art/fx/character/fx_character.esf");
  titan->LoadAsset (Parser, "game_assets/art/fx/character/fx_npc.esf");
  titan->LoadAsset (Parser, "game_assets/art/fx/character/fx_player.esf");
  titan->LoadAsset (Parser, "game_assets/art/fx/global/fx_global.esf");
  titan->LoadAsset (Parser, fxFileName, true);
  scene->SetMultiPass(mpTitan->GetMultiPass());

  //
  // Init AI blackboard
  //
  if (NULL == mpAIBlackboard)
  {
    mpAIBlackboard = new cAIBlackboard();
  }
  SyAssertf(mpAIBlackboard != NULL, "Blackboard should have been created!");

  if (mpAIBlackboard)
  {
    mpAIBlackboard->SetRemote(IsRemote());
    mpAIBlackboard->Init(this);
  }

  //
  // Init Area of Effect System
  //
  if (NULL == mpAreaEffectSys)
  {
    mpAreaEffectSys = new cAreaEffectSys();
  }
  else
  {
    mpAreaEffectSys->Clear(); 
  }

  SyAssertf(mpAreaEffectSys != NULL, "AreaEffectSys should have been created!");

  //
  // Init Quest System
  //
  if (NULL == mpQuestSys)
  {
    mpQuestSys = new cQuestSys();
  }

  if (mMusicName.AsChar()[0] != '\0')
  {
    PlayMusic(mMusicName.AsChar(), false);
  }

  _LoadHighlightMaterial();

#ifdef STREAMING
  mpLoader->SetProgressCallback( NULL, NULL );
#endif

  mpTitan->GetScriptSys()->LoadLevelScript(mLevelName.AsChar(), mScriptDataProxies.Size() > 0);

  mpTitan->GetScriptSys()->PushScriptData(mScriptDataProxies);
  ClearScriptData();
}


void
cLevelObject::SaveScriptData()
{
  ClearScriptData();
  mpTitan->GetScriptSys()->PullScriptData(mScriptDataProxies);
}

void
cLevelObject::ClearScriptData()
{
  for (int ii=mScriptDataProxies.Begin(); ii!=mScriptDataProxies.End(); ii=mScriptDataProxies.Next(ii))
  {
    delete mScriptDataProxies(ii);
  }

  mScriptDataProxies.Clear();
}


void
cLevelObject::Exit()
{
  if (mpAreaEffectSys)
  {
    mpAreaEffectSys->Clear();
  }
}

void
cLevelObject::Update(float time)
{
  mpAreaEffectSys->Update(time);

#ifdef STREAMING
  SyScene *scene = mpTitan->GetScene();
  SyVect3 cameraLocation = mpTitan->GetCameraController()->GetCamera()->GetLocation();
  //SyDebug( "Stream %.2f,%.2f,%.2f\n", cameraLocation.X, cameraLocation.Y, cameraLocation.Z );
  if ( scene->Stream( cameraLocation, *mpLoader, 0 ) < 0 )
  {
    SyAssert(!"SyScene::Stream failed");
  }
#endif // STREAMING
}

void
cLevelObject::NetworkReceiveBroadcast(const char *state, int maxlen)
{
  cNetPacket::eNetPacketType type;

  SyPack::Unpack8u(state, &type);      // note: pack-format must match cNetPacket::Pack

  switch (type)
  {
    case cNetPacket::NET_SCRIPT_FUNC:
    case cNetPacket::NET_GLOBAL_SCRIPT_STATE:
      mpTitan->GetScriptSys()->NetworkReceiveBroadcast(state,maxlen);
      break;

    case cNetPacket::NET_TRANSITION_LEVEL:
      {
        cNetTransitionLevelPacket level_packet;
        level_packet.UnpackBuffer(state, maxlen);
        mpTitan->SetTransitionLevel(level_packet.mLevelName,level_packet.mSpawnPointName);
        // have to do this right now, before we get more incoming packets creating/deleting objects
        mpTitan->TransitionLevel();
      }
      break;
    default:
      if (mpAIBlackboard)
      {
        mpAIBlackboard->NetworkReceiveBroadcast(state, maxlen);
      }
      break;
  }
}

void
cLevelObject::NetworkReceiveMessage(const char *state, int maxlen)
{
  cNetPacket::eNetPacketType type;

  SyPack::Unpack8u(state, &type);      // note: pack-format must match cNetPacket::Pack

  switch (type)
  {
    case cNetPacket::NET_SCRIPT_FUNC:
      mpTitan->GetScriptSys()->NetworkReceiveMessage(state,maxlen);
      break;

    case cNetPacket::NET_GLOBAL_SCRIPT_STATE:
      SyAssertf(false, "We're getting told by the remote to transfer global script state!");
      break;

    default:
      if (mpAIBlackboard)
      {
        mpAIBlackboard->NetworkReceiveMessage(state, maxlen);
      }
      break;
  }
}


int32             
cLevelObject::PlayMusic(const char *filename, bool bLooping)  // returns resource handle.  Registers& loads resource
{
  StopMusic();

  SyScene *scene = mpTitan->GetScene();

  // stub out if we don't have a sound device
  if (scene->GetSoundDev() == NULL)
  {
    return -1;
  }
  mMusicID = scene->GetSoundDev()->PlayStream(filename, 1, SySoundDevice::kSoundClassMusic, bLooping);

  return mMusicID;
};

void              
cLevelObject::StopMusic()
{

  SyScene *scene = mpTitan->GetScene();
  // stub out if we don't have a sound device
  if (scene->GetSoundDev() == NULL)
  {
    return;
  }
  if (mMusicID != -1)
  {
    scene->GetSoundDev()->Stop(mMusicID);
    mMusicID = -1;
  }
};


void              
cLevelObject::SetQuest(tQuestID id,int32 value)
{
  if (mpQuestSys!=NULL)
  {
    mpQuestSys->set(id,value);
  };
};

int32             
cLevelObject::GetQuest(tQuestID id)
{
  if (mpQuestSys!=NULL)
  {
    return mpQuestSys->get(id);
  };
  return 0;
};

void 
cLevelObject::_LoadHighlightMaterial()
{
  // Look up texture
  TiUII * ui = mpTitan->GetTitanUI()->GetUII();
  if (ui == NULL)
  {
    return;
  }

  int32 handle=0;
  SyVect2I offset,size;

  if (ui->GetBitmapInfo("highlight.tga",handle,offset,size) < 0)
  {
    SyAssertf(0,"Unable to find highlight texture");
    return;
  }
  SyRaster *pRaster = mpTitan->GetScene()->GetRasterDev();
  SySurface *surf = pRaster->Surface(handle);
  mHighlightMinUV.X = offset.X / ((float)surf->Width()+1.0f);
  mHighlightMinUV.Y = offset.Y / ((float)surf->Height()+1.0f);
  mHighlightMaxUV.X = (offset.X+size.X) / ((float)surf->Width()+1.0f);
  mHighlightMaxUV.Y = (offset.Y+size.Y) / ((float)surf->Height()+1.0f);

  mHighlightHandle = pRaster->CreateMaterial( SYMATERIALTYPE_SIMPLE, 0 );
  if(mHighlightHandle < 0)
  {
    SyAssert(0);
    return;
  }

  SySimpleMaterial *pMaterial = (SySimpleMaterial*)(pRaster->Material( mHighlightHandle ));
  pMaterial->SetBlendMode( SYALPHABLENDMODE_AVG );
  pMaterial->SetBlendAlpha( 1.0f );
  pMaterial->SetTexture(
              handle, 
              (*pRaster), 
              (*mpTitan->GetScene()->GetDictionary()) );
}

void                  
cLevelObject::GetHighlightMaterial(int32 &handle,SyVect2 &minUV,SyVect2 &maxUV)
{
  handle = mHighlightHandle;
  minUV = mHighlightMinUV;
  maxUV = mHighlightMaxUV;
}

void cLevelObject::DisableScriptCam()
{
  mbScriptCamMode = false;
}

void cLevelObject::EnableScriptCam(int               scriptCamSetting,
                                   tGameObjectID     scriptCamSourceID,
                                   tGameObjectID     scriptCamTargetID,
                                   float             scriptCamSourceYOffset,
                                   float             scriptCamTargetYOffset,
                                   float             scriptCamViewAngle)
{
  mbScriptCamMode = true;
  mScriptCamSetting = scriptCamSetting;
  mScriptCamSourceID = scriptCamSourceID;
  mScriptCamTargetID = scriptCamTargetID;
  mScriptCamSourceYOffset = scriptCamSourceYOffset;
  mScriptCamTargetYOffset = scriptCamTargetYOffset;
  mScriptCamViewAngle = scriptCamViewAngle;
}

void cLevelObject::UpdateLevelNameHashID()
{
  // strip off the suffix and store the hash ID...
  SyPathname path(mLevelName.AsChar());
  mLevelNameHashID = SyHashResourceID(path.Base().AsChar());
}

//////////////////////////////////////////////////////////////////////////////////// global functions

void 
RegPropClasses_LevelObj()
{
  cLevelObject::InitPropClass();
  cAIBlackboard::InitPropClass();
  cAreaEffectSys::InitPropClass();
  cQuestSys::InitPropClass();
}

// EOF
