/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/

#include "hsTypes.h"
#include "hsGEnviron.h"
#include "../plSurface/hsGLayer.h"
#include "../plSurface/hsGMaterial.h"
//#include "hsG3DDevice.h"
//#include "plCamera.h"
#include "hsFogControl.h"
#include "../plGRenderProcs/hsGRenderProcs.h"
#if 0 // SCENENODE_DEFER
#include "hsScene.h"
#endif // SCENENODE_DEFER
#include "../plResMgr/hsResMgr.h"
#include "hsGStatGather3.h"
#include "../plResMgr/plKey.h"
#include "hsTimer.h"
#include "../plResMgr/plRefMsg.h"

const UInt16 hsGEnvironment::kSaveMagicNumber = 0x1f7a;
const UInt16 hsGEnvironment::kSaveVersion = 2;

hsScalar hsGEnvironment::fYonScale = 1.f;

hsGEnvironment::hsGEnvironment() 
    : fFlags(0), 
    fValidScale(1.f),
    fMap(nil), 
    fPos(0,0,0), 
    fRadius(0), 
    fFogDistance(0),
    fFogDepth(0),
    fCurrentDepth(0),
    fFogDensity(0),
    fDevCache(nil),
    fYon(0),
    fFogControl(nil)
{
    fFogColor.SetValue(hsColorRGBA().Set(0, 0, 0, hsScalar1));
    SetFogColor(fFogColor.GetValue());
    *fMapName = 0;
}

hsGEnvironment::~hsGEnvironment()
{ 
    hsRefCnt_SafeUnRef(fMap); 
    hsRefCnt_SafeUnRef(fDevCache);

    hsRefCnt_SafeUnRef(fFogControl);

    Int32 i;
    for (i=0; i<fFogStateStack.GetCount(); i++)
        delete fFogStateStack[i];

    for( i = 0; i < GetNumRenderProcs(); i++ )
        hsRefCnt_SafeUnRef(GetRenderProc(i));

}


#if 0 // SCENENODE_DEFER
hsBool32 hsGEnvironment::AddNode(hsSceneNode *node)
{
    return AddNodeKey(node->GetKey());
}
#endif // SCENENODE_DEFER

hsBool32 hsGEnvironment::AddNodeKey(plKey *key)
{
    Int32 i;
    for (i = 0; i < GetNumNodes(); i++)
    {
        if (GetNodeKey(i) == key) // nothing to do
            return false;
    }
    fNodeKeys.Append(key);

    hsSceneNode *node = (hsSceneNode *)(key->GetObjectPtr());
#if 0 // SCENENODE_DEFER
    if (node)
    {
        if (!(GetFlags() & hsGEnvironment::kFarOut))
        {
            node->SetEnvironment(this);
        }
        else
        {
            node->SetFarEnvironment(this);
        }
    }
#endif // SCENENODE_DEFER

    return true;
}

hsSceneNode* hsGEnvironment::GetNode(Int32 i) 
{ 
    return (hsSceneNode*)(fNodeKeys[i]->GetObjectPtr()); 
}

void hsGEnvironment::FogState::ValidateEnv(hsGEnvironment* env)
{
    char* msg = "hsGEnvironment not in reset state.";
    if( fFlags & kYonSet )
    {
        hsAssert(env->GetFlags() & hsGEnvironment::kYonSet, msg);
        hsAssert(fYon == env->YonState(), msg);
    }
    
    if( fFlags & kDistanceSet )
    {
        hsAssert(env->GetFlags() & hsGEnvironment::kFogDistanceSet, msg);
        hsAssert(fDistance == env->FogDistanceState(), msg);
    }

    if( fFlags & kDepthSet )
    {
        hsAssert(env->GetFlags() & hsGEnvironment::kFogDepthSet, msg);
        hsAssert(fDepth == env->FogDepthState(), msg);
    }

    if( fFlags & kDensitySet )
    {
        hsAssert(env->GetFlags() & hsGEnvironment::kFogDensitySet, msg);
        hsAssert(fDensity == env->FogDensityState(), msg);
    }

    if( fFlags & kColorSet )
    {
        hsAssert(env->GetFlags() & hsGEnvironment::kFogColorSet, msg);
        hsAssert(fColor == env->FogColorState(), msg);
    }

    if( fFlags & kClearSet )
    {
        hsAssert(env->GetFlags() & hsGEnvironment::kClearColorSet, msg);
        hsAssert(fClear == env->ClearColorState(), msg);
    }

    UInt32 envFogType = env->GetFlags() & hsGEnvironment::kFogTypeMask;
    switch( fFlags & kTypeMask )
    {
    case kLinear:
        hsAssert(envFogType == hsGEnvironment::kFogLinear, msg);
        break;
    case kExp:
        hsAssert(envFogType == hsGEnvironment::kFogExp, msg);
        break;
    case kExp2:
        hsAssert(envFogType == hsGEnvironment::kFogExp2, msg);
        break;
    default:
        hsAssert(false, msg);
        break;
    }
}

