#include "interiorMapRes.h"

#include "dgl/gBitmap.h"
#include "console/consoleTypes.h"
#include "console/console.h"
#include "dgl/dgl.h"
#include "collision/convexBrush.h"

InteriorMapResource::InteriorMapResource()
{
   mBrushFormat = Unknown;
   mTexGensCalced = false;
   mNextBrushID = 0;
   mWorldSpawn = NULL;
   mBrushScale = 1.0f;
}

InteriorMapResource::~InteriorMapResource()
{
}

bool InteriorMapResource::load(const char* filename)
{
   // Open our file and read it into a buffer
   FileStream* pStream = new FileStream;

	if (pStream->open(filename, FileStream::Read) == false)
	{
      Con::errorf("Unable to open %s", filename);

		delete pStream;
		return false;
	}

   read(*pStream);

   // Done with actual file
	pStream->close();
	delete pStream;

   return true;
}

bool InteriorMapResource::read(Stream& stream)
{
   // Parse that sucker
   Tokenizer toker;
   toker.openFile(&stream);

   parseMap(&toker);

   for (U32 i = 0; i < mBrushes.size(); i++)
      mBrushes[i]->processBrush();

   // Assign IDs
   for (U32 i = 0; i < mBrushes.size(); i++)
      mBrushes[i]->mID = getNextBrushID();

  	for (U32 i = 0; i < mBrushes.size(); i++)
   {
      if (mBrushes[i]->mStatus != ConvexBrush::Good)
         Con::errorf(mBrushes[i]->mDebugInfo);
   }

   return true;
}

bool InteriorMapResource::write(Stream& stream)
{
   char buffer[10240];

   dSprintf(buffer, 10240, "// This map has been written by the Torque Constructor\r\n");
   stream.write(dStrlen(buffer), buffer);

   dSprintf(buffer, 10240, "// For more information see http://www.garagegames.com\r\n");
   stream.write(dStrlen(buffer), buffer);

   stream.write(2, "\r\n");

   // Start the worldspawn dump
   dSprintf(buffer, 10240, "{\r\n");
   stream.write(dStrlen(buffer), buffer);

   // Write out the properties of worldspawn
   if (mWorldSpawn)
   {
      for (U32 i = 0; i < mWorldSpawn->properties.size(); i++)
      {
         dSprintf(buffer, 10240, "   \"%s\" \"%s\"\r\n", mWorldSpawn->properties[i].name, mWorldSpawn->properties[i].value);
         stream.write(dStrlen(buffer), buffer);
      }
   }
   else // Write out some default properties
   {
      dSprintf(buffer, 10240, "   \"classname\" \"worldspawn\"\r\n");
      stream.write(dStrlen(buffer), buffer);

      dSprintf(buffer, 10240, "   \"detail_number\" \"0\"\r\n");
      stream.write(dStrlen(buffer), buffer);

      dSprintf(buffer, 10240, "   \"min_pixels\" \"250\"\r\n");
      stream.write(dStrlen(buffer), buffer);

      dSprintf(buffer, 10240, "   \"geometry_scale\" \"32.0\"\r\n");
      stream.write(dStrlen(buffer), buffer);

      dSprintf(buffer, 10240, "   \"light_geometry_scale\" \"32.0\"\r\n");
      stream.write(dStrlen(buffer), buffer);

      dSprintf(buffer, 10240, "   \"ambient_color\" \"0 0 0\"\r\n");
      stream.write(dStrlen(buffer), buffer);

      dSprintf(buffer, 10240, "   \"emergency_ambient_color\" \"0 0 0\"\r\n");
      stream.write(dStrlen(buffer), buffer);

      dSprintf(buffer, 10240, "   \"mapversion\" \"220\"\r\n");
      stream.write(dStrlen(buffer), buffer);
   }

   // Dump the structural brushes into the worldspawn
   for (U32 i = 0; i < mBrushes.size(); i++)
   {
      if (mBrushes[i]->mType != Structural)
         continue;

      // Don't write any deleted brushes
      if (mBrushes[i]->mStatus == ConvexBrush::Deleted)
         continue;

      writeBrush(i, stream);
   }

   // Finish up the worldspawn dump
   dSprintf(buffer, 10240, "}\r\n");
   stream.write(dStrlen(buffer), buffer);

   // Dump the rest of the entities
   for (U32 i = 0; i < mEntities.size(); i++)
   {
      if (dStricmp("worldspawn", mEntities[i]->classname) == 0)
         continue;

      // Begin the entity dump
      dSprintf(buffer, 10240, "{\r\n");
      stream.write(dStrlen(buffer), buffer);

      // Write out the entity properties
      for (U32 j = 0; j < mEntities[i]->properties.size(); j++)
      {
         dSprintf(buffer, 10240, "   \"%s\" \"%s\"\r\n", mEntities[i]->properties[j].name, mEntities[i]->properties[j].value);
         stream.write(dStrlen(buffer), buffer);
      }

      // Dump any brushes that belong to this entity
      for (U32 j = 0; j < mBrushes.size(); j++)
      {
         // We already dumped the Structural brushes with worldspawn
         if (mBrushes[j]->mType == Structural)
            continue;

         // Skip any that don't belong to this entity
         if (mBrushes[j]->mOwner != mEntities[i])
            continue;

         // Don't write any deleted brushes
         if (mBrushes[j]->mStatus == ConvexBrush::Deleted)
            continue;

         writeBrush(j, stream);
      }

      // Finish the entity dump
      dSprintf(buffer, 10240, "}\r\n");
      stream.write(dStrlen(buffer), buffer);
   }

   return true;
}

