//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#include "platform/platform.h"
#include "dgl/gTexManager.h"
#include "core/resManager.h"
#include "core/stream.h"
#include "dgl/materialList.h"

//--------------------------------------
MaterialList::MaterialList()
{
   mTextureType = BitmapTexture;
   mClampToEdge = false;

   VECTOR_SET_ASSOCIATION(mMaterialNames);
   VECTOR_SET_ASSOCIATION(mMaterials);
}

MaterialList::MaterialList(const MaterialList* pCopy)
{
   VECTOR_SET_ASSOCIATION(mMaterialNames);
   VECTOR_SET_ASSOCIATION(mMaterials);

   mClampToEdge = pCopy->mClampToEdge;
   mTextureType = pCopy->mTextureType;

   mMaterialNames.setSize(pCopy->mMaterialNames.size());
   S32 i;
   for (i = 0; i < mMaterialNames.size(); i++) {
      if (pCopy->mMaterialNames[i]) {
         mMaterialNames[i] = new char[dStrlen(pCopy->mMaterialNames[i]) + 1];
         dStrcpy(mMaterialNames[i], pCopy->mMaterialNames[i]);
      } else {
         mMaterialNames[i] = NULL;
      }
   }

   mMaterials.setSize(pCopy->mMaterials.size());
   for (i = 0; i < mMaterials.size(); i++) {
      constructInPlace(&mMaterials[i]);
      mMaterials[i] = pCopy->mMaterials[i];
   }
}



MaterialList::MaterialList(U32 materialCount, const char **materialNames)
{
   VECTOR_SET_ASSOCIATION(mMaterialNames);
   VECTOR_SET_ASSOCIATION(mMaterials);

   set(materialCount, materialNames);
}


//--------------------------------------
void MaterialList::set(U32 materialCount, const char **materialNames)
{
   free();
   mMaterials.setSize(materialCount);
   mMaterialNames.setSize(materialCount);
   for(U32 i = 0; i < materialCount; i++)
   {
      // vectors DO NOT initialize classes so manually call the constructor
      constructInPlace(&mMaterials[i]);
      mMaterialNames[i] = new char[dStrlen(materialNames[i]) + 1];
      dStrcpy(mMaterialNames[i], materialNames[i]);
   }
}


//--------------------------------------
MaterialList::~MaterialList()
{
   free();
}


//--------------------------------------
void MaterialList::load(U32 index, const char* path)
{
   AssertFatal(index < size(), "MaterialList:: index out of range.");
   if (index < size())
   {
      TextureHandle &handle = mMaterials[index];
      if (handle.getBitmap() == NULL)
      {
         const char *name = mMaterialNames[index];
         if (name && *name)
         {
            if (path) {
               char buffer[512];
               dSprintf(buffer, sizeof(buffer), "%s/%s", path , name);
               handle.set(buffer, mTextureType, mClampToEdge);
            }
            else
               handle.set(name, mTextureType, mClampToEdge);
         }
      }
   }
}


//--------------------------------------
bool MaterialList::load(const char* path)
{
   AssertFatal(mMaterialNames.size() == mMaterials.size(), "MaterialList::load: internal vectors out of sync.");

   for(S32 i=0; i < mMaterials.size(); i++)
      load(i,path);

   for(S32 i=0; i < mMaterials.size(); i++)
   {
      // TSMaterialList nulls out the names of IFL materials, so
      // we need to ignore empty names.
      const char *name = mMaterialNames[i];
      if (name && *name && !mMaterials[i])
         return false;
   }
   return true;
}


//--------------------------------------
void MaterialList::unload()
{
   AssertFatal(mMaterials.size() == mMaterials.size(), "MaterialList::unload: internal vectors out of sync.");
   for(S32 i=0; i < mMaterials.size(); i++)
      mMaterials[i].~TextureHandle();
}


//--------------------------------------
void MaterialList::free()
{
   AssertFatal(mMaterials.size() == mMaterials.size(), "MaterialList::free: internal vectors out of sync.");
   for(S32 i=0; i < mMaterials.size(); i++)
   {
      if(mMaterialNames[i])
         delete [] mMaterialNames[i];
      mMaterials[i].~TextureHandle();
   }
   mMaterialNames.setSize(0);
   mMaterials.setSize(0);
}


//--------------------------------------
U32 MaterialList::push_back(TextureHandle textureHandle, const char * filename)
{
   mMaterials.increment();
   mMaterialNames.increment();

   // vectors DO NOT initialize classes so manually call the constructor
   constructInPlace(&mMaterials.last());
   mMaterials.last()    = textureHandle;
   mMaterialNames.last() = new char[dStrlen(filename) + 1];
   dStrcpy(mMaterialNames.last(), filename);

   // return the index
   return mMaterials.size()-1;
}