void hsGEnvironment::FogState::SetFromEnv(hsGEnvironment* env)
{
    if( env->GetFlags() & hsGEnvironment::kYonSet )
    {
        fYon = env->YonState();
        fFlags |= kYonSet;
    }
    if( env->GetFlags() & hsGEnvironment::kFogDistanceSet )
    {
        fDistance = env->FogDistanceState();
        fFlags |= kDistanceSet;
    }
    if( env->GetFlags() & hsGEnvironment::kFogDepthSet )
    {
        fDepth = env->FogDepthState();
        fFlags |= kDepthSet;
    }
    if( env->GetFlags() & hsGEnvironment::kFogDensitySet )
    {
        fDensity = env->FogDensityState();
        fFlags |= kDensitySet;
    }
    if( env->GetFlags() & hsGEnvironment::kFogColorSet )
    {
        fColor = env->FogColorState();
        fFlags |= kColorSet;
    }
    if( env->GetFlags() & hsGEnvironment::kClearColorSet )
    {
        fClear = env->ClearColorState();
        fFlags |= kClearSet;
    }

    fFlags &= ~kTypeMask;
    switch( env->GetFlags() & hsGEnvironment::kFogTypeMask )
    {
    case hsGEnvironment::kFogLinear:
        fFlags |= kLinear;
        break;
    case hsGEnvironment::kFogExp:
        fFlags |= kExp;
        break;
    case hsGEnvironment::kFogExp2:
        fFlags |= kExp2;
        break;
    default:
        hsAssert(false, "Fog type should at least default at load");
        break;
    }
}

void hsGEnvironment::FogState::SetToEnv(hsGEnvironment* env)
{
    if( fFlags & kYonSet )
        env->SetYon(fYon);
    else
        env->UnSetYon();
    
    if( fFlags & kDistanceSet )
        env->SetFogDistance(fDistance);
    else
        env->UnSetFogDistance();

    if( fFlags & kDepthSet )
    {
        env->SetFogDepth(fDepth);
        env->SetCurrentDepth(fDepth.GetValue());
    }
    else
        env->UnSetFogDepth();

    if( fFlags & kDensitySet )
        env->SetFogDensity(fDensity);
    else
        env->UnSetFogDensity();

    if( fFlags & kColorSet )
        env->SetFogColor(fColor);
    else
        env->UnSetFogColor();

    if( fFlags & kClearSet )
        env->SetClearColor(fClear);
    else
        env->UnSetClearColor();

    switch( fFlags & kTypeMask )
    {
    case kLinear:
        env->SetFogType(hsGEnvironment::kFogLinear);
        break;
    case kExp:
        env->SetFogType(hsGEnvironment::kFogExp);
        break;
    case kExp2:
        env->SetFogType(hsGEnvironment::kFogExp2);
        break;
    default:
        hsAssert(false, "Setting lack of fog type");
        env->SetFogType(0);
        break;
    }
}

const UInt16 hsGEnvironment::FogState::kSaveMagicNumber = 0x7385;
const UInt16 hsGEnvironment::FogState::kSaveVersion = 1;

void hsGEnvironment::FogState::Save(hsStream *stream, hsResMgr* mgr)
{
    stream->WriteSwap16(kSaveMagicNumber);
    stream->WriteSwap16(kSaveVersion);

    hsScalar currSecs = hsTimer::GetSysSeconds();
    stream->WriteSwap32(fFlags);
    fColor.Write(stream, currSecs);
    fClear.Write(stream, currSecs);
    fDistance.WriteScalar(stream, currSecs);
    fDensity.WriteScalar(stream, currSecs);
    fDepth.WriteScalar(stream, currSecs);
    fYon.WriteScalar(stream, currSecs);
}

void hsGEnvironment::FogState::Load(hsStream *stream, hsResMgr* mgr)
{
    UInt16 magic = stream->ReadSwap16();
    hsAssert(magic == kSaveMagicNumber, "Bad magic number in hsGEnvironment::FogState on load.");

    UInt16 version = stream->ReadSwap16();
    hsAssert(version == kSaveVersion, "Bad version in hsGEnvironment::FogState on load.");

    hsScalar currSecs = hsTimer::GetSysSeconds();
    fFlags = stream->ReadSwap32();
    fColor.Read(stream, currSecs);
    fClear.Read(stream, currSecs);
    fDistance.ReadScalar(stream, currSecs);
    fDensity.ReadScalar(stream, currSecs);
    fDepth.ReadScalar(stream, currSecs);
    fYon.ReadScalar(stream, currSecs);
}


void hsGEnvironment::SetResetState()
{
    fResetState.SetFromEnv(this);
}