bool InteriorMapResource::writeBrush(U32 brushIndex, Stream& stream)
{
   char buffer[10240];

   if (mBrushes[brushIndex]->mFaces.mPolyList.size() > 3)
      {
         dSprintf(buffer, 10240, "\r\n   // Brush %d\r\n", brushIndex);
         stream.write(dStrlen(buffer), buffer);

         dSprintf(buffer, 10240, "   {\r\n");
         stream.write(dStrlen(buffer), buffer);

         for (U32 j = 0; j < mBrushes[brushIndex]->mFaces.mPolyList.size(); j++)
         {
            // MDFFIX: Really shouldn't rely on pre-calced points..should generate them from plane
            if (mBrushes[brushIndex]->mFaces.mPolyList[j].vertexCount > 2)
            {
               // MDFFIX: Only testing first 3 verts...should really loop until find a valid triangle
               U32 i0 = mBrushes[brushIndex]->mFaces.mIndexList[mBrushes[brushIndex]->mFaces.mPolyList[j].vertexStart];
               U32 i1 = mBrushes[brushIndex]->mFaces.mIndexList[mBrushes[brushIndex]->mFaces.mPolyList[j].vertexStart + 1];
               U32 i2 = mBrushes[brushIndex]->mFaces.mIndexList[mBrushes[brushIndex]->mFaces.mPolyList[j].vertexStart + 2];

               // Snag the points
               Point3F p0 = mBrushes[brushIndex]->mFaces.mVertexList[i0];
               Point3F p1 = mBrushes[brushIndex]->mFaces.mVertexList[i1];
               Point3F p2 = mBrushes[brushIndex]->mFaces.mVertexList[i2];

               // Multiply by the brush transform
               Point3F tp0, tp1, tp2;
               MatrixF mat = mBrushes[brushIndex]->mTransform;
               mat.mulP(p0, &tp0);
               mat.mulP(p1, &tp1);
               mat.mulP(p2, &tp2);

               // Scale it by our transform scale
               tp0.convolve(mBrushes[brushIndex]->mScale);
               tp1.convolve(mBrushes[brushIndex]->mScale);
               tp2.convolve(mBrushes[brushIndex]->mScale);

               // Now scale it up by the brush scale
               tp0 *= mBrushScale;
               tp1 *= mBrushScale;
               tp2 *= mBrushScale;

               F32 rot = mBrushes[brushIndex]->mTexInfos[j].rot;
               F32 texScale[2];
               F32 texDiv[2];
               PlaneF texGens[2];
               texScale[0] = mBrushes[brushIndex]->mTexInfos[j].scale[0];
               texScale[1] = mBrushes[brushIndex]->mTexInfos[j].scale[1];
               texDiv[0]   = mBrushes[brushIndex]->mTexInfos[j].texDiv[0];
               texDiv[1]   = mBrushes[brushIndex]->mTexInfos[j].texDiv[1];
               texGens[0]  = mBrushes[brushIndex]->mTexInfos[j].texGens[0];
               texGens[1]  = mBrushes[brushIndex]->mTexInfos[j].texGens[1];

               //Con::warnf("preconditioned texGens[0](%g, %g, %g, %g) texGens[1](%g, %g, %g, %g)",
               //            texGens[0].x, texGens[0].y, texGens[0].z, texGens[0].d,
               //            texGens[1].x, texGens[1].y, texGens[1].z, texGens[1].d);

               // Divide out the brushscale
               texGens[0] /= mBrushScale;
               texGens[1] /= mBrushScale;

               // Multiply by our texture sizes
               texGens[0] *= texDiv[0];
               texGens[0].d *= texDiv[0];
               texGens[1] *= texDiv[1];
               texGens[1].d *= texDiv[1];

               // Multiply by texgen scale
               texGens[0] *= texScale[0];
               texGens[1] *= texScale[1];

               // Normalize axii and d
               F32 mag[2];
               mag[0] = 1.0f / texGens[0].len();
               mag[1] = 1.0f / texGens[1].len();

               texGens[0].normalize();
               texGens[1].normalize();

               texGens[0].d *= mag[0];
               texGens[1].d *= mag[1];

               //Con::warnf("conditioned texGens[0](%g, %g, %g, %g) texGens[1](%g, %g, %g, %g)",
               //            texGens[0].x, texGens[0].y, texGens[0].z, texGens[0].d,
               //            texGens[1].x, texGens[1].y, texGens[1].z, texGens[1].d);

               if (validatePlane(p0, p1, p2))
               {
                  // ( -2 4 -158 ) ( -2 -252 -158 ) ( 254 4 -158 ) concrete_1 [ 1.00000 0.00000 0.00000 1.00000 ]  [ 0.00000 -1.00000 0.00000 -2.00000 ]  0  2.00000 -2.00000

                  // First the verts specifying the plane
                  dSprintf(buffer, 10240, "      ( %g %g %g ) ( %g %g %g ) ( %g %g %g )", tp0.x, tp0.y, tp0.z, tp1.x, tp1.y, tp1.z, tp2.x, tp2.y, tp2.z);
                  stream.write(dStrlen(buffer), buffer);

                  // Then the texture
                  dSprintf(buffer, 10240, " %s", mMaterials[mBrushes[brushIndex]->mFaces.mPolyList[j].material]);
                  stream.write(dStrlen(buffer), buffer);

                  // Now the texgens
                  dSprintf(buffer, 10240, " [ %g %g %g %g ]", texGens[0].x, texGens[0].y, texGens[0].z, texGens[0].d);
                  stream.write(dStrlen(buffer), buffer);

                  dSprintf(buffer, 10240, " [ %g %g %g %g ]", texGens[1].x, texGens[1].y, texGens[1].z, texGens[1].d);
                  stream.write(dStrlen(buffer), buffer);

                  // And last the rotation and scaling
                  dSprintf(buffer, 10240, " %g %g %g\r\n", rot, texScale[0], texScale[1]);
                  stream.write(dStrlen(buffer), buffer);
               }
            }
         }

         dSprintf(buffer, 10240, "   }\r\n");
         stream.write(dStrlen(buffer), buffer);
      }
   return true;
}