//--------------------------------------
U32 MaterialList::push_back(const char *filename)
{
   mMaterials.increment();
   mMaterialNames.increment();

   // vectors DO NOT initialize classes so manually call the constructor
   constructInPlace(&mMaterials.last());
   mMaterialNames.last() = new char[dStrlen(filename) + 1];
   dStrcpy(mMaterialNames.last(), filename);

   // return the index
   return mMaterials.size()-1;
}


//--------------------------------------
U32 MaterialList::push_back(const char *filename, GBitmap *bmp, TextureHandleType type, bool clampToEdge)
{
   mMaterials.increment();
   mMaterialNames.increment();

   // vectors DO NOT initialize classes so manually call the constructor
   constructInPlace(&mMaterials.last());
   mMaterials.last().set(filename, bmp, type, clampToEdge);
   mMaterialNames.last() = new char[dStrlen(filename) + 1];
   dStrcpy(mMaterialNames.last(), filename);

   // return the index
   return mMaterials.size()-1;
}


//--------------------------------------
bool MaterialList::read(Stream &stream)
{
   free();

   // check the stream version
   U8 version;
   if ( stream.read(&version) && version != BINARY_FILE_VERSION)
      return readText(stream,version);

   // how many materials?
   U32 count;
   if ( !stream.read(&count) )
      return false;

   // pre-size the vectors for efficiency
   mMaterials.reserve(count);
   mMaterialNames.reserve(count);

   // read in the materials
   for (U32 i=0; i<count; i++)
   {
      // Load the bitmap name
      char buffer[256];
      stream.readString(buffer);
      if( !buffer[0] )
      {
         AssertWarn(0, "MaterialList::read: error reading stream");
         return false;
      }

      // Material paths are a legacy of Tribes tools,
      // strip them off...
      char *name = &buffer[dStrlen(buffer)];
      while (name != buffer && name[-1] != '/' && name[-1] != '\\')
         name--;

      // Add it to the list
      mMaterials.increment();
      mMaterialNames.increment();
      // vectors DO NOT initialize classes so manually call the constructor
      constructInPlace(&mMaterials.last());
      mMaterialNames.last() = new char[dStrlen(name) + 1];
      dStrcpy(mMaterialNames.last(), name);
   }

   return (stream.getStatus() == Stream::Ok);
}


//--------------------------------------
bool MaterialList::write(Stream &stream)
{
   AssertFatal(mMaterials.size() == mMaterialNames.size(), "MaterialList::write: internal vectors out of sync.");

   stream.write((U8)BINARY_FILE_VERSION);    // version
   stream.write((U32)mMaterials.size());     // material count

   for(S32 i=0; i < mMaterials.size(); i++)  // material names
      stream.writeString(mMaterialNames[i]);

   return (stream.getStatus() == Stream::Ok);
}


//--------------------------------------
bool MaterialList::readText(Stream &stream, U8 firstByte)
{
   free();

   if (!firstByte)
      return (stream.getStatus() == Stream::Ok || stream.getStatus() == Stream::EOS);

   char buf[1024];
   buf[0] = firstByte;
   U32 offset = 1;

   for(;;)
   {
      stream.readLine((U8*)(buf+offset), sizeof(buf)-offset);
      if(!buf[0])
         break;
      offset = 0;

      // Material paths are a legacy of Tribes tools,
      // strip them off...
      char *name = &buf[dStrlen(buf)];
      while (name != buf && name[-1] != '/' && name[-1] != '\\')
         name--;

      // Add it to the list
      mMaterials.increment();
      mMaterialNames.increment();
      // vectors DO NOT initialize classes so manually call the constructor
      constructInPlace(&mMaterials.last());
      mMaterialNames.last() = new char[dStrlen(name) + 1];
      dStrcpy(mMaterialNames.last(), name);
   }
   return (stream.getStatus() == Stream::Ok || stream.getStatus() == Stream::EOS);
}

bool MaterialList::readText(Stream &stream)
{
   U8 firstByte;
   stream.read(&firstByte);
   return readText(stream,firstByte);
}

//--------------------------------------
bool MaterialList::writeText(Stream &stream)
{
   AssertFatal(mMaterials.size() == mMaterialNames.size(), "MaterialList::writeText: internal vectors out of sync.");

   for(S32 i=0; i < mMaterials.size(); i++)
      stream.writeLine((U8*)mMaterialNames[i]);
   stream.writeLine((U8*)"");

   return (stream.getStatus() == Stream::Ok);
}


//--------------------------------------
ResourceInstance* constructMaterialList(Stream &stream)
{
   MaterialList *matList = new MaterialList;
   if(matList->readText(stream))
      return matList;
   else
   {
      delete matList;
      return NULL;
   }
}