void hsGEnvironment::Reset()
{
    fResetState.SetToEnv(this);

    Int32 i;
    for (i=0; i<fFogStateStack.GetCount(); i++)
        delete fFogStateStack[i];
    fFogStateStack.Reset();
}

void hsGEnvironment::ValidateInResetState()
{
    fResetState.ValidateEnv(this);

    hsAssert(fFogStateStack.GetCount() == 0, "Bad fog state stack.");
}

void hsGEnvironment::Save(hsStream *stream, hsResMgr* mgr)
{
    hsKeyedObject::Save(stream, mgr);

    stream->WriteSwap16(kSaveMagicNumber);
    stream->WriteSwap16(kSaveVersion);

    stream->WriteSwap32(fFlags);

    if( fFlags & hsGEnvironment::kCenterSet )
        fPos.Write(stream);

    if( fFlags & hsGEnvironment::kRadiusSet )
        stream->WriteSwapScalar(fRadius);

    FogState tempState;
    tempState.SetFromEnv(this);

    tempState.Save(stream, mgr);
    fResetState.Save(stream, mgr);

    UInt32 numFogStates = fFogStateStack.GetCount();
    stream->WriteSwap32(numFogStates);
    for (UInt32 i = 0; i < numFogStates; i++)
    {
        fFogStateStack[i]->Save(stream, mgr);
    }
}

void hsGEnvironment::Load(hsStream *stream, hsResMgr* mgr)
{
    // Clear old data
    UInt32 i;
    for (i=0; i<fFogStateStack.GetCount(); i++)
        delete fFogStateStack[i];
    fFogStateStack.Reset();

    // Load
    hsKeyedObject::Load(stream, mgr);

    UInt16 magic = stream->ReadSwap16();
    hsAssert(magic == kSaveMagicNumber, "Bad magic number in hsGEnvironment on load.");

    UInt16 version = stream->ReadSwap16();
    hsAssert(version == kSaveVersion, "Bad version in hsGEnvironment on load.");

    fFlags = stream->ReadSwap32();

    if( fFlags & hsGEnvironment::kCenterSet )
        fPos.Read(stream);
    
    if( fFlags & hsGEnvironment::kRadiusSet )
        fRadius = stream->ReadSwapScalar();
    
    FogState tempState;
    tempState.Load(stream, mgr);
    tempState.SetToEnv(this);
    fResetState.Load(stream, mgr);

    UInt32 numFogStates = stream->ReadSwap32();
    for (i = 0; i < numFogStates; i++)
    {
        FogState *newFogState = new FogState;
        newFogState->Load(stream, mgr);
        fFogStateStack.Append(newFogState);
    }
}

void hsGEnvironment::Update(hsScalar s, const hsPoint3& vPos)
{
    if( fFlags & kFogDepthSet )
        fFogDepth.Update(s);
    if( fFlags & kFogDensitySet )
        fFogDensity.Update(s);
    if( fFlags & kFogColorSet )
        fFogColor.Update(s);
    if( fFlags & kClearColorSet )
        fClearColor.Update(s);
    if( fFlags & kYonSet )
        fYon.Update(s);
    if( fFlags & kMapSet )
    {
        hsGStatGather3::UpdateMaterialBegin();
        fMap->Update(s);
        hsGStatGather3::UpdateMaterialEnd();
        
        if( fFlags & kClearColorAmbient )
            SetClearColor(fMap->GetLayer(0)->GetAmbientColor());

        if( fFlags & kFogColorAmbient )
            SetFogColor(fMap->GetLayer(0)->GetAmbientColor());
        else
        if( fFlags & kFogColorDiffuse )
            SetFogColor(fMap->GetLayer(0)->GetColor());
    }

    if( fFlags & kFogDistanceSet )
    {
        fFogDistance.Update(s);


        hsVector3 del(&vPos, &fPos);
        hsScalar dist = del.Magnitude();
        
        hsScalar yon = hsMaximum(dist, fFogDistance.GetValue());

        SetYon(yon);

        hsScalar depth = fFogDistance.GetValue();
        if( fFlags & kFogDepthSet )
            depth *= fFogDepth.GetValue();

        depth += yon - dist;
        depth /= yon;

        if( depth > hsScalar1 )
            depth = hsScalar1;

        fCurrentDepth = depth;
        fFlags |= kCurrentDepthSet;
    }
    else if( fFlags & kFogDepthSet )
    {
        fCurrentDepth = fFogDepth.GetValue();
        fFlags |= kCurrentDepthSet;
    }
    else
        fFlags &= ~kCurrentDepthSet;

    if( fValidScale != fYonScale )
    {
        if( fDevCache )
            fDevCache->Validate(false);
        fValidScale = fYonScale;
    }
}