bool InteriorMapResource::parseMap(Tokenizer* toker)
{
   // Find a worldspawn entity and then find a brush
   toker->reset();

   while (toker->advanceToken(true))
   {
      // Search for the beginning of an entity
      while (toker->getToken()[0] != '{')
      {
         if (!toker->advanceToken(true))
            break;
      }

      // Check to see if we are at the end of the file or at
      // the beginning of an entity
      if (toker->getToken()[0] == '{')
      {
         // Found the start of an entity...parse it
         parseEntity(toker);
      }

      // Unable to support the newest Quake 3 format
      if (mBrushFormat == QuakeNew)
      {
         Con::errorf("The latest Quake 3 format is incompatible with this version of map2dif. Please use an older format.");
         return false;
      }

      // parseEntity *should* return us at the end of the entity - i.e }
      // If it doesn't then loop through till we find the end
      while (toker->getToken()[0] != '}')
      {
         if (!toker->advanceToken(true))
            break;
      }
   }

   //for (U32 i = 0; i < mEntities.size(); i++)
   //{
   //   Con::errorf("Entity[%d] %s", i, mEntities[i]->classname);
   //   for (U32 j = 0; j < mEntities[i]->properties.size(); j++)
   //      Con::printf("   %s %s", mEntities[i]->properties[j].name, mEntities[i]->properties[j].value);
   //}

   // If we don't have a worldspawn entity then create one
   if (!mWorldSpawn)
   {
      // Add a worldspawn entity
      mWorldSpawn = new Entity;
      mEntities.push_back(mWorldSpawn);

      mWorldSpawn->classname = StringTable->insert("worldspawn");

      // Fill in the default properties
      InteriorMapResource::Property prop;

      prop.name = StringTable->insert("classname");
      prop.value = StringTable->insert("worldspawn");
      mWorldSpawn->properties.push_back(prop);

      prop.name = StringTable->insert("detail_number");
      prop.value = StringTable->insert("0");
      mWorldSpawn->properties.push_back(prop);

      prop.name = StringTable->insert("min_pixels");
      prop.value = StringTable->insert("250");
      mWorldSpawn->properties.push_back(prop);

      prop.name = StringTable->insert("geometry_scale");
      prop.value = StringTable->insert("1.0");
      mWorldSpawn->properties.push_back(prop);

      prop.name = StringTable->insert("light_geometry_scale");
      prop.value = StringTable->insert("32.0");
      mWorldSpawn->properties.push_back(prop);

      prop.name = StringTable->insert("ambient_color");
      prop.value = StringTable->insert("0 0 0");
      mWorldSpawn->properties.push_back(prop);

      prop.name = StringTable->insert("emergency_ambient_color");
      prop.value = StringTable->insert("0 0 0");
      mWorldSpawn->properties.push_back(prop);

      prop.name = StringTable->insert("mapversion");
      prop.value = StringTable->insert("220");
      mWorldSpawn->properties.push_back(prop);
   }

   return true;
}

