/*==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==*/

#define PLFACTORY_PRIVATE
#include "hsTypes.h"
#include "plFactory.h"
#include "hsStream.h"
#include "plCreatable.h"
#include "plCreator.h"
#include "hsUtils.h"

// For class names
#include "plCreatableStrings.h"


static plFactory*   theFactory = nil;

plFactory::plFactory()
{
    fCreators.SetCountAndZero(plCreatableIndex::plNumClassIndices);
}

plFactory::~plFactory()
{
}

hsBool plFactory::ICreateTheFactory()
{
    if( theFactory )
        return true;

    theFactory = TRACKED_NEW plFactory;

    return theFactory != nil;
}

UInt16 plFactory::IGetNumClasses()
{
    return plCreatableIndex::plNumClassIndices;
}

void plFactory::IForceShutdown()
{
    int i;
    for( i = 0; i < fCreators.GetCount(); i++ )
    {
        if( fCreators[i] )
        {
            hsRefCnt_SafeUnRef(this);
            fCreators[i] = nil;
        }
    }
}

void plFactory::IShutdown()
{
    delete theFactory;
    theFactory = nil;
}

UInt16 plFactory::IRegister(UInt16 hClass, plCreator* worker)
{
    delete fCreators[hClass];
    fCreators[hClass] = worker;
    return hClass;
}

//
// return true if creator exists
//
bool plFactory::CanCreate(UInt16 hClass)
{
    if( hClass & 0x8000 )               // nil creatable
        return false;

    if( !theFactory && !ICreateTheFactory() )   // no factory
        return false;

    if( hClass >= theFactory->IGetNumClasses() )    // invalid index
        return false;

    return ( theFactory->fCreators[ hClass ] != nil );  // check creator
}

plCreatable* plFactory::ICreate(UInt16 hClass)
{
    if (CanCreate(hClass))
    {
        return fCreators[hClass]->Create();
    }

    if (!(hClass & 0x8000))
    {
        hsAssert( false, "Invalid class index or nil creator : plFactory::Create()" );  
    }
    return nil;
}

void plFactory::UnRegister(UInt16 hClass, plCreator* worker)
{
    if( theFactory )
    {
        theFactory->IUnRegister(hClass);
        hsRefCnt_SafeUnRef(theFactory);
        if( theFactory->RefCnt() < 2 )
            IShutdown();
    }
}

void plFactory::IUnRegister(UInt16 hClass)
{
    fCreators[hClass] = nil;
}

UInt16 plFactory::Register(UInt16 hClass, plCreator* worker)
{
    if( !theFactory && !ICreateTheFactory() )
            return nil;

    hsRefCnt_SafeRef(theFactory);
    return theFactory->IRegister(hClass, worker);
}

plCreatable* plFactory::Create(UInt16 hClass)
{
    if( !theFactory && !ICreateTheFactory() )
            return nil;

    return theFactory->ICreate(hClass);
}



UInt16 plFactory::GetNumClasses()
{
    if( !theFactory && !ICreateTheFactory() )
        return 0;

    return theFactory->IGetNumClasses();
}

hsBool plFactory::IDerivesFrom(UInt16 hBase, UInt16 hDer)
{
    if( hDer >= fCreators.GetCount() )
        return false;

    return fCreators[hDer] ? fCreators[hDer]->HasBaseClass(hBase) : false;
}

hsBool plFactory::DerivesFrom(UInt16 hBase, UInt16 hDer)
{
    if( !theFactory && !ICreateTheFactory() )
        return 0;

    return theFactory->IDerivesFrom(hBase, hDer);
}

// slow lookup for things like console
UInt16 plFactory::FindClassIndex(const char* className)
{
    int numClasses=GetNumClasses();

    if (className && theFactory)
    {
        int i;
        for( i = 0; i < theFactory->fCreators.GetCount(); i++ )
        {
            if( theFactory->fCreators[i] && !_stricmp(className, theFactory->fCreators[i]->ClassName()) )
            {
                return theFactory->fCreators[i]->ClassIndex();
            }
        }
    }
    return numClasses;      // err
}


hsBool plFactory::IIsValidClassIndex(UInt16 hClass)
{
    return ( hClass < fCreators.GetCount() );
}

hsBool plFactory::IsValidClassIndex(UInt16 hClass)
{
    return theFactory->IIsValidClassIndex(hClass);
}