void hsGEnvironment::SetTimedFogDistance(const hsScalar g, const hsScalar s) 
{ 
    if( fFlags & kFogDistanceSet )
    {
        fFogDistance.Update(hsTimer::GetSysSeconds());
        fFogDistance.SetGoal(g); 
        fFogDistance.SetDuration(s); 
        fFogDistance.StartClock(hsTimer::GetSysSeconds()); 
    }
    else
        SetFogDistance(g);
}

void hsGEnvironment::SetTimedFogDepth(const hsScalar g, const hsScalar s) 
{ 
    if( fFlags & kFogDepthSet )
    {
        fFogDepth.Update(hsTimer::GetSysSeconds());
        fFogDepth.SetGoal(g); 
        fFogDepth.SetDuration(s); 
        fFogDepth.StartClock(hsTimer::GetSysSeconds()); 
    }
    else
        SetFogDepth(g);
}

void hsGEnvironment::SetTimedFogDensity(const hsScalar g, const hsScalar s) 
{ 
    if( fFlags & kFogDensitySet )
    {
        fFogDensity.Update(hsTimer::GetSysSeconds());
        fFogDensity.SetGoal(g); 
        fFogDensity.SetDuration(s); 
        fFogDensity.StartClock(hsTimer::GetSysSeconds()); 
    }
    else
        SetFogDensity(g);
}

void hsGEnvironment::SetTimedFogColor(const hsColorRGBA& g, const hsScalar s) 
{ 
    if( fFlags & kFogColorSet )
    {
        fFogColor.Update(hsTimer::GetSysSeconds());
        fFogColor.SetGoal(g); 
        fFogColor.SetDuration(s); 
        fFogColor.StartClock(hsTimer::GetSysSeconds()); 
    }
    else
        SetFogColor(g);
}

void hsGEnvironment::SetTimedClearColor(const hsColorRGBA& g, const hsScalar s) 
{ 
    if( fFlags & kClearColorSet )
    {
        fClearColor.Update(hsTimer::GetSysSeconds());
        fClearColor.SetGoal(g); 
        fClearColor.SetDuration(s); 
        fClearColor.StartClock(hsTimer::GetSysSeconds()); 
    }
    else
        SetClearColor(g);
}

void hsGEnvironment::SetTimedYon(const hsScalar g, const hsScalar s) 
{ 
    if( fFlags & kYonSet )
    {
        fYon.Update(hsTimer::GetSysSeconds());
        fYon.SetGoal(g); 
        fYon.SetDuration(s);
        fYon.StartClock(hsTimer::GetSysSeconds());
    }
    else
        SetYon(g);
}

void hsGEnvironment::SetMapName(const char *n)
{
    if( n )
    {
        hsAssert(strlen(n) < 255, "Environment name overflow");
        fFlags |= kMapSet; 
        strcpy(fMapName, n);
    }
    else
    {
        fFlags &= ~kMapSet;
        *fMapName = 0;
    }
}

void hsGEnvironment::SetMap(hsGMaterial *m) 
{ 
    fFlags |= kMapSet; 
    hsRefCnt_SafeAssign(fMap, m); 
}

void hsGEnvironment::SetCenter(const hsPoint3 &p) 
{ 
    fFlags |= kCenterSet; fPos = p; 
}

void hsGEnvironment::SetRadius(hsScalar r) 
{ 
    fFlags |= kRadiusSet; 
    fRadius = r; 
}

void hsGEnvironment::SetFogDistance(hsScalar f) 
{ 
    if( fDevCache &&( f != fFogDistance.GetValue()) )
        fDevCache->Validate(false);
    fFlags |= kFogDistanceSet; 
    fFogDistance.SetValue(f);
}

void hsGEnvironment::SetCurrentDepth(hsScalar f) 
{ 
    if( fDevCache && (f != fCurrentDepth) )
        fDevCache->Validate(false);
    fFlags |= kCurrentDepthSet; 
    fCurrentDepth = f;
}

void hsGEnvironment::SetFogDepth(hsScalar f) 
{ 
    if( fDevCache && (f != fFogDepth.GetValue()) )
        fDevCache->Validate(false);
    fFlags |= kFogDepthSet; 
    fFogDepth.SetValue(f); 
}

void hsGEnvironment::SetFogDensity(hsScalar f) 
{ 
    if( fDevCache && (f != fFogDensity.GetValue()) )
        fDevCache->Validate(false);
    fFlags |= kFogDensitySet; 
    fFogDensity.SetValue(f); 
}

void hsGEnvironment::SetFogColor(const hsColorRGBA &c) 
{ 
    if( fDevCache 
        && (
            (c.r != fFogColor.GetValue().r) 
            || (c.g != fFogColor.GetValue().g) 
            || (c.b != fFogColor.GetValue().b) 
            ) )
        fDevCache->Validate(false);
    fFlags |= kFogColorSet; 
    fFogColor.SetValue(c); 
}

void hsGEnvironment::SetClearColor(const hsColorRGBA &c) 
{ 
    fFlags |= kClearColorSet; 
    fClearColor.SetValue(c); 
}