bool InteriorMapResource::parsePatch(Tokenizer* toker)
{
   // For now we aren't parsing patches so just skip to the end of the entity
   U32 bcnt = 1;

   while (bcnt > 0)
   {
      toker->advanceToken(true);

      if (toker->getToken()[0] == '{')
         bcnt++;
      if (toker->getToken()[0] == '}')
         bcnt--;
   }

   return true;
}

bool InteriorMapResource::parseBrush(Tokenizer* toker, BrushType type)
{
   mBrushes.increment();
   mBrushes.last() = new ConvexBrush;
   mBrushes.last()->mType = type;
   mBrushes.last()->mOwner = mEntities.last();

   U32 bcnt = 1;

   while (bcnt > 0)
   {
      toker->advanceToken(true);

      if (toker->getToken()[0] == '{')
         bcnt++;
      if (toker->getToken()[0] == '}')
         bcnt--;

      // Look for the beginning of a brush
      if (toker->getToken()[0] == '(')
         parsePlane(toker);

      // Unable to support the newest Quake 3 format
      if (mBrushFormat == QuakeNew)
         return false;
   }

   return true;
}

bool InteriorMapResource::parseEntity(Tokenizer* toker)
{
   // In the .map format entities have this format:
   // {
   //    "property1 name" "property1 value"
   //    "property2 name" "property2 value"
   //    ...
   //    { // brush1 (optional - has to have at least 4 planes to be valid)
   //       <brush plane1>
   //       <brush plane2>
   //       <brush plane3>
   //       <brush plane4>
   //       ...
   //    }
   //    { // brush2 (optional - has to have at least 4 planes to be valid)
   //       <brush plane1>
   //       <brush plane2>
   //       <brush plane3>
   //       <brush plane4>
   //       ...
   //    }
   //    ...
   //    { // patch1 (optional - bezier patches in the q3 format)
   //       patchDef2
   //       {
   //          <texture/shader>
   //          ( dimensions )
   //          (
   //             ( <control point1> )
   //             ( <control point2> )
   //             ...
   //          )
   //       }
   //    }
   //    { // patch2 (optional - bezier patches in the q3 format)
   //       patchDef2
   //       {
   //          <texture/shader>
   //          ( dimensions )
   //          (
   //             ( <control point1> )
   //             ( <control point2> )
   //             ...
   //          )
   //       }
   //    }
   //    ...
   // }

   // Sanity check
   if (toker->getToken()[0] != '{')
   {
      Con::errorf("InteriorMapResource::parseEntity() not at the beginning of an entity - token is %s line %d", toker->getToken(), toker->getCurrentLine());
      return false;
   }

   // Add an entity to the entities list
   mEntities.increment();
   mEntities.last() = new Entity;

   Entity* ent = mEntities.last();
   ent->classname = StringTable->insert("unknown");

   // We are at brace level 1
   U32 bcnt = 1;

   // Loop through till we hit the ending brace
   while (bcnt > 0)
   {
      toker->advanceToken(true);

      if (toker->endOfFile())
         return true;

      //const char* tok = toker->getToken();

      if (toker->getToken()[0] == '{')
         bcnt++;
      if (toker->getToken()[0] == '}')
         bcnt--;

      // Properties are on the first brace level
      if (bcnt == 1 && toker->getToken()[0] != '}')
      {
         ent->properties.increment();
         ent->properties.last().name = StringTable->insert(toker->getToken());

         toker->advanceToken(false);

         ent->properties.last().value = StringTable->insert(toker->getToken());

         // Store the classname if we find it (just makes it easier to look up entities
         if (dStricmp(ent->properties.last().name, "classname") == 0)
            ent->classname = ent->properties.last().value;
      }
      else if (bcnt == 2 && toker->getToken()[0] == '{')  // We have found either a brush or a patch
      {
         // Determine whether this is a patch or a brush
         int sub = -1;

         toker->advanceToken(true);

         if (toker->tokenICmp("patchDef2"))
            sub = 0;
         else if (toker->tokenICmp("brushDef") || toker->getToken()[0] == '(')
            sub = 1;
         else
            Con::errorf("InteriorMapResource::parseEntity() unknown brush entity - token is %s line %d", toker->getToken(), toker->getCurrentLine());

         // Regress to the beginning of the subobject
         toker->regressToken(true);

         // Sanity check
         if (toker->getToken()[0] != '{')
            Con::errorf("InteriorMapResource::parseEntity() failed to return to the beginning of a subobject - token is %s line %d", toker->getToken(), toker->getCurrentLine());
         else
         {
            if (sub == 0)
               parsePatch(toker);
            else if (dStricmp(ent->classname, "worldspawn") == 0)
               parseBrush(toker, Structural);
            else if (dStricmp(ent->classname, "detail") == 0)
               parseBrush(toker, Detail);
            else if (dStricmp(ent->classname, "collision") == 0)
               parseBrush(toker, Collision);
            else if (dStricmp(ent->classname, "portal") == 0)
               parseBrush(toker, Portal);
            else if (dStricmp(ent->classname, "trigger") == 0)
               parseBrush(toker, Trigger);
            else
               Con::errorf("InteriorMapResource::parseEntity() unknown entity %s with brushes", ent->classname);

            // Unable to support the newest Quake 3 format
            if (mBrushFormat == QuakeNew)
               return false;

            // Loop to the end of the subobject
            while (toker->getToken()[0] != '}' && !toker->endOfFile())
               toker->advanceToken(true);

            // Should return at the end of the subobject
            if (toker->getToken()[0] == '}')
               bcnt--;
            else
            {
               Con::errorf("InteriorMapResource::parseEntity() failure to parse to the end of a subobject");
               Con::errorf("InteriorMapResource::parseEntity() entity is %s token is %s line %d", ent->classname, toker->getToken(), toker->getCurrentLine());
            }
         }
      }
   }

   // See if this entiry is our worldspawn
   if (dStricmp(ent->classname, "worldspawn") == 0)
   {
      mWorldSpawn = ent;

      // See if we have a brush scale
      char* scale = mWorldSpawn->getValue("geometry_scale");
      if (scale)
      mBrushScale = dAtof(scale);
   }

   // Sanity check
   if (toker->getToken()[0] != '}')
   {
      Con::errorf("InteriorMapResource::parseEntity() not at the end of an entity - token is %s line %d", toker->getToken(), toker->getCurrentLine());
      return false;
   }

   return true;
}