void plFactory::SetTheFactory(plFactory* fac)
{
    // There are four cases here.
    // 1) Our factory is nil, and we're being given one to use
    //      Just take it and ref it.
    // 2) Our factory is non-nil, and we're being given on to use
    //      Ours is bogus, pitch it and use the new one.
    // 3) Our factory is non-nil, and we're being given a nil one
    //      Means we're being shut down. Unref the old one. If
    //      the refcnt drops to one, we're the last one out, so
    //      go ahead and delete it.
    // 4) Our factory is nil and the new one is nil
    //      Shouldn't happen, but if it does, just ignore it.
    if( !theFactory && fac )
    {
        hsRefCnt_SafeAssign(theFactory, fac);
    }
    else if( theFactory && fac )
    {
        theFactory->IForceShutdown();
        hsRefCnt_SafeAssign(theFactory, fac);
    }
    else if( theFactory && !fac )
    {
        hsRefCnt_SafeUnRef(theFactory);
        if( theFactory->RefCnt() < 2 )
            delete theFactory;
        theFactory = nil;
    }

}

plFactory* plFactory::GetTheFactory()
{
    if( !theFactory && !ICreateTheFactory() )
            return nil;

    return theFactory;
}

// For my own nefarious purposes... hsStatusMessage  plCreatableIndex
const char  *plFactory::GetNameOfClass(UInt16 type)
{
    if( type < GetNumClasses() )
    {
        if( theFactory->fCreators[ type ] )
            return theFactory->fCreators[ type ]->ClassName();
        
        static int keyedStringsSize = sizeof(plCreatableStrings::fKeyedStrings)/4;
        // If we don't have a creator yet, try falling back on plCreatableStrings
        if( type < KEYED_OBJ_DELINEATOR && type<keyedStringsSize)
            return plCreatableStrings::fKeyedStrings[ type ];

        if (type < plCreatableIndex::kDatabaseStructIndexesStart)
        {
            static int nonKeyedStringsSize = sizeof(plCreatableStrings::fNonKeyedStrings)/4;
            int idx=type - KEYED_OBJ_DELINEATOR;
            if (idx<nonKeyedStringsSize)
                return plCreatableStrings::fNonKeyedStrings[ idx ];
        }

        static int nonKeyedPostDBStringsSize = sizeof(plCreatableStrings::fNonKeyedPostDBStrings)/4;
        int idx=type - plCreatableIndex::kDatabaseStructIndexesEnd -1;
        if (idx<nonKeyedPostDBStringsSize)
            return plCreatableStrings::fNonKeyedPostDBStrings[ idx ];
    }

    hsAssert(type < GetNumClasses() || type==0xffff,"InValid type");
    return nil;
}

#ifdef HS_DEBUGGING

/*
**
**  Function Name:          Validate
**  Input(s):               Void
**  Output(s):              Void
**  Function Description:   This function examines all the Workers in the Factory and compares their Factory 
**                              index to the Enums found in plCreatableIndex.  If they are Keyed objects, and
**                              larger than 512 on the Factory index, or non-Keyed objects with a Factory
**                              index of less than 512, exit with an Error Message.  Otherwise continue through
**                              the iteration of Factory Indices.
**
**
*/

void plFactory::IValidate(UInt16 keyIndex)
{

    int FactoryIndex = GetNumClasses();

    hsBool bogus = false;

    for(int iter=0; iter < FactoryIndex; iter++)
    {
        if (IDerivesFrom(keyIndex, iter))
        {
            if(iter >= KEYED_OBJ_DELINEATOR && theFactory->fCreators[iter])
            {
                char Buffer[512];
                sprintf(Buffer, "Object %s is a hsKeyedObject, Must appear before 'KEYED_OBJ_DELINEATOR' in plCreatableIndex.h\n",GetNameOfClass(iter));
                hsStatusMessage(Buffer);
                bogus = true;
            } 
            
        }
        else
        {
            if(iter < KEYED_OBJ_DELINEATOR && theFactory->fCreators[iter])
            {
                char Buffer[512];
                sprintf(Buffer, "Object %s is NOT a hsKeyedObject, Must appear after 'KEYED_OBJ_DELINEATOR' in plCreatableIndex.h\n",GetNameOfClass(iter));
                hsStatusMessage(Buffer);
                bogus = true;
            
            } 
        }
    }
    hsAssert(!bogus,"The class(s) you just added to plCreatableIndex.h in wrong spot, see output window");

}  

void plFactory::Validate(UInt16 keyIndex)
{
    theFactory->IValidate(keyIndex);

}


#endif