void hsGEnvironment::SetYon(hsScalar f) 
{ 
    if( fDevCache && (f != fYon.GetValue()) )
        fDevCache->Validate(false);
    fFlags |= kYonSet; 
    fYon.SetValue(f); 
}

void hsGEnvironment::SetFogDistance(const hsTimedValue<hsScalar>& f) 
{ 
    if( fDevCache )
        fDevCache->Validate(false);
    fFlags |= kFogDistanceSet; 
    fFogDistance = f;
}

void hsGEnvironment::SetFogDepth(const hsTimedValue<hsScalar>& f) 
{ 
    if( fDevCache )
        fDevCache->Validate(false);
    fFlags |= kFogDepthSet; 
    fFogDepth = f; 
}

void hsGEnvironment::SetFogDensity(const hsTimedValue<hsScalar>& f) 
{ 
    if( fDevCache )
        fDevCache->Validate(false);
    fFlags |= kFogDensitySet; 
    fFogDensity = f; 
}

void hsGEnvironment::SetFogColor(const hsTimedValue<hsColorRGBA>& c) 
{ 
    if( fDevCache )
        fDevCache->Validate(false);
    fFlags |= kFogColorSet; 
    fFogColor = c; 
}

void hsGEnvironment::SetClearColor(const hsTimedValue<hsColorRGBA>& c) 
{ 
    fFlags |= kClearColorSet; 
    fClearColor = c; 
}

void hsGEnvironment::SetYon(const hsTimedValue<hsScalar>& f) 
{ 
    if( fDevCache )
        fDevCache->Validate(false);
    fFlags |= kYonSet; 
    fYon = f; 
}

void hsGEnvironment::SetFogType(UInt32 t) 
{ 
    fFlags &= ~kFogTypeMask; 
    fFlags |= (t & kFogTypeMask); 
}

void hsGEnvironment::SetFogColorAmbient(hsBool32 on)
{
    if( on )
    {
        fFlags &= ~kFogColorDiffuse;
        fFlags |= kFogColorAmbient;
    }
    else
        fFlags &= ~kFogColorAmbient;
}

void hsGEnvironment::SetFogColorDiffuse(hsBool32 on)
{
    if( on )
    {
        fFlags &= ~kFogColorAmbient;
        fFlags |= kFogColorDiffuse;
    }
    else
        fFlags &= ~kFogColorDiffuse;
}

void hsGEnvironment::SetClearColorAmbient(hsBool32 on)
{
    if( on )
        fFlags |= kClearColorAmbient;
    else
        fFlags &= ~kClearColorAmbient;
}

void hsGEnvironment::SetOverride(hsBool32 on)
{
    if( on )
        fFlags |= kOverride;
    else
        fFlags &= ~kOverride;
}

void hsGEnvironment::SetIsFar(hsBool32 on)
{
    if( on )
        fFlags |= kFarOut;
    else
        fFlags &= ~kFarOut;
}

void hsGEnvironment::SetSortObjects(hsBool32 on)
{
    if( on )
        fFlags |= kSortObjects;
    else
        fFlags &= ~kSortObjects;
}

void hsGEnvironment::SetHasFogControl(hsBool32 on)
{
    if( on )
        fFlags |= kFogControl;
    else
        fFlags &= ~kFogControl;
}

void hsGEnvironment::SetFogControl(hsFogControl* fc)
{
    hsRefCnt_SafeAssign(fFogControl, fc);
}

void hsGEnvironment::SetDeviceCache(hsGDevEnvCache *p) 
{ 
    hsRefCnt_SafeAssign(fDevCache, p); 
}

hsGEnvironment *hsGEnvironment::Copy(hsGEnvironment *env)
{
    if( env->GetFlags() & kMapSet )
        SetMap(env->GetMap());
    else
        fFlags &= ~kMapSet;
    if( env->GetFlags() & kCenterSet )
        SetCenter(env->GetCenter());
    else
        fFlags &= ~kCenterSet;
    if( env->GetFlags() & kRadiusSet )
        SetRadius(env->GetRadius());
    else
        fFlags &= ~kRadiusSet;
    if( env->GetFlags() & kFogDepthSet )
        SetFogDepth(env->GetFogDepth());
    else
        fFlags &= ~kFogDepthSet;
    if( env->GetFlags() & kFogDensitySet )
        SetFogDensity(env->GetFogDensity());
    else
        fFlags &= ~kFogDensitySet;
    if( env->GetFlags() & kFogColorSet )
        SetFogColor(env->GetFogColor());
    else
        fFlags &= ~kFogColorSet;
    if( env->GetFlags() & kClearColorSet )
        SetClearColor(env->GetClearColor());
    else
        fFlags &= ~kClearColorSet;
    if( env->GetFlags() & kYonSet )
        SetYon(env->GetUnscaledYon());
    else
        fFlags &= ~kYonSet;

    if( env->GetFlags() & kCurrentDepthSet )
    {
        fCurrentDepth = env->GetCurrentDepth();
        fFlags |= kCurrentDepthSet;
    }

    fFlags &= ~hsGEnvironment::kFogTypeMask;
    fFlags |= env->GetFlags() & hsGEnvironment::kFogTypeMask;

    SetOverride(env->GetOverride());

    SetDeviceCache(env->GetDeviceCache());

    if( env->GetFlags() & kCacheInvalid )
        fFlags |= kCacheInvalid;
    else
        fFlags &= ~kCacheInvalid;

    int i;
    for( i = 0; i < env->GetNumRenderProcs(); i++ )
        AddRenderProc(env->GetRenderProc(i));

    return this;
}