bool InteriorMapResource::validatePlane(Point3F k, Point3F j, Point3F l)
{
   F32 ax = k.x-j.x;
   F32 ay = k.y-j.y;
   F32 az = k.z-j.z;
   F32 bx = l.x-j.x;
   F32 by = l.y-j.y;
   F32 bz = l.z-j.z;
   F32 x = ay*bz - az*by;
   F32 y = az*bx - ax*bz;
   F32 z = ax*by - ay*bx;
   F32 squared = x*x + y*y + z*z;

   if (squared == 0.0f)
      return false;
   else
      return true;
}

bool InteriorMapResource::parsePlane(Tokenizer* toker)
{
   // We start at the first (
   F32 x, y, z;
   Point3F p0, p1, p2;

   // All of the formats share this in common
   toker->advanceToken(false);
   x = dAtof(toker->getToken());
   toker->advanceToken(false);
   y = dAtof(toker->getToken());
   toker->advanceToken(false);
   z = dAtof(toker->getToken());

   p0 = Point3F(x, y, z);

   // Brings us to end of the first ()
   toker->advanceToken(false);

   // Brings us to the beginning of the second ()
   toker->advanceToken(true);

   toker->advanceToken(false);
   x = dAtof(toker->getToken());
   toker->advanceToken(false);
   y = dAtof(toker->getToken());
   toker->advanceToken(false);
   z = dAtof(toker->getToken());

   p1 = Point3F(x, y, z);

   // Brings us to end of the second ()
   toker->advanceToken(false);

   // Brings us to the beginning of the third ()
   toker->advanceToken(true);

   toker->advanceToken(false);
   x = dAtof(toker->getToken());
   toker->advanceToken(false);
   y = dAtof(toker->getToken());
   toker->advanceToken(false);
   z = dAtof(toker->getToken());

   p2 = Point3F(x, y, z);

   // Brings us to end of the third ()
   toker->advanceToken(false);

   // So now we have enough to calculate our plane
   PlaneF normal;

   // Check to make sure that none of the points are the same
   if (!validatePlane(p0, p1, p2))
   {
      normal.set(1.0f, 0.0f, 0.0f);
      mBrushes.last()->mStatus = ConvexBrush::Malformed;
      mBrushes.last()->mDebugInfo = StringTable->insert("This brush was exported improperly with badly formed plane points. Please validate the brushes in your modelling tool before export.");
   }
   else
      normal.set(p0, p1, p2);

   // Here is where things start to split up by format
   // If we find a ( here then the map is in the newest
   // Quake 3 format. Otherwise we are in Quake/Valve format
   // which further splits

   // This the data that needs to be returned
   U32 texIndex;
   PlaneF texGens[2];
   F32 scale[2] = {1.0f, 1.0f };

   // If we don't know what format we are using see if it is
   // the newest Quake 3 format.
   if (mBrushFormat == Unknown)
   {
      toker->advanceToken(false);

      if (toker->getToken()[0] == '(')
         mBrushFormat = QuakeNew;

      // Move back to the beginning of the texgen fields
      toker->regressToken(true);
   }

   if (mBrushFormat == QuakeNew)
   {
      // The newest Quake 3 format is not supported at this time
      // b/c its texgens are incompatible with map2dif's
      return false;

      //parseQuakeNew(toker, texIndex, texGens);
   }
   else
      parseQuakeValve(toker, normal, texIndex, texGens, scale);

   // Now that we have our data add it to the brush
   mBrushes.last()->addFace(normal, texGens, scale, texIndex);

   // ( 64 -64 -64 ) ( -64 -64 -64 ) ( -64 -64 64 ) WALL_PANEL01 [ 1 0 0 64 ] [ 0 0 -1 64 ] 0 1 1
   //Con::warnf("( %g %g %g ) ( %g %g %g ) ( %g %g %g ) %s [ %g %g %g %g ] [ %g %g %g %g ] 0 %g %g",
   //               p0.x, p0.y, p0.z,
   //               p1.x, p1.y, p1.z,
   //               p2.x, p2.y, p2.z,
   //               mMaterials[texIndex],
   //               texGens[0].x, texGens[0].y, texGens[0].z, texGens[0].d,
   //               texGens[1].x, texGens[1].y, texGens[1].z, texGens[1].d,
   //               scale[0], scale[1]);

   return true;
}

