/*==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 "plUoid.h"
#include "hsStream.h"
#include "hsUtils.h"

//// plLocation //////////////////////////////////////////////////////////////

const plLocation plLocation::kGlobalFixedLoc(plLocation::kGlobalFixedLocIdx);
const plLocation plLocation::kLocalStartLoc(plLocation::kLocalLocStartIdx);
const plLocation plLocation::kLocalEndLoc(plLocation::kLocalLocEndIdx);
const plLocation plLocation::kNormalStartLoc(plLocation::kNormalLocStartIdx);
const plLocation plLocation::kGlobalServerLoc(plLocation::kGlobalServerLocIdx, plLocation::kReserved);
const plLocation plLocation::kInvalidLoc;

plLocation::plLocation(const plLocation& toCopyFrom)
{
    *this = toCopyFrom;
}

void plLocation::Read(hsStream* s)
{
    s->LogReadSwap(&fSequenceNumber, "Location Sequence Number");
    s->LogReadSwap(&fFlags, "Location Flags");
}

void plLocation::Write(hsStream* s) const
{
    s->WriteSwap(fSequenceNumber);
    s->WriteSwap(fFlags);
}

plLocation& plLocation::operator=(const plLocation& rhs)
{
    fSequenceNumber = rhs.fSequenceNumber;
    fFlags = rhs.fFlags;
    return *this;
}

hsBool plLocation::operator==(const plLocation& u) const
{
    // Ignore the itinerant flag when comparing, because
    return (fSequenceNumber == u.fSequenceNumber) && ((fFlags & ~kItinerant) == (u.fFlags & ~kItinerant));
}

void plLocation::Set(UInt32 seqNum)
{
    fSequenceNumber = seqNum;
}

void plLocation::Invalidate()
{
    fSequenceNumber = kInvalidLocIdx;
    fFlags = 0; // Set to kInvalid?
}

hsBool plLocation::IsValid() const
{
    return (fSequenceNumber == kInvalidLocIdx) ? false : true;
}

hsBool plLocation::IsReserved() const
{
    return hsCheckBits(fFlags, kReserved);
}

hsBool plLocation::IsItinerant() const
{
    return hsCheckBits(fFlags, kItinerant);
}

hsBool plLocation::IsVirtual() const
{
    // This returns whether the location is "virtual", i.e. isn't a true room per se. Like fixed keys
    if (fSequenceNumber == kGlobalFixedLocIdx)
        return true;

    return false;
}

// THIS SHOULD BE FOR DEBUGGING ONLY <hint hint>
char* plLocation::StringIze(char* str)  const // Format to displayable string
{
    sprintf(str, "S0x%xF0x%x", fSequenceNumber, int(fFlags));
    return str;
}

plLocation plLocation::MakeReserved(UInt32 number)
{
    return plLocation(kReservedLocAvailableStart + number, kReserved);
}

plLocation plLocation::MakeNormal(UInt32 number)
{
    return plLocation(kNormalLocStartIdx + number);
}

//// plUoid //////////////////////////////////////////////////////////////////

plUoid::plUoid(const plLocation& location, UInt16 classType, const char* objectName, const plLoadMask& m)
{
    fObjectName = nil;
    Invalidate();

    fLocation = location;
    fClassType = classType;
    fObjectName = hsStrcpy(objectName);
    fLoadMask = m;
    fClonePlayerID = 0;
}

plUoid::plUoid(const plUoid& src)
{
    fObjectName = nil;
    Invalidate();
    *this = src;
}

plUoid::~plUoid()
{
    Invalidate();
}

void plUoid::Read(hsStream* s)
{
    hsAssert(fObjectName == nil, "Reading over an old uoid? You're just asking for trouble, aren't you?");

    // first read contents flags
    UInt8 contents = s->ReadByte();

    fLocation.Read(s);

    // conditional loadmask read
    if (contents & kHasLoadMask)
        fLoadMask.Read(s);
    else
        fLoadMask.SetAlways();

    s->LogReadSwap(&fClassType, "ClassType");
    s->LogReadSwap(&fObjectID, "ObjectID");
    s->LogSubStreamPushDesc("ObjectName");
    fObjectName = s->LogReadSafeString();

    // conditional cloneIDs read
    if (contents & kHasCloneIDs)
    {       
        s->LogReadSwap( &fCloneID ,"CloneID");
        UInt16 dummy;
        s->LogReadSwap(&dummy, "dummy"); // To avoid breaking format
        s->LogReadSwap( &fClonePlayerID ,"ClonePlayerID");
    }
    else
    {
        fCloneID = 0;
        fClonePlayerID = 0;
    }
}

void plUoid::Write(hsStream* s) const
{
    // first write contents byte
    UInt8 contents = IsClone() ? kHasCloneIDs : 0;
    if (fLoadMask.IsUsed())
        contents |= kHasLoadMask;
    s->WriteByte(contents);

    fLocation.Write(s);

    // conditional loadmask write
    if (contents & kHasLoadMask)
        fLoadMask.Write(s);

    s->WriteSwap( fClassType );
    s->WriteSwap( fObjectID );
    s->WriteSafeString( fObjectName );

    // conditional cloneIDs write
    if (contents & kHasCloneIDs)
    {
        s->WriteSwap(fCloneID);
        UInt16 dummy = 0;
        s->WriteSwap(dummy); // to avoid breaking format
        s->WriteSwap(fClonePlayerID);
    }
}

void plUoid::Invalidate()
{
    fObjectID = 0;
    fCloneID = 0;
    fClonePlayerID = 0;
    fClassType = 0;
    if (fObjectName)
        delete [] fObjectName;
    fObjectName = nil;
    fLocation.Invalidate();
    fLoadMask = plLoadMask::kAlways;

}

hsBool plUoid::IsValid() const
{
    if (!fLocation.IsValid() || fObjectName == nil)
        return false;

    return true;
}

hsBool plUoid::operator==(const plUoid& u) const
{
    return  fLocation == u.fLocation
            && fLoadMask == u.fLoadMask
            && fClassType == u.fClassType
            && hsStrEQ(fObjectName, u.fObjectName)
            && fObjectID == u.fObjectID
            && fCloneID == u.fCloneID
            && fClonePlayerID == u.fClonePlayerID;
}

plUoid& plUoid::operator=(const plUoid& rhs)
{
    fObjectID = rhs.fObjectID;
    fCloneID = rhs.fCloneID;
    fClonePlayerID = rhs.fClonePlayerID;
    fClassType = rhs.fClassType;
    if (fObjectName)
        delete [] fObjectName;
    fObjectName = hsStrcpy(rhs.fObjectName);
    fLocation = rhs.fLocation;
    fLoadMask = rhs.fLoadMask;

    return *this;
}

// THIS SHOULD BE FOR DEBUGGING ONLY <hint hint>
char* plUoid::StringIze(char* str) const // Format to displayable string
{
    sprintf(str, "(0x%x:0x%x:%s:C:[%lu,%lu])", 
        fLocation.GetSequenceNumber(), 
        int(fLocation.GetFlags()), 
        fObjectName, 
        GetClonePlayerID(), 
        GetCloneID());
    return str;
}