void hsGEnvironment::MixEnvirons(hsGEnvironment *env, hsGEnvironment *def)
{
    if( env && (env->GetFlags() & kMapSet) )
    {
        SetMap(env->GetMap());
    }
    else if( def && (def->GetFlags() & kMapSet) )
    {
        hsGMaterial *map = def->GetMap();
        if( map )
        {
            SetMap(def->GetMap());
        }
        else
        {
            def->SetMapName(nil);
            hsRefCnt_SafeUnRef(fMap);
            fMap = nil;
            fFlags &= ~kMapSet;
        }
    }
    else
    {
        hsRefCnt_SafeUnRef(fMap);
        fMap = nil;
        fFlags &= ~kMapSet;
    }
    if( env && (env->GetFlags() & kCenterSet) )
        SetCenter(env->GetCenter());
    else if( def &&(def->GetFlags() & kCenterSet) )
        SetCenter(def->GetCenter());
    else
        fFlags &= ~kCenterSet;
    if( env && (env->GetFlags() & kRadiusSet) )
        SetRadius(env->GetRadius());
    else if( def &&(def->GetFlags() & kRadiusSet) )
        SetRadius(def->GetRadius());
    else
        fFlags &= ~kRadiusSet;

    if( env && (env->GetFlags() & kFogDepthSet) )
        SetFogDepth(env->GetFogDepth());
    else if( def &&(def->GetFlags() & kFogDepthSet) )
        SetFogDepth(def->GetFogDepth());
    else
        fFlags &= ~kFogDepthSet;

    if( env &&(env->GetFlags() & kCurrentDepthSet) )
    {
        fCurrentDepth = env->GetCurrentDepth();
        fFlags |= kCurrentDepthSet;
    }
    else
    if( def &&(def->GetFlags() & kCurrentDepthSet) )
    {
        fCurrentDepth = def->GetCurrentDepth();
        fFlags |= kCurrentDepthSet;
    }
    else
        fFlags &= ~kCurrentDepthSet;

    fFlags &= ~kFogTypeMask;
    if( env && (env->GetFlags() & kFogTypeMask)  )
        fFlags |= env->GetFlags() & kFogTypeMask;
    else if( def && (def->GetFlags() & kFogTypeMask) )
        fFlags |= def->GetFlags() & kFogTypeMask;
    else
        fFlags &= ~kFogTypeMask;

    if( env && (env->GetFlags() & kFogDensitySet) )
        SetFogDensity(env->GetFogDensity());
    else if( def &&(def->GetFlags() & kFogDensitySet) )
        SetFogDensity(def->GetFogDensity());
    else
        SetFogDensity(hsScalar1);

    if( env &&(env->GetFlags() & kFogColorSet) )
        SetFogColor(env->GetFogColor());
    else if( def &&(def->GetFlags() & kFogColorSet) )
        SetFogColor(def->GetFogColor());
    else
    {
        hsColorRGBA col;
        col.r = col.g = col.b = col.a = 0;
        SetFogColor(col);
    }
    if( env &&(env->GetFlags() & kClearColorSet) )
        SetClearColor(env->GetClearColor());
    else if( def &&(def->GetFlags() & kClearColorSet) )
        SetClearColor(def->GetClearColor());
    else
        fFlags &= ~kClearColorSet;

    if( env && (env->GetFlags() & kYonSet) )
        SetYon(env->GetUnscaledYon());
    else if( def &&(def->GetFlags() & kYonSet) )
        SetYon(def->GetUnscaledYon());
    else
        fFlags &= ~kYonSet;

    if( env && (env->GetOverride()) )
        SetOverride(true);
    else if( def &&(def->GetOverride()) )
        SetOverride(true);
    else
        SetOverride(false);

    if( env && env->GetDeviceCache() )
        SetDeviceCache(env->GetDeviceCache());
    else if( def && def->GetDeviceCache() )
        SetDeviceCache(def->GetDeviceCache());
    else
        SetDeviceCache(nil);

    int i;
    for( i = 0; i < GetNumRenderProcs(); i++ )
    {
        hsRefCnt_SafeUnRef(GetRenderProc(i));
    }
    fRenderProcs.Reset();
    if( env )
    {
        for( i = 0; i < env->GetNumRenderProcs(); i++ )
            AddRenderProc(env->GetRenderProc(i));
    }
    if( def )
    {
        for( i = 0; i < def->GetNumRenderProcs(); i++ )
            AddRenderProc(def->GetRenderProc(i));
    }

}