bool InteriorMapResource::parseQuakeValve(Tokenizer* toker, VectorF normal, U32& tdx, PlaneF* texGens, F32* scale)
{
   // Brings us to the texture name
   toker->advanceToken(false);

   tdx = addTexture((char*)toker->getToken());

   // Ok here is where things start to differ between the formats
   // If we haven't determined the format yet then check for a '['
   if (mBrushFormat == Unknown)
   {
      toker->advanceToken(false);

      if (toker->getToken()[0] == '[')
         mBrushFormat = Valve220;
      else
         mBrushFormat = QuakeOld;

      // Move back to the beginning of the texgen fields
      toker->regressToken(true);
   }

   if (mBrushFormat == Valve220)
      parseValve220TexGens(toker, texGens, scale);
   else if (mBrushFormat == QuakeOld)
      parseQuakeTexGens(toker, normal, texGens, scale);

   return true;
}

bool InteriorMapResource::parseQuakeNew(Tokenizer* toker, U32& tdx, PlaneF* texGens, F32* scale)
{
   // Brings us to the beginning of the outer ()
   toker->advanceToken(false);

   // Brings us to the beginning of the next ()
   toker->advanceToken(false);

   // MDFFIX:: figure out what these are
   toker->advanceToken(false);
   toker->advanceToken(false);
   toker->advanceToken(false);

   // Brings us to the end of ()
   toker->advanceToken(false);

   // Brings us to the beginning of the next ()
   toker->advanceToken(false);

   // MDFFIX:: figure out what these are
   toker->advanceToken(false);
   toker->advanceToken(false);
   toker->advanceToken(false);

   // Brings us to the end of ()
   toker->advanceToken(false);

   // Brings us to the end of the outer ()
   toker->advanceToken(false);

   // Brings us to the texture
   toker->advanceToken(false);

   tdx = addTexture((char*)toker->getToken());

   // MDFFIX:: figure out what these are
   toker->advanceToken(false);
   toker->advanceToken(false);
   toker->advanceToken(false);

   return true;
}

