//-----------------------------------------------------------------------------------
//
//   Torque Network Library - ZAP example multiplayer vector graphics space game
//   Copyright (C) 2004 GarageGames.com, Inc.
//   For more information see http://www.opentnl.org
//
//   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 2 of the License, or
//   (at your option) any later version.
//
//   For use in products that are not compatible with the terms of the GNU 
//   General Public License, alternative licensing options are available 
//   from GarageGames.com.
//
//   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, write to the Free Software
//   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//------------------------------------------------------------------------------------

#include "UIEditor.h"
#include "glutInclude.h"
#include "UICredits.h"
#include <stdio.h>
#include <ctype.h>
#include "gameObjectRender.h"
#include "barrier.h"

namespace Zap
{

bool EditorUserInterface::editorEnabled = false;

EditorUserInterface gEditorUserInterface;

void EditorUserInterface::setEditName(const char *name)
{
   mGridSize = Game::DefaultGridSize;
   mGameType[0] = 0;
   char fileBuffer[256];
   dSprintf(fileBuffer, sizeof(fileBuffer), "levels/%s", name);
   initLevelFromFile(fileBuffer);
   if(mTeams.size() == 0)
   {
      Team t;
      t.color = Color(0, 0, 1);
      strcpy(t.name, "Blue");
      mTeams.push_back(t);
   }
   mCurrentOffset.set(0,0);
   mCurrentScale = 100;
   mDragSelecting = false;
   mUp = mDown = mLeft = mRight = mIn = mOut = false;
   mCreatingPoly = false;
   mCurrentTeam = 0;

   strcpy(mEditFileName, name);
   mOriginalItems = mItems;
}

struct GameItemRec
{
   const char *name;
   bool hasWidth;
   bool hasTeam;
   bool canHaveNoTeam;
   bool isPoly;
   char letter;
};

enum GameItems
{
   ItemSpawn,
   ItemSoccerBall,
   ItemCTFFlag,
   ItemBarrierMaker,
   ItemTeleporter,
   ItemRepair,
   ItemTest,
   ItemResource,
   ItemLoadoutZone,
   ItemTurret,
   ItemForceField,
   ItemGoalZone,
};

GameItemRec gGameItemRecs[] = {
   { "Spawn", false, true, false, false, 'S' },
   { "SoccerBallItem", false, false, false, false, 'B' },
   { "FlagItem", false, true, true, false, 0 },
   { "BarrierMaker", true, false, false, true, 0 },
   { "Teleporter", false, false, false, true, 'T' },
   { "RepairItem", false, false, false, false, 'R' },
   { "TestItem", false, false, false, false, 't' },
   { "ResourceItem", false, false, false, false, 'r' },
   { "LoadoutZone", false, true, true, true, 0 },
   { "Turret", false, true, true, false, 'N' },
   { "ForceFieldProjector", false, true, true, false, 'P' },
   { "GoalZone", false, true, false, true, 0 },
   { NULL, false, false, false, false, 0 },
};

void EditorUserInterface::processLevelLoadLine(int argc, const char **argv)
{
   U32 index;
   U32 strlenCmd = (U32) strlen(argv[0]);
   for(index = 0; gGameItemRecs[index].name != NULL; index++)
   {
      if(!strcmp(argv[0], gGameItemRecs[index].name))
      {
         S32 minArgs = 3;
         if(gGameItemRecs[index].isPoly)
            minArgs += 2;
         if(gGameItemRecs[index].hasTeam)
            minArgs++;
         if(argc >= minArgs)
            break;
      }
   }

   if(gGameItemRecs[index].name)
   {
      WorldItem i;
      i.index = index;
      S32 arg = 1;
      i.team = -1;
      i.selected = false;
      i.width = 0;
      if(gGameItemRecs[index].hasWidth)
      {
         i.width = atof(argv[arg]);
         arg++;
      }
      if(gGameItemRecs[index].hasTeam)
      {
         i.team = atoi(argv[arg]);
         arg++;
      }
      for(;arg < argc; arg += 2)
      {
         Point p;
         if(arg != argc - 1)
         {
            p.read(argv + arg);
            i.verts.push_back(p);
            i.vertSelected.push_back(false);
         }
      }
      mItems.push_back(i);
   }
   else if(strlenCmd >= 8 && !strcmp(argv[0] + strlenCmd - 8, "GameType"))
   {
      strcpy(mGameType, argv[0]);
      mGameType[strlenCmd - 8] = 0;
      for(S32 i = 1; i < argc; i++)
         mGameTypeArgs.push_back(strdup(argv[i]));
   }
   else if(!strcmp(argv[0], "Team") && argc == 5)
   {
      Team t;
      strcpy(t.name, argv[1]);
      t.color.read(argv + 2);
      mTeams.push_back(t);
   }
   else
   {
      if(!strcmp(argv[0], "GridSize") && argc == 2)
         mGridSize = atof(argv[1]);

      Vector<const char *> item;
      for(S32 i = 0; i < argc; i++)
         item.push_back(strdup(argv[i]));
      mUnknownItems.push_back(item);
   }
}

void EditorUserInterface::onActivate()
{
   editorEnabled = true;
}

Point EditorUserInterface::snapToLevelGrid(Point p)
{
   F32 mulFactor, divFactor;
   if(mCurrentScale >= 100)
   {
      mulFactor = 10;
      divFactor = 0.1;
   }
   else
   {
      mulFactor = 2;
      divFactor = 0.5;
   }

   return Point(floor(p.x * mulFactor + 0.5) * divFactor, 
                floor(p.y * mulFactor + 0.5) * divFactor);
}

void EditorUserInterface::render()
{
   glColor3f(0.0, 0.0, 0.0);
   if(mCurrentScale >= 100)
   {
      F32 gridScale = mCurrentScale * 0.1;
      F32 yStart = fmod(mCurrentOffset.y, gridScale);
      F32 xStart = fmod(mCurrentOffset.x, gridScale);
      glColor3f(0.2, 0.2, 0.2);
      glBegin(GL_LINES);
      while(yStart < canvasHeight)
      {
         glVertex2f(0, yStart);
         glVertex2f(canvasWidth, yStart);
         yStart += gridScale;
      }
      while(xStart < canvasWidth)
      {
         glVertex2f(xStart, 0);
         glVertex2f(xStart, canvasHeight);
         xStart += gridScale;
      }
      glEnd();
   }

   if(mCurrentScale >= 10)
   {
      F32 yStart = fmod(mCurrentOffset.y, mCurrentScale);
      F32 xStart = fmod(mCurrentOffset.x, mCurrentScale);
      glColor3f(0.4, 0.4, 0.4);
      glBegin(GL_LINES);
      while(yStart < canvasHeight)
      {
         glVertex2f(0, yStart);
         glVertex2f(canvasWidth, yStart);
         yStart += mCurrentScale;
      }
      while(xStart < canvasWidth)
      {
         glVertex2f(xStart, 0);
         glVertex2f(xStart, canvasHeight);
         xStart += mCurrentScale;
      }
      glEnd();
   }
   glColor3f(0.7, 0.7, 0.7);
   glLineWidth(3);
   Point origin = convertLevelToCanvasCoord(Point(0,0));
   glBegin(GL_LINES );
   glVertex2f(0, origin.y);
   glVertex2f(canvasWidth, origin.y);
   glVertex2f(origin.x, 0);
   glVertex2f(origin.x, canvasHeight);
   glEnd();
   glLineWidth(1);

   for(S32 i = 0; i < mItems.size(); i++)
      renderItem(i);

   if(mCreatingPoly)
   {
      Point mouseVertex = snapToLevelGrid(convertCanvasToLevelCoord(mMousePos));
      mNewItem.verts.push_back(mouseVertex);
      glLineWidth(3);
      glColor3f(1, 1, 0);
      renderPoly(mNewItem);
      glLineWidth(1);
      mNewItem.verts.erase_fast(mNewItem.verts.size() - 1);
   }
   else
   {
      for(S32 i = 0; i < mItems.size(); i++)
      {
         if(mItems[i].selected && mItems[i].index == ItemBarrierMaker)
         {
            glColor3f(1,1,1);
            drawStringf(680, 5, 20, "Width: %g", mItems[i].width);
            break;
         }
      }
   }
   if(mDragSelecting)
   {
      glColor3f(1,1,1);
      glBegin(GL_LINE_LOOP);
      Point downPos = convertLevelToCanvasCoord(mMouseDownPos);
      glVertex2f(downPos.x, downPos.y);
      glVertex2f(mMousePos.x, downPos.y);
      glVertex2f(mMousePos.x, mMousePos.y);
      glVertex2f(downPos.x, mMousePos.y);
      glEnd();
   }
}

void EditorUserInterface::renderPoly(WorldItem &p)
{
   glBegin(GL_LINE_STRIP);
   for(S32 j = 0; j < p.verts.size(); j++)
   {
      Point v = convertLevelToCanvasCoord(p.verts[j]);
      glVertex2f(v.x, v.y);
   }
   glEnd();
}

extern void constructBarrierPoints(const Vector<Point> &vec, F32 width, Vector<Point> &barrierEnds );

void EditorUserInterface::renderItem(S32 index)
{
   EditorUserInterface::WorldItem &i = mItems[index];
   Point pos = convertLevelToCanvasCoord(i.verts[0]);
   Color c;

   if(i.index == ItemTeleporter)
   {
      Point dest = convertLevelToCanvasCoord(i.verts[1]);
      glColor3f(0,1,0);

      if(i.selected)
         glLineWidth(3);

      glBegin(GL_POLYGON);
      glVertex2f(pos.x - 5, pos.y - 5);
      glVertex2f(pos.x + 5, pos.y - 5);
      glVertex2f(pos.x + 5, pos.y + 5);
      glVertex2f(pos.x - 5, pos.y + 5);
      glEnd();

      glBegin(GL_LINES);
      glVertex2f(pos.x, pos.y);
      glVertex2f(dest.x, dest.y);
      glVertex2f(dest.x - 5, dest.y - 5);
      glVertex2f(dest.x + 5, dest.y + 5);
      glVertex2f(dest.x + 5, dest.y - 5);
      glVertex2f(dest.x - 5, dest.y + 5);
      glEnd();

      glLineWidth(1);

      glColor3f(1,1,1);
      if(i.vertSelected[0])
      {
         glBegin(GL_LINE_LOOP);
         glVertex2f(pos.x - 5, pos.y - 5);
         glVertex2f(pos.x + 5, pos.y - 5);
         glVertex2f(pos.x + 5, pos.y + 5);
         glVertex2f(pos.x - 5, pos.y + 5);
         glEnd();
      }
      if(i.vertSelected[1])
      {
         glBegin(GL_LINE_LOOP);
         glVertex2f(dest.x - 5, dest.y - 5);
         glVertex2f(dest.x + 5, dest.y - 5);
         glVertex2f(dest.x + 5, dest.y + 5);
         glVertex2f(dest.x - 5, dest.y + 5);
         glEnd();
      }
   }
   else if(gGameItemRecs[i.index].isPoly)
   {
      Color theColor;
      if(i.selected)
         theColor.set(1,1,0);
      else if(i.index == ItemBarrierMaker)
         theColor.set(0,0,1);
      else if(i.team == -1)
         theColor.set(0.7, 0.7, 0.7);
      else
      {
         Color c = mTeams[i.team].color;
         theColor = c;
      }
      glColor(theColor);

      if(i.index != ItemBarrierMaker)
      {
         // render the team ones as GL_POLYGONs
         glBegin(GL_POLYGON);
         for(S32 j = 0; j < i.verts.size(); j++)
         {
            Point v = convertLevelToCanvasCoord(i.verts[j]);
            glVertex2f(v.x, v.y);
         }
         glEnd();
      }
      else
      {
         Vector<Point> barPoints;
         constructBarrierPoints(i.verts, i.width / mGridSize, barPoints );

         for(S32 j = 0; j < barPoints.size(); j += 2)
         {
            Point dir = barPoints[j+1] - barPoints[j];
            Point crossVec(dir.y, -dir.x);
            crossVec.normalize(i.width * 0.5 / mGridSize);
            glBegin(GL_POLYGON);
            glColor3f(0.5, 0.5, 1);
            glVertex(convertLevelToCanvasCoord(barPoints[j] + crossVec));
            glVertex(convertLevelToCanvasCoord(barPoints[j] - crossVec));
            glVertex(convertLevelToCanvasCoord(barPoints[j+1] - crossVec));
            glVertex(convertLevelToCanvasCoord(barPoints[j+1] + crossVec));
            glEnd();
         }
         glColor(theColor);
         glLineWidth(3);
         renderPoly(i);
         glLineWidth(1);
      }
      for(S32 j = 0; j < i.verts.size(); j++)
      {
         Point v = convertLevelToCanvasCoord(i.verts[j]);
         if(i.vertSelected[j])
            glColor3f(1,1,0);
         else
            glColor3f(1,0,0);
         glBegin(GL_LINE_LOOP);
         glVertex2f(v.x - 5, v.y - 5);
         glVertex2f(v.x + 5, v.y - 5);
         glVertex2f(v.x + 5, v.y + 5);
         glVertex2f(v.x - 5, v.y + 5);
         glEnd();
      }
   }
   else
   {
      char letter = gGameItemRecs[i.index].letter;
      if(i.team == -1)
         c = Color(0.5, 0.5, 0.5);
      else
         c = mTeams[i.team].color;
      if(i.index == ItemCTFFlag)
      {
         glPushMatrix();
         glTranslatef(pos.x, pos.y, 0);
         glScalef(0.6, 0.6, 1);
         renderFlag(Point(0,0), c);
         glPopMatrix();
      }
      else
      {
         glColor(c);
         glBegin(GL_POLYGON);
         glVertex2f(pos.x - 8, pos.y - 8);
         glVertex2f(pos.x + 8, pos.y - 8);
         glVertex2f(pos.x + 8, pos.y + 8);
         glVertex2f(pos.x - 8, pos.y + 8);
         glEnd();
      }
      if(i.selected)
      {
         Point pos = convertLevelToCanvasCoord(i.verts[0]);
         glColor3f(1,1,1);
         glBegin(GL_LINE_LOOP);
         glVertex2f(pos.x - 10, pos.y - 10);
         glVertex2f(pos.x + 10, pos.y - 10);
         glVertex2f(pos.x + 10, pos.y + 10);
         glVertex2f(pos.x - 10, pos.y + 10);
         glEnd();
      }
      if(letter)
      {
         glColor3f(1,1,1);
         drawStringf(pos.x - 5, pos.y - 8, 15, "%c", letter);
      }
   }
}

void EditorUserInterface::clearSelection()
{
   for(S32 i = 0; i < mItems.size(); i++)
   {
      WorldItem &itm = mItems[i];
      itm.selected = false;
      for(S32 j = 0; j < itm.vertSelected.size(); j++)
         itm.vertSelected[j] = false;
   }
}

S32 EditorUserInterface::countSelectedItems()
{
   S32 count = 0;
   for(S32 i = 0; i < mItems.size(); i++)
      if(mItems[i].selected)
         count++;
   return count;
}

S32 EditorUserInterface::countSelectedVerts()
{
   S32 count = 0;
   for(S32 i = 0; i < mItems.size(); i++)
      for(S32 j = 0; j < mItems[i].vertSelected.size(); j++)
         if(mItems[i].vertSelected[j])
            count++;
   return count;
}

void EditorUserInterface::duplicateSelection()
{
   mOriginalItems = mItems;

   S32 itemCount = mItems.size();
   for(S32 i = 0; i < itemCount; i++)
   {
      if(mItems[i].selected)
      {
         WorldItem newItem = mItems[i];
         mItems[i].selected = false;
         for(S32 j = 0; j < newItem.verts.size(); j++)
            newItem.verts[j] += Point(0.5, 0.5);
         mItems.push_back(newItem);
      }
   }
}

void EditorUserInterface::rotateSelection(F32 angle)
{
   mOriginalItems = mItems;
   Point min, max;
   computeSelectionMinMax(min,max);
   //Point ctr = (min + max)*0.5;
   //ctr.x = floor(ctr.x * 10) * 0.1f;
   //ctr.y = floor(ctr.y * 10) * 0.1f;
   F32 sinTheta = sin(angle * Float2Pi / 360.0f);
   F32 cosTheta = cos(angle * Float2Pi / 360.0f);
   for(S32 i = 0; i < mItems.size(); i++)
   {
      if(!mItems[i].selected)
         continue;
      WorldItem &itm = mItems[i];
      for(S32 j = 0; j < itm.verts.size(); j++)
      {
         Point v = itm.verts[j];// - ctr;
         Point n(v.x * cosTheta + v.y * sinTheta, v.y * cosTheta - v.x * sinTheta);
         itm.verts[j] = n;// + ctr;
      }
   }
}


void EditorUserInterface::computeSelectionMinMax(Point &min, Point &max)
{
   min.set(1000000, 1000000);
   max.set(-1000000, -1000000);

   for(S32 i = 0; i < mItems.size(); i++)
   {
      if(!mItems[i].selected)
         continue;
      WorldItem &itm = mItems[i];
      for(S32 j = 0; j < itm.verts.size(); j++)
      {
         Point v = itm.verts[j];

         if(v.x < min.x)
            min.x = v.x;
         if(v.x > max.x)
            max.x = v.x;
         if(v.y < min.y)
            min.y = v.y;
         if(v.y > max.y)
            max.y = v.y;
      }
   }
}

void EditorUserInterface::setCurrentTeam(S32 currentTeam)
{
   mCurrentTeam = currentTeam;

   for(S32 i = 0; i < mItems.size(); i++)
   {
      if(!mItems[i].selected)
         continue;
      if(!gGameItemRecs[mItems[i].index].hasTeam)
         continue;
      if(currentTeam == -1 && !gGameItemRecs[mItems[i].index].canHaveNoTeam)
         continue;
      mItems[i].team = mCurrentTeam;
   }
}

void EditorUserInterface::flipSelectionHorizontal()
{
   mOriginalItems = mItems;

   Point min, max;
   computeSelectionMinMax(min, max);
   for(S32 i = 0; i < mItems.size(); i++)
   {
      if(!mItems[i].selected)
         continue;

      for(S32 j = 0; j < mItems[i].verts.size(); j++)
         mItems[i].verts[j].x = min.x + (max.x - mItems[i].verts[j].x);
   }
}

void EditorUserInterface::flipSelectionVertical()
{
   mOriginalItems = mItems;

   Point min, max;
   computeSelectionMinMax(min, max);
   for(S32 i = 0; i < mItems.size(); i++)
   {
      if(!mItems[i].selected)
         continue;

      for(S32 j = 0; j < mItems[i].verts.size(); j++)
         mItems[i].verts[j].y = min.y + (max.y - mItems[i].verts[j].y);
   }
}

void EditorUserInterface::findHitVertex(Point canvasPos, S32 &hitItem, S32 &hitVertex)
{
   hitItem = -1;
   hitVertex = -1;
   for(S32 i = mItems.size() - 1; i >= 0; i--)
   {
      WorldItem &p = mItems[i];
      if(!gGameItemRecs[p.index].isPoly)
         continue;

      for(S32 j = p.verts.size() - 1; j >= 0; j--)
      {
         Point v = convertLevelToCanvasCoord(p.verts[j]);
         if(fabs(v.x - canvasPos.x) < 5 && fabs(v.y - canvasPos.y) < 5)
         {
            hitItem = i;
            hitVertex = j;
            return;
         }
      }
   }
}

void EditorUserInterface::findHitItemAndEdge(Point canvasPos, S32 &hitItem, S32 &hitEdge)
{
   hitItem = -1;
   hitEdge = -1;

   for(S32 i = mItems.size() - 1; i >= 0; i--)
   {
      WorldItem &p = mItems[i];

      if(!gGameItemRecs[mItems[i].index].isPoly)
      {
         Point pos = convertLevelToCanvasCoord(p.verts[0]);
         if(fabs(canvasPos.x - pos.x) < 8 && fabs(canvasPos.y - pos.y) < 8)
         {
            hitItem = i;
            return;
         }
      }

      Point p1 = convertLevelToCanvasCoord(p.verts[0]);
      for(S32 j = 0; j < p.verts.size() - 1; j++)
      {
         Point p2 = convertLevelToCanvasCoord(p.verts[j+1]);

         Point edgeDelta = p2 - p1;
         Point clickDelta = canvasPos - p1;
         float fraction = clickDelta.dot(edgeDelta);
         float lenSquared = edgeDelta.dot(edgeDelta);
         if(fraction > 0 && fraction < lenSquared)
         {
            // compute the closest point:
            Point closest = p1 + edgeDelta * (fraction / lenSquared);
            float distance = (canvasPos - closest).len();
            if(distance < 5)
            {
               hitEdge = j;
               hitItem = i;
               return;
            }
         }
         p1 = p2;
      }
   }
}

void EditorUserInterface::onMouseDown(S32 x, S32 y)
{
   mMousePos = convertWindowToCanvasCoord(Point(x,y));
   if(mCreatingPoly)
   {
      mOriginalItems = mItems;
      if(mNewItem.verts.size() > 1)
         mItems.push_back(mNewItem);
      mNewItem.verts.clear();
      mCreatingPoly = false;
   }

   mMouseDownPos = convertCanvasToLevelCoord(mMousePos);

   // rules for mouse down:
   // if the click has no shift- modifier, then
   //   if the click was on something that was selected
   //     do nothing
   //   else
   //     clear the selection
   //     add what was clicked to the selection
   //  else
   //    toggle the selection of what was clicked

   bool shiftKeyDown = glutGetModifiers() & GLUT_ACTIVE_SHIFT;

   S32 vertexHit, vertexHitPoly;
   S32 edgeHit, itemHit;
   
   findHitVertex(mMousePos, vertexHitPoly, vertexHit);
   findHitItemAndEdge(mMousePos, itemHit, edgeHit);

   if(!shiftKeyDown)
   {
      if(vertexHit != -1 && mItems[vertexHitPoly].selected)
      {
         vertexHit = -1;
         itemHit = vertexHitPoly;
      }
      if(vertexHit != -1 && (itemHit == -1 || !mItems[itemHit].selected))
      {
         if(!mItems[vertexHitPoly].vertSelected[vertexHit])
         {
            clearSelection();
            mItems[vertexHitPoly].vertSelected[vertexHit] = true;
         }
      }
      else if(itemHit != -1)
      {
         if(!mItems[itemHit].selected)
         {
            clearSelection();
            mItems[itemHit].selected = true;
         }
      }
      else
      {
         mDragSelecting = true;
         clearSelection();
      }
   }
   else
   {
      if(vertexHit != -1)
      {
         mItems[vertexHitPoly].vertSelected[vertexHit] = 
            !mItems[vertexHitPoly].vertSelected[vertexHit];
      }
      else if(itemHit != -1)
         mItems[itemHit].selected = !mItems[itemHit].selected;
      else
         mDragSelecting = true;
   }
   mOriginalItems = mItems;
}

void EditorUserInterface::onMouseUp(S32 x, S32 y)
{
   mMousePos = convertWindowToCanvasCoord(Point(x,y));
   if(mDragSelecting)
   {
      Rect r(convertCanvasToLevelCoord(mMousePos),
             mMouseDownPos);
      for(S32 i = 0; i < mItems.size(); i++)
      {
         S32 j;
         for(j = 0; j < mItems[i].verts.size(); j++)
         {
            if(!r.contains(mItems[i].verts[j]))
               break;
         }
         if(j == mItems[i].verts.size())
            mItems[i].selected = true;
      }
      mDragSelecting = false;
   }
}

void EditorUserInterface::onMouseDragged(S32 x, S32 y)
{
   mMousePos = convertWindowToCanvasCoord(Point(x,y));
   if(mCreatingPoly || mDragSelecting)
      return;
   Point delta = convertCanvasToLevelCoord(mMousePos);
   delta = snapToLevelGrid(delta - mMouseDownPos);

   for(S32 i = 0; i < mItems.size(); i++)
   {
      for(S32 j = 0; j < mItems[i].verts.size(); j++)
      {
         if(mItems[i].selected || mItems[i].vertSelected[j])
            mItems[i].verts[j] = mOriginalItems[i].verts[j] + delta;
      }
   }
}

void EditorUserInterface::onRightMouseDown(S32 x, S32 y)
{
   mMousePos = convertWindowToCanvasCoord(Point(x,y));
   if(mCreatingPoly)
   {
      Point newVertex = snapToLevelGrid(convertCanvasToLevelCoord(mMousePos));
      mNewItem.verts.push_back(newVertex);
      mNewItem.vertSelected.push_back(false);
      return;
   }

   S32 edgeHit, itemHit;
   findHitItemAndEdge(mMousePos, itemHit, edgeHit);

   if(itemHit != -1 && gGameItemRecs[mItems[itemHit].index].isPoly &&
      mItems[itemHit].index != ItemTeleporter)
   {
      clearSelection();

      Point newVertex = snapToLevelGrid(convertCanvasToLevelCoord(mMousePos));
      // insert an extra vertex at the mouse clicked point,
      // and then select it.
      mItems[itemHit].verts.insert(edgeHit + 1);
      mItems[itemHit].verts[edgeHit + 1] = newVertex;
      mItems[itemHit].vertSelected.insert(edgeHit + 1);
      mItems[itemHit].vertSelected[edgeHit + 1] = true;
      mMouseDownPos = newVertex;
      mOriginalItems = mItems;
   }
   else
   {
      //london chikara kelly markling
      mCreatingPoly = true;
      mNewItem.verts.clear();
      mNewItem.index = ItemBarrierMaker;
      mNewItem.width = Barrier::BarrierWidth;
      mNewItem.team = -1;
      mNewItem.selected = false;
      mNewItem.vertSelected.clear();
      Point newVertex = snapToLevelGrid(convertCanvasToLevelCoord(mMousePos));
      mNewItem.verts.push_back(newVertex);
      mNewItem.vertSelected.push_back(false);
   }
}

void EditorUserInterface::onMouseMoved(S32 x, S32 y)
{
   glutSetCursor(GLUT_CURSOR_RIGHT_ARROW);
   mMousePos = convertWindowToCanvasCoord(Point(x,y));
   //bool shiftKeyDown = glutGetModifiers() & GLUT_ACTIVE_SHIFT;

   if(!mCreatingPoly)// && !shiftKeyDown)
   {
      S32 vertexHit, vertexHitPoly;
      S32 edgeHit, itemHit;
   
      findHitVertex(mMousePos, vertexHitPoly, vertexHit);
      findHitItemAndEdge(mMousePos, itemHit, edgeHit);
      if( (vertexHit != -1 && mItems[vertexHitPoly].vertSelected[vertexHit]) ||
          (itemHit != -1 && mItems[itemHit].selected))
         glutSetCursor(GLUT_CURSOR_SPRAY);
   }
}

void EditorUserInterface::deleteSelection()
{
   for(S32 i = 0; i < mItems.size(); )
   {
      if(mItems[i].selected)
      {
         mItems.erase(i);
      }
      else
      {
         for(S32 j = 0; j < mItems[i].verts.size(); )
         {
            if(mItems[i].vertSelected[j])
            {
               mItems[i].verts.erase(j);
               mItems[i].vertSelected.erase(j);
            }
            else
               j++;
         }

         if(mItems[i].verts.size() == 0)
            mItems.erase(i);
         else
            i++;
      }
   }
}

void EditorUserInterface::incBarrierWidth()
{
   for(S32 i = 0; i < mItems.size(); i++)
   {
      if(mItems[i].index == ItemBarrierMaker && mItems[i].selected)
         mItems[i].width += 5;
   }
}

void EditorUserInterface::decBarrierWidth()
{
   for(S32 i = 0; i < mItems.size(); i++)
   {
      if(mItems[i].index == ItemBarrierMaker && mItems[i].selected && mItems[i].width >= 9)
         mItems[i].width -= 5;
   }
}

void EditorUserInterface::constructItem(U32 itemIndex)
{
   clearSelection();
   WorldItem w;
   w.index = itemIndex;
   if(gGameItemRecs[itemIndex].hasTeam)
      w.team = mCurrentTeam;
   else
      w.team = -1;
   if(w.team == -1 && gGameItemRecs[itemIndex].hasTeam && !gGameItemRecs[itemIndex].canHaveNoTeam)
      w.team = 0;

   w.selected = false;
   Point pos = convertCanvasToLevelCoord(mMousePos);
   w.verts.push_back(snapToLevelGrid(pos));
   w.vertSelected.push_back(false);
   if(itemIndex == ItemTeleporter)
   {
      w.verts.push_back(w.verts[0] + Point(1,1));
      w.vertSelected.push_back(false);
   }
   mItems.push_back(w);
}

void EditorUserInterface::onKeyDown(U32 key)
{
   bool ctrlActive = glutGetModifiers() & GLUT_ACTIVE_CTRL;

   if(key >= '0' && key < U32('1' + mTeams.size()))
   {
      setCurrentTeam(S32(key) - '1');
      return;
   }

   switch(tolower(key))
   {
      case 0x4: // control-d
         duplicateSelection();
         break;
      case 'f':
         flipSelectionHorizontal();
         break;
      case 26:
         {
            Vector<WorldItem> temp = mItems;
            mItems = mOriginalItems;
            mOriginalItems = temp;
            break;
         }
      case 'v':
         flipSelectionVertical();
         break;
      case 'r':
         if (key == 'R')
            rotateSelection(15.0f);
         else
            rotateSelection(-15.0f);
         break;
      case 'z':
         mCurrentOffset.set(0,0);
         break;
      case 'w':
         mUp = true;
         break;
      case 's':
         mDown = true;
         break;
      case 'a':
         mLeft = true;
         break;
      case 'd':
         mRight = true;
         break;
      case 'e':
         mIn = true;
         break;
      case 'c':
         mOut = true;
         break;
      case 't':
         constructItem(ItemTeleporter);
         break;
      case 'g':
         constructItem(ItemSpawn);
         break;
      case 'b':
         constructItem(ItemRepair);
         break;
      case 'y':
         constructItem(ItemTurret);
         break;
      case 'h':
         constructItem(ItemForceField);
         break;
      case 8:
      case 127:
         deleteSelection();
         break;
      case '+':
      case '=':
         incBarrierWidth();
         break;
      case '-':
      case '_':
         decBarrierWidth();
         break;
      case 27:
         gEditorMenuUserInterface.activate();
         break;
   }
}

void EditorUserInterface::onKeyUp(U32 key)
{
   switch(tolower(key))
   {
      case 'w':
         mUp = false;
         break;
      case 's':
         mDown = false;
         break;
      case 'a':
         mLeft = false;
         break;
      case 'd':
         mRight = false;
         break;
      case 'e':
         mIn = false;
         break;
      case 'c':
         mOut = false;
         break;
   }
}

void EditorUserInterface::idle(U32 timeDelta)
{
   F32 pixelsToScroll = timeDelta * 0.5f;

   if(mLeft && !mRight)
      mCurrentOffset.x += pixelsToScroll;
   else if(mRight && !mLeft)
      mCurrentOffset.x -= pixelsToScroll;
   if(mUp && !mDown)
      mCurrentOffset.y += pixelsToScroll;
   else if(mDown && !mUp)
      mCurrentOffset.y -= pixelsToScroll;

   Point mouseLevelPoint = convertCanvasToLevelCoord(mMousePos);
   if(mIn && !mOut)
      mCurrentScale *= 1 + timeDelta * 0.002;
   if(mOut && !mIn)
      mCurrentScale *= 1 - timeDelta * 0.002;
   if(mCurrentScale > 200)
      mCurrentScale = 200;
   else if(mCurrentScale < 10)
      mCurrentScale = 10;
   Point newMousePoint = convertLevelToCanvasCoord(mouseLevelPoint);
   mCurrentOffset += mMousePos - newMousePoint;   
}

static void escapeString(const char *src, char dest[1024])
{
   S32 i;
   for(i = 0; src[i]; i++)
      if(src[i] == '\"' || src[i] == ' ' || src[i] == '\n' || src[i] == '\t')
         break;

   if(!src[i])
   {
      strcpy(dest, src);
      return;
   }
   char *dptr = dest;
   *dptr++ = '\"';
   char c;
   while((c = *src++) != 0)
   {
      switch(c)
      {
         case '\"':
            *dptr++ = '\\';
            *dptr++ = '\"';
            break;
         case '\n':
            *dptr++ = '\\';
            *dptr++ = 'n';
            break;
         case '\t':
            *dptr++ = '\\';
            *dptr++ = 't';
            break;
         default:
            *dptr++ = c;
      }
   }
   *dptr++ = '\"';
   *dptr++ = 0;
}

void EditorUserInterface::saveLevel()
{
   char fileNameBuffer[256];
   dSprintf(fileNameBuffer, sizeof(fileNameBuffer), "levels/%s", mEditFileName);
   FILE *f = fopen(fileNameBuffer, "w");
   fprintf(f, "%sGameType", mGameType);
   for(S32 i = 0; i < mGameTypeArgs.size(); i++)
      fprintf(f, " %s", mGameTypeArgs[i]);
   fprintf(f, "\n");
   for(S32 i = 0; i < mTeams.size(); i++)
   {
      fprintf(f, "Team %s %g %g %g\n", mTeams[i].name,
         mTeams[i].color.r, mTeams[i].color.g, mTeams[i].color.b);
   }
   for(S32 i = 0; i < mUnknownItems.size(); i++)
   {
      Vector<const char *> &v = mUnknownItems[i];
      for(S32 j = 0; j < v.size(); j++)
      {
         char outBuffer[1024];
         escapeString(v[j], outBuffer);
         fputs(outBuffer, f);
         if(j == v.size() - 1)
            fputs("\n", f);
         else
            fputs(" ", f);
      }
   }
   for(S32 i = 0; i < mItems.size(); i++)
   {
      WorldItem &p = mItems[i];
      fprintf(f, "%s ", gGameItemRecs[mItems[i].index].name);
      if(gGameItemRecs[mItems[i].index].hasWidth)
         fprintf(f, "%g ", mItems[i].width);
      if(gGameItemRecs[mItems[i].index].hasTeam)
         fprintf(f, "%d ", mItems[i].team);
      for(S32 j = 0; j < p.verts.size(); j++)
         fprintf(f, "%g %g%c", p.verts[j].x, p.verts[j].y, (j == p.verts.size() - 1) ? '\n' : ' ');
   }

   fclose(f);
}

extern void hostGame(bool dedicated, Address bindAddress);
extern const char *gLevelList;

void EditorUserInterface::testLevel()
{
   char tmpFileName[256];
   strcpy(tmpFileName, mEditFileName);
   strcpy(mEditFileName, "editor.tmp");
   saveLevel();
   strcpy(mEditFileName, tmpFileName);
   const char *gLevelSave = gLevelList;
   gLevelList = "editor.tmp";
   hostGame(false, Address(IPProtocol, Address::Any, 28000));
   gLevelList = gLevelSave;
}

EditorMenuUserInterface gEditorMenuUserInterface;

EditorMenuUserInterface::EditorMenuUserInterface()
{
   menuTitle = "EDITOR MENU:";
}

void EditorMenuUserInterface::onActivate()
{
   setupMenus();
}

void EditorMenuUserInterface::setupMenus()
{
   Parent::onActivate();
   menuItems.clear();
   menuItems.push_back(MenuItem("RETURN TO EDITOR", 0));
   if(OptionsMenuUserInterface::fullscreen)
      menuItems.push_back(MenuItem("SET WINDOWED MODE", 1));
   else
      menuItems.push_back(MenuItem("SET FULLSCREEN MODE", 1));
   menuItems.push_back(MenuItem("TEST LEVEL",2));
   menuItems.push_back(MenuItem("SAVE LEVEL",3));
   menuItems.push_back(MenuItem("QUIT",4));
}

void EditorMenuUserInterface::processSelection(U32 index)
{
   switch(index)
   {
      case 0:
         gEditorUserInterface.activate();
         break;
      case 1:
         gOptionsMenuUserInterface.processSelection(1);
         setupMenus();
         break;
      case 2:
         gEditorUserInterface.testLevel();
         break;
      case 3:
         gEditorUserInterface.activate();
         gEditorUserInterface.saveLevel();
         break;
      case 4:
         gCreditsUserInterface.activate();
         break;
   }
}

void EditorMenuUserInterface::onEscape()
{
   gEditorUserInterface.activate();
}

void EditorMenuUserInterface::render()
{
   gEditorUserInterface.render();
   glColor4f(0, 0, 0, 0.5);
   glEnable(GL_BLEND);
   glBegin(GL_POLYGON);
   glVertex2f(0, 0);
   glVertex2f(canvasWidth, 0);
   glVertex2f(canvasWidth, canvasHeight);
   glVertex2f(0, canvasHeight);
   glEnd();  
   glDisable(GL_BLEND); 
   Parent::render();
}

};


syntax highlighted by Code2HTML, v. 0.9.1