void hsGEnvironment::Blend()
{
    if( fFogControl )
        fFogControl->Blend();
}

void hsGEnvironment::Restore()
{
    if( fFogControl )
        fFogControl->Restore();
}

void hsGEnvironment::Init(hsSceneNode* node)
{
    if (!node)
    {
        char str[256];
        sprintf(str, "hsGEnvironment %s initted with nil room.  Weirdness may result.",
            GetKeyName());
        HSDebugProc(str);
    }
    else
    {
        if( fFogControl )
            fFogControl->Init(node);
    }

    SetResetState();
}

void hsGEnvironment::SaveFogState()
{
    FogState* curFogState = new FogState;
    curFogState->SetFromEnv(this);
    fFogStateStack.Push(curFogState);
}

void hsGEnvironment::RestoreFogState()
{
    FogState* curFogState = fFogStateStack.GetCount() ? fFogStateStack.Pop() : nil;
    if (curFogState)
    {
        curFogState->SetToEnv(this);
    }
    delete curFogState;
}

void hsGEnvironment::Read(hsStream *stream)
{
    fFlags = stream->ReadSwap32();

    if( fFlags & hsGEnvironment::kFogDistanceSet )
    {
        hsScalar d = stream->ReadSwapScalar();
        SetFogDistance(d);
    }
    if( fFlags & hsGEnvironment::kFogDepthSet )
    {
        hsScalar d = stream->ReadSwapScalar();
        SetFogDepth(d);
        SetCurrentDepth(d);
    }
    if( fFlags & hsGEnvironment::kFogDensitySet )
    {
        hsScalar d = stream->ReadSwapScalar();
        SetFogDensity(d);
    }
    if( fFlags & hsGEnvironment::kFogColorSet )
    {
        hsColorRGBA c;
        c.r = stream->ReadSwapScalar();
        c.g = stream->ReadSwapScalar();
        c.b = stream->ReadSwapScalar();
        c.a = stream->ReadSwapScalar();
        SetFogColor(c);
    }
    if( fFlags & hsGEnvironment::kClearColorSet )
    {
        hsColorRGBA c;
        c.r = stream->ReadSwapScalar();
        c.g = stream->ReadSwapScalar();
        c.b = stream->ReadSwapScalar();
        c.a = stream->ReadSwapScalar();
        SetClearColor(c);
    }
    if( fFlags & hsGEnvironment::kYonSet )
    {
        hsScalar d = stream->ReadSwapScalar();
        SetYon(d);
    }

    if( fFlags & hsGEnvironment::kCenterSet )
        fPos.Read(stream);
    if( fFlags & hsGEnvironment::kRadiusSet )
        fRadius = stream->ReadSwapScalar();
    // still don't write the map out

    if( !(fFlags & kFogTypeMask) )
    {
        if( GetFogDepth() <= 0.5f )
            fFlags |= kFogLinear;
        else
            fFlags |= kFogExp;
    }
}

void hsGEnvironment::Write(hsStream *stream)
{
    stream->WriteSwap32(fFlags);

    if( fFlags & hsGEnvironment::kFogDistanceSet )
        stream->WriteSwapScalar(GetFogDistance());
    if( fFlags & hsGEnvironment::kFogDepthSet )
        stream->WriteSwapScalar(GetFogDepth());
    if( fFlags & hsGEnvironment::kFogDensitySet )
        stream->WriteSwapScalar(GetFogDensity());
    if( fFlags & hsGEnvironment::kFogColorSet )
    {
        hsColorRGBA c = GetFogColor();
        stream->WriteSwapScalar(c.r);
        stream->WriteSwapScalar(c.g);
        stream->WriteSwapScalar(c.b);
        stream->WriteSwapScalar(c.a);
    }
    if( fFlags & hsGEnvironment::kClearColorSet )
    {
        hsColorRGBA c = GetClearColor();
        stream->WriteSwapScalar(c.r);
        stream->WriteSwapScalar(c.g);
        stream->WriteSwapScalar(c.b);
        stream->WriteSwapScalar(c.a);
    }
    if( fFlags & hsGEnvironment::kYonSet )
        stream->WriteSwapScalar(GetUnscaledYon());

    if( fFlags & hsGEnvironment::kCenterSet )
        fPos.Write(stream);
    if( fFlags & hsGEnvironment::kRadiusSet )
        stream->WriteSwapScalar(fRadius);
    // still don't write the map out

}