bool InteriorMapResource::parseValve220TexGens(Tokenizer* toker, PlaneF* texGens, F32* scale)
{
   // Brings us to the opening of the first []
   toker->advanceToken(false);

   toker->advanceToken(false);
   texGens[0].x = dAtof(toker->getToken());
   toker->advanceToken(false);
   texGens[0].y = dAtof(toker->getToken());
   toker->advanceToken(false);
   texGens[0].z = dAtof(toker->getToken());
   toker->advanceToken(false);
   texGens[0].d = dAtof(toker->getToken());

   // Takes us to the close of the first []
   toker->advanceToken(false);
   // Brings us to the opening of the second []
   toker->advanceToken(false);

   toker->advanceToken(false);
   texGens[1].x = dAtof(toker->getToken());
   toker->advanceToken(false);
   texGens[1].y = dAtof(toker->getToken());
   toker->advanceToken(false);
   texGens[1].z = dAtof(toker->getToken());
   toker->advanceToken(false);
   texGens[1].d = dAtof(toker->getToken());

   // Takes us to the close of the second []
   toker->advanceToken(false);

   // Skip rotation
   toker->advanceToken(false);

   // Scale
   toker->advanceToken(false);
   scale[0] = dAtof(toker->getToken());
   toker->advanceToken(false);
   scale[1] = dAtof(toker->getToken());

   texGens[0].x /= scale[0];
   texGens[0].y /= scale[0];
   texGens[0].z /= scale[0];

   texGens[1].x /= scale[1];
   texGens[1].y /= scale[1];
   texGens[1].z /= scale[1];

   return true;
}

F32	baseaxis[18][3] =
{
   {0,0,1}, {1,0,0}, {0,-1,0},			// floor
   {0,0,-1}, {1,0,0}, {0,-1,0},		// ceiling
   {1,0,0}, {0,1,0}, {0,0,-1},			// west wall
   {-1,0,0}, {0,1,0}, {0,0,-1},		// east wall
   {0,1,0}, {1,0,0}, {0,0,-1},			// south wall
   {0,-1,0}, {1,0,0}, {0,0,-1}			// north wall
};

void textureAxisFromPlane(const VectorF& normal, F32* xv, F32* yv)
{
   int		bestaxis;
   F32		dot,best;
   int		i;
	
   best = 0;
   bestaxis = 0;
	
   for (i=0 ; i<6 ; i++) {
      dot = mDot (normal, VectorF (baseaxis[i*3][0], baseaxis[i*3][1], baseaxis[i*3][2]));
      if (dot > best)
      {
         best = dot;
         bestaxis = i;
      }
   }
	
   xv[0] = baseaxis [bestaxis * 3 + 1][0];
   xv[1] = baseaxis [bestaxis * 3 + 1][1];
   xv[2] = baseaxis [bestaxis * 3 + 1][2];
   yv[0] = baseaxis [bestaxis * 3 + 2][0];
   yv[1] = baseaxis [bestaxis * 3 + 2][1];
   yv[2] = baseaxis [bestaxis * 3 + 2][2];
}

void quakeTextureVecs(const VectorF& normal,
                                    F32 offsetX,
                                    F32 offsetY,
                                    F32 rotate,
                                    F32 scaleX,
                                    F32 scaleY,
                                    PlaneF* u,
                                    PlaneF *v)
{
   F32   vecs[2][3];
   int   sv, tv;
   F32   ang, sinv, cosv;
   F32   ns, nt;
   int   i, j;

   textureAxisFromPlane(normal, vecs[0], vecs[1]);

   ang = rotate / 180 * M_PI;
   sinv = sin(ang);
   cosv = cos(ang);

   if (vecs[0][0] != 0.0)
      sv = 0;
   else if (vecs[0][1] != 0.0)
      sv = 1;
   else
      sv = 2;
				
   if (vecs[1][0] != 0.0)
      tv = 0;
   else if (vecs[1][1] != 0.0)
      tv = 1;
   else
      tv = 2;
					
   for (i=0 ; i<2 ; i++) {
      ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
      nt = sinv * vecs[i][sv] +  cosv * vecs[i][tv];
      vecs[i][sv] = ns;
      vecs[i][tv] = nt;
   }

   u->x = vecs[0][0] / scaleX;
   u->y = vecs[0][1] / scaleX;
   u->z = vecs[0][2] / scaleX;
   u->d = offsetX;

   v->x = vecs[1][0] / scaleY;
   v->y = vecs[1][1] / scaleY;
   v->z = vecs[1][2] / scaleY;
   v->d = offsetY;
}

bool InteriorMapResource::parseQuakeTexGens(Tokenizer* toker, VectorF normal, PlaneF* texGens, F32* scale)
{
   toker->advanceToken(false);
   F32 shiftU = dAtof(toker->getToken());
   toker->advanceToken(false);
   F32 shiftV = dAtof(toker->getToken());

   toker->advanceToken(false);
   F32 rot = dAtof(toker->getToken());

   toker->advanceToken(false);
   scale[0] = dAtof(toker->getToken());
   toker->advanceToken(false);
   scale[1] = dAtof(toker->getToken());

   // Make sure the normal is normalized
   normal.normalize();

   quakeTextureVecs (normal, shiftU, shiftV, rot, scale[0], scale[1], &texGens[0], &texGens[1]);

   return true;
}

U32 InteriorMapResource::addTexture(char* texture)
{
   S32 idx = getTextureIndex(texture);

   if (idx == -1)
   {
      idx = mMaterials.size();
      mMaterials.increment();
      mMaterials.last() = StringTable->insert(texture);
   }

   return idx;
}

S32 InteriorMapResource::getTextureIndex(char* texture)
{
   S32 idx = -1;

   for (U32 i = 0; i < mMaterials.size(); i++)
   {
      if (dStricmp(texture, mMaterials[i]) == 0)
      {
         idx = i;
         break;
      }
   }

   return idx;
}

char* InteriorMapResource::getTextureName(U32 texIdx)
{
   if (mMaterials.size() == 0 || texIdx > mMaterials.size() - 1)
   {
      Con::warnf("InteriorMapResource::getTextureName(): texture index is outside of range");

      return "error";
   }

   return (char*)mMaterials[texIdx];
}

ResourceInstance* constructInteriorMAP(Stream& stream)
{
   InteriorMapResource* pResource = new InteriorMapResource;

   if (pResource->read(stream) == true)
      return pResource;
   else
   {
      delete pResource;
      return NULL;
   }
}