void hsGEnvironment::Write(hsStream* stream, hsResMgr* group)
{
    Write(stream);

    if( fFlags & hsGEnvironment::kHasRenderProcs )
    {
        int n = GetNumRenderProcs();
        stream->WriteSwap32(n);
        int i;
        for( i = 0; i < n; i++ )
        {
hsAssert(0,"Its Pauls fault");
//          plFactory::LabelAndWrite(stream, group, GetRenderProc(i));
        }
    }

    if ((GetFlags() & hsGEnvironment::kEnvironMapSet) == hsGEnvironment::kEnvironMapSet)
    {
        group->WriteKey(stream, GetMap());
    }        
    
    stream->WriteSwap32(fNodeKeys.GetCount());
    Int32 i;
    for (i = 0; i < fNodeKeys.GetCount(); i++)
    {
        group->WriteKey(stream, fNodeKeys[i]);
    }

    if( fFlags & hsGEnvironment::kFogControl )
        IWriteFogControl(stream, group);
}

void hsGEnvironment::Read(hsStream* stream, hsResMgr* group)
{
    Read(stream);
    
    if( fFlags & hsGEnvironment::kHasRenderProcs )
    {
        int n = stream->ReadSwap32();
        int i;
        for( i = 0; i < n; i++ )
        {
hsAssert(0,"Its Pauls fault");
//          hsGRenderProcs* rp = hsGRenderProcs::ConvertNoRef(plFactory::CreateAndRead(stream, group));
//          AddRenderProc(rp);
//          hsRefCnt_SafeUnRef(rp);
        }
    }

    if ((GetFlags() & hsGEnvironment::kEnvironMapSet) == hsGEnvironment::kEnvironMapSet)
    {
        plRefMsg* refMsg = new plRefMsg(GetKey(), plRefMsg::kOnCreate);
        group->ReadKeyNotifyMe(stream,refMsg);
    }
    
    hsAssert(fNodeKeys.GetCount() == 0, "fNodeKeys not empty in hsGEnvironment::Read.");
    Int32 nodeCount = stream->ReadSwap32();
    hsAssert(nodeCount > 0, "Environment node not in any rooms in hsGEnvironment::Read.");
    Int32 i;
    for (i = 0; i < nodeCount; i++)
    {
        plKey *key = group->ReadKey(stream);
        AddNodeKey(key);
    }

    if( fFlags & hsGEnvironment::kFogControl )
        IReadFogControl(stream, group);
}

void hsGEnvironment::IReadFogControl(hsStream* s, hsResMgr* mgr)
{
hsAssert(0,"Its Pauls fault");
//  hsFogControl* fc = hsFogControl::ConvertNoRef(plFactory::CreateAndRead(s, mgr));
//  SetFogControl(fc);
//  hsRefCnt_SafeUnRef(fc);

}

void hsGEnvironment::IWriteFogControl(hsStream* s, hsResMgr* mgr)
{
hsAssert(0,"Its Pauls fault");
//  plFactory::LabelAndWrite(s, mgr, fFogControl);
}

void hsGEnvironment::Push(hsG3DDevice* d)
{
#if 0 // Nuking old device - mf
    int i;

    for( i = 0; i < GetNumRenderProcs(); i++ )
        d->AddRenderProc(GetRenderProc(i));
#endif // Nuking old device - mf
}

void hsGEnvironment::Pop(hsG3DDevice* d)
{
#if 0 // Nuking old device - mf
    int i;
    for( i = 0; i < GetNumRenderProcs(); i++ )
        d->RemoveRenderProc(GetRenderProc(i));
#endif // Nuking old device - mf
}

void hsGEnvironment::AddRenderProc(hsGRenderProcs* rp) 
{ 
    hsRefCnt_SafeRef(rp); 
    fRenderProcs.Append(rp); 
    fFlags |= hsGEnvironment::kHasRenderProcs;
} 

hsGRenderProcs* hsGEnvironment::GetRenderProc(int i) 
{ 
    return fRenderProcs[i]; 
} 

UInt32 hsGEnvironment::GetNumRenderProcs() 
{ 
    return fRenderProcs.GetCount(); 
}

hsScalar hsGEnvironment::SetYonScale(hsScalar s)
{
    const hsScalar kMinYonScale = 0.05f;
    const hsScalar kMaxYonScale = 100.f;

    if( s < kMinYonScale )
        s = kMinYonScale;
    else if( s > kMaxYonScale )
        s = kMaxYonScale;
    return fYonScale = s;
}

hsBool hsGEnvironment::MsgReceive(plMessage* msg)
{
    plRefMsg* refMsg = plRefMsg::ConvertNoRef(msg);
    
    if( refMsg )
    {

        hsGMaterial *mat = hsGMaterial::ConvertNoRef(refMsg->GetRef());
        SetMap(mat);
    }
    return false;
}