/*
  Maze editor: application class
  Copyright (C) 1998 by Jorrit Tyberghein
  Written by Andrew Zabolotny <bit@eltech.ru>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library 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
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <stdarg.h>

#define SYSDEF_PATH
#include "sysdef.h"
#include "me_app.h"
#include "me_util.h"
#include "me_res.h"
#include "me_text.h"
#include "isystem.h"
#include "csengine/sysitf.h"
#include "csparser/csloader.h"
#include "csobject/nameobj.h"
#include "csengine/thingtpl.h"
#include "itxtmgr.h"
#include "csengine/dumper.h"

// Global option variables
bool  MazeEditor::fDraftAxis = true;
bool  MazeEditor::fDraftGrid = true;
bool  MazeEditor::fSnapToGrid = false;
float MazeEditor::DraftGridSpacing = 0.25;
bool  MazeEditor::fCrossVertices = true;
float MazeEditor::DraftPickDist = 3*3;
bool  MazeEditor::fDraftModificationAxis = true;
bool  MazeEditor::fDraftShowInactiveRooms = true;
int   MazeEditor::NgonSides = 12;
int   MazeEditor::PrismSides = 8;
int   MazeEditor::PyramidSides = 8;
int   MazeEditor::TorusRadialDetail = 12;
int   MazeEditor::TorusCrossSectionDetail = 8;
int   MazeEditor::SphereDetail = 16;

// A derivate of toolbar which updates correctly the popup menu check marks
class mzFloatingToolBar : public csDialog
{
  csMenu *updMenu;
  int updCommand;

public:
  mzFloatingToolBar (csComponent *iParent, csMenu *iMenu, int iCommand) :
    csDialog (iParent, csdfsAround)
  {
    updMenu = iMenu;
    updCommand = iCommand;
    updMenu->SetCheck (updCommand, true);
  }

  virtual ~mzFloatingToolBar ()
  {
    updMenu->SetCheck (updCommand, false);
  }
};

MazeEditor::MazeEditor (int argc, char **argv)
  : csApp ("Maze Editor", argc, argv), Models (16, 16)
{
  VertexMode = (unsigned int)cscmdMzCreateVertex;
  Create2Dmode = (unsigned int)cscmdMzCreateTriangle;
  Create3Dmode = (unsigned int)cscmdMzCreateBox;
  CreatePolyMode = (unsigned int)cscmdMzCreatePolyConnect;
  SelectMode = (unsigned int)cscmdMzVertexSelect;
  ModifyMode = (unsigned int)cscmdMzMove;
  ActiveButton = cscmdNothing;
  memset (&Axis, 0, sizeof (Axis));
  Axis.Type = axCenter;
  Axis.Pos = axViewPlane;
  Axis.OnlySelection = false;

  WorldName = strnew (config->GetStr ("MazeD", "WORLDS_PATH", NULL));
  ModelsPath = strnew (config->GetStr ("MazeD", "MODELS_PATH", NULL));

  CurrentModel = -1;
  SelectModel (0);
}

MazeEditor::~MazeEditor ()
{
  DeleteAllModels ();
  if (WorldName)
    delete [] WorldName;
}

void MazeEditor::EnsureFullyVisible (csComponent *comp)
{
  csRect newbound (comp->bound);
  if (newbound.xmin < bound.xmin)
    newbound.Move (bound.xmin - newbound.xmin, 0);
  if (newbound.ymin < bound.ymin)
    newbound.Move (0, bound.ymin - newbound.ymin);
  if (newbound.xmax > bound.xmax)
    newbound.Move (bound.xmax - newbound.xmax, 0);
  if (newbound.ymax > bound.ymax)
    newbound.Move (0, bound.ymax - newbound.ymax);
  if (newbound != comp->bound)
    comp->SetRect (newbound);
}

bool MazeEditor::InitialSetup ()
{
  // Print startup info
  CsPrintf (MSG_INITIALIZATION, "Crystal Space Maze Editor version %s (%s).\n", VERSION, RELEASE_DATE);
  CsPrintf (MSG_INITIALIZATION, "Created by Andrew Zabolotny and others...\n");
  
  // For GUI apps double buffering is a performance hit
  System->piG2D->DoubleBuffer (false);

  Mode = (unsigned int)cscmdMzCreateVertex;
  WindowLayout = 0;

  if (!csApp::InitialSetup ())
    return false;

  InitializeMainMenu (this);
  InitializeKeyboardAccelerator (this);

  // Initialize initial window layout @@todo: load from config file
  DefaultLayout ();

  ReservedTextures = GetTextures ()->GetNumTextures ();

  return true;
}

void MazeEditor::DefaultLayout ()
{
  CloseAllWindows ();

  Toggle_InfoBox ();
  Toggle_WindowManager ();
  Toggle_ToolBox ();

  Insert_DraftView ();
  Insert_DraftView ();
  Insert_DraftView ();
  Insert_3DView ();
}

static bool do_closeall (csComponent *child, void *param)
{
  (void)param;
  if ((child->id != CSWID_MENUBAR)
   && (child->id != MZID_ACCELERATOR))
    child->Close ();
  return false;
}

void MazeEditor::CloseAllWindows ()
{
  ForEach (do_closeall);
}

void MazeEditor::Toggle_ToolBox ()
{
  mzFloatingToolBar *toolbar = (mzFloatingToolBar *)GetChild (MZID_TOOLBOX);
  if (toolbar)
  {
    if (toolbar->GetState (CSS_VISIBLE))
      toolbar->Close ();
    else
      toolbar->Show ();
    return;
  }

  csMenu *menu = (csMenu *)GetChild (CSWID_MENUBAR);
  CHK (toolbar = new mzFloatingToolBar (this, menu, cscmdMzToolBox));
  toolbar->id = MZID_TOOLBOX;
  toolbar->SetAutoGrid (2, 1, true);
  toolbar->SetState (CSS_SELECTABLE, false);

  InitializeToolbox (app, toolbar);
  toolbar->SetText (TEXT_TOOLBOXTITLE);

  toolbar->SetSize (0, bound.ymax);
  int ToolbarW, ToolbarH;
  toolbar->SuggestSize (ToolbarW, ToolbarH);
  toolbar->SetSize (ToolbarW * 2, 0);
  toolbar->SuggestSize (ToolbarW, ToolbarH);
  toolbar->SetRect (0, menu->bound.ymax, ToolbarW, menu->bound.ymax + ToolbarH);
  SetMode (Mode, true);
}

void MazeEditor::Toggle_InfoBox ()
{
  mzFloatingToolBar *toolbar = (mzFloatingToolBar *)GetChild (MZID_INFOBAR);
  if (toolbar)
  {
    if (toolbar->GetState (CSS_VISIBLE))
      toolbar->Close ();
    else
      toolbar->Show ();
    return;
  }

  CHK (toolbar = new mzFloatingToolBar (this, (csMenu *)GetChild (CSWID_MENUBAR),
    cscmdMzInformation));
  toolbar->id = MZID_INFOBAR;
  toolbar->SetState (CSS_SELECTABLE, false);

  InitializeInfobar (app, toolbar);
  toolbar->SetText (TEXT_INFOTITLE);

  toolbar->SetPos (bound.xmax - toolbar->bound.Width (),
    bound.ymax - toolbar->bound.Height ());
}

void MazeEditor::Toggle_WindowManager ()
{
  mzFloatingToolBar *toolbar = (mzFloatingToolBar *)GetChild (MZID_WINDOWBAR);
  if (toolbar)
  {
    if (toolbar->GetState (CSS_VISIBLE))
      toolbar->Close ();
    else
      toolbar->Show ();
    return;
  }

  CHK (toolbar = new mzFloatingToolBar (this, (csMenu *)GetChild (CSWID_MENUBAR),
    cscmdMzWindowManager));
  toolbar->id = MZID_WINDOWBAR;
  toolbar->SetAutoGrid (2, 1, true);
  toolbar->SetState (CSS_SELECTABLE, false);

  InitializeWindowManager (app, toolbar);
  toolbar->SetText (TEXT_WINDOWMANAGERTITLE);

  toolbar->SetSize (0, bound.ymax);
  int ToolbarW, ToolbarH;
  toolbar->SuggestSize (ToolbarW, ToolbarH);
  toolbar->SetSize (ToolbarW * 2, 0);
  toolbar->SuggestSize (ToolbarW, ToolbarH);
  toolbar->SetRect (0, bound.ymax - ToolbarH, ToolbarW, bound.ymax);

  SetWindowLayout (WindowLayout);
}

static bool do_countdraft (csComponent *child, void *param)
{
  if (child->id == MZID_DRAFTVIEW)
  {
    char *snum = strchr (child->GetText (), '[');
    if (snum)
    {
      int num = atoi (snum + 1);
      if (num)
        (*((int *)param)) |= (1 << (num - 1));
    } /* endif */
  }
  return false;
}

void MazeEditor::Insert_DraftView ()
{
  // Close main menu to free space on desktop
  while (!EventQueue->IsEmpty ())
    Process ();
  GetChild (CSWID_MENUBAR)->SendCommand (cscmdMenuSetDropFlag, (void *)false);
  do
    Process ();
  while (!EventQueue->IsEmpty ());

  // First count how many draft views we already have
  int i, count = 0;
  ForEach (do_countdraft, &count);
  for (i = 0; i < 32; i++)
    if ((count & (1 << i)) == 0)
    {
      count = i + 1;
      break;
    } /* endif */
  if (count == 0)
    return;

  // Now compute maximal uncovered area of desktop
  csRect wb;
  FindMaxFreeRect (wb);
  // If there is not enough free space, complain
  if (wb.IsEmpty ())
  {
    MessageBox (this, TEXT_ERROR, TEXT_NODESKTOPSPACE);
    return;
  } /* endif */

  // If rectangle is too big, cut it in half
  if (wb.Width () > bound.Width () * 2 / 3)
    wb.xmax = (wb.xmin + wb.xmax) / 2;
  if (wb.Height () > bound.Height () * 2 / 3)
    wb.ymax = (wb.ymin + wb.ymax) / 2;

  char title [16];
  sprintf (title, TEXT_DRAFTVIEWTITLE, count);
  CHK (csWindow *window = new mzDraftWindow (this, title));
  window->id = MZID_DRAFTVIEW;
  CHK (mzDraftEditor *dv = new mzDraftEditor (window));

  // See if window is too small
  int ww = wb.Width (), wh = wb.Height ();
  window->FixSize  (ww, wh);
  if ((ww != wb.Width ()) || (wh != wb.Height ()))
  {
    delete window;
    MessageBox (this, TEXT_ERROR, TEXT_NODESKTOPSPACE);
    return;
  }

  switch (count)
  {
    case 1:
      dv->SendCommand (cscmdMzDraftViewTop);
      break;
    case 2:
      dv->SendCommand (cscmdMzDraftViewBack);
      break;
    case 3:
      dv->SendCommand (cscmdMzDraftViewRight);
      break;
    case 4:
      dv->SendCommand (cscmdMzDraftViewBottom);
      break;
    case 5:
      dv->SendCommand (cscmdMzDraftViewFront);
      break;
    case 6:
      dv->SendCommand (cscmdMzDraftViewLeft);
      break;
  } /* endswitch */
  window->SetRect (wb.xmin, wb.ymin, wb.xmax, wb.ymax);
  window->Select ();
}

void MazeEditor::Insert_3DView ()
{
  // Close main menu to free space on desktop
  while (!EventQueue->IsEmpty ())
    Process ();
  GetChild (CSWID_MENUBAR)->SendCommand (cscmdMenuSetDropFlag, (void *)false);
  do
    Process ();
  while (!EventQueue->IsEmpty ());

  // Now compute maximal uncovered area of desktop
  csRect wb;
  FindMaxFreeRect (wb);
  // If there is not enough free space, complain
  if ((wb.IsEmpty ())
   || (wb.Width () < bound.Width () / 16)
   || (wb.Height () < bound.Height () / 16))
  {
    MessageBox (this, TEXT_ERROR, TEXT_NODESKTOPSPACE);
    return;
  } /* endif */

  // If rectangle is too big, cut it in half
  if (wb.Width () > bound.Width () * 2 / 3)
    wb.xmax = (wb.xmin + wb.xmax) / 2;
  if (wb.Height () > bound.Height () * 2 / 3)
    wb.ymax = (wb.ymin + wb.ymax) / 2;

  CHK (csWindow *window = new mz3DWindow (this, TEXT_3DVIEWTITLE));
  window->id = MZID_3DVIEW;
  CHK ((void)new mzCameraView (window));
  window->SetRect (wb.xmin, wb.ymin, wb.xmax, wb.ymax);
  window->Select ();
}

void MazeEditor::LoadConfig ()
{
  csApp::LoadConfig ();
  if (customtexturename && customtextureparm)
  {
    customtexturename->Push (strnew (TOOLBAR_BITMAPS));
    customtextureparm->Push (strnew ("TRANSPARENT (0.8, 0.8, 0.8)"));
  }
}

static bool do_findsel (csComponent *child, void *param)
{
  if (((csButton *)child)->GetCommandCode () == (int)param)
  {
    ((csButton *)child)->SetPressed (true);
    child->Select ();
    return true;
  } /* endif */
  return false;
}

void MazeEditor::BarSetupDialog (csButton *Source)
{
  int oldMode = Mode;

  csComponent *tbp = Source->parent;
  if (!tbp)
    return;

  csComponent *tbpp = tbp->parent;
  if (!tbpp)
    return;

  CHK (csDialog *toolbar = new csDialog (tbpp, csdfsAround));
  toolbar->SetAutoGrid (2, 1, true);
  toolbar->SetState (CSS_TOPSELECT, true);

  int vrows = 1;
  int defaultb = cscmdNothing;
  switch (Source->GetCommandCode ())
  {
    case cscmdMzVertexSelect:
    case cscmdMzVertexDeselect:
    case cscmdMzVertexInvert:
      if (Model)
      {
        Model->SelectAllVertices (Source->GetCommandCode ());
        GLOBAL_DRAFTCHANGED;
      } /* endif */
      app->Dismiss (cscmdCancel);
      break;
    case cscmdMzPolySelect:
    case cscmdMzPolyDeselect:
    case cscmdMzPolyInvert:
	case cscmdMzPolyChg:
      if (Model)
      {
        Model->SelectAllPolygons (Source->GetCommandCode ());
        GLOBAL_DRAFTCHANGED;
      } /* endif */
      app->Dismiss (cscmdCancel);
      break;
    case cscmdMzVertex:
      defaultb = VertexMode;
      InitializeVertexToolbar (app, toolbar);
      break;
    case cscmdMzCreate2D:
      defaultb = Create2Dmode;
      InitializeCreate2DToolbar (app, toolbar);
      break;
    case cscmdMzCreate3D:
      defaultb = Create3Dmode;
      InitializeCreate3DToolbar (app, toolbar);
      break;
    case cscmdMzCreateLight:
	{

      MessageBox (app, "Info", "Setup cscmdMzCreateLight operation");
    }  break;
    case cscmdMzCreatePoly:
      defaultb = CreatePolyMode;
      InitializeCreatePolyToolbar (app, toolbar);
      break;
    case cscmdMzSelect:
      vrows = 2;
      defaultb = SelectMode;
      InitializeSelectToolbar (app, toolbar);
      break;
    case cscmdMzModify:
      defaultb = ModifyMode;
      InitializeModifyToolbar (app, toolbar);
      break;
    case cscmdMzToggleGrid:
    case cscmdMzToggleAxis:
      if (DraftSetupDialog (this) == cscmdOK)
        GLOBAL_DRAFTCHANGED;
      break;
  } /* endswitch */
  if (!toolbar->focused)
  {
    CHK (delete toolbar);
    return;
  } /* endif */

  SetMode (Source->GetCommandCode (), true);
  toolbar->SetSize (bound.xmax, 0);
  int ToolbarW, ToolbarH;
  toolbar->SuggestSize (ToolbarW, ToolbarH);
  toolbar->SetSize (ToolbarW / vrows + 1, 0);
  toolbar->SuggestSize (ToolbarW, ToolbarH);

  // Find the active button
  toolbar->ForEach (do_findsel, (void *)defaultb);

  int x = (Source->bound.xmin - toolbar->focused->bound.xmin) + tbp->bound.xmin;
  int y = (Source->bound.ymin - toolbar->focused->bound.ymin) + tbp->bound.ymin;

  toolbar->SetRect (x, y, x + ToolbarW, y + ToolbarH);
  EnsureFullyVisible (toolbar);

  int code = Execute (toolbar);
  switch (code)
  {
    case cscmdCancel:
      SetMode (oldMode, true);
      break;
    case cscmdMzCreateVertex:
    case cscmdMzDeleteVertex:
      VertexMode = code;
      break;
    case cscmdMzDeleteSelVertices:
    case cscmdMzDeleteSelPolygons:
      // This is not a "sticky" operation
      SetMode (oldMode, true);
      break;
    case cscmdMzCreateTriangle:
    case cscmdMzCreateSquare:
    case cscmdMzCreateHexagon:
    case cscmdMzCreateOctagon:
    case cscmdMzCreateNgon:
      Create2Dmode = code;
      break;
    case cscmdMzCreateBox:
    case cscmdMzCreatePrism:
    case cscmdMzCreatePyramid:
    case cscmdMzCreateTorus:
    case cscmdMzCreateSphere:
      Create3Dmode = code;
      break;
    case cscmdMzCreatePolyConnect:
    case cscmdMzCreatePolyTriFan:
    case cscmdMzCreatePolyTriStrip:
    case cscmdMzCreatePolyQuadStrip:
      CreatePolyMode = code;
      break;
    case cscmdMzVertexSelect:
    case cscmdMzVertexDeselect:
    case cscmdMzVertexInvert:
    case cscmdMzPolySelect:
    case cscmdMzPolyDeselect:
    case cscmdMzPolyInvert:
	case cscmdMzPolyChg:
		SelectMode = code;
      break;
    case cscmdMzMove:
    case cscmdMzScale2D:
    case cscmdMzScale3D:
    case cscmdMzRotate:
    case cscmdMzSetupAxis:
      ModifyMode = code;
      break;
  } /* endswitch */
  CHK (delete toolbar);
}

bool MazeEditor::SetupAxis ()
{
  int code = SetupAxisDialog (app, Axis);

  if (code != cscmdCancel)
    GLOBAL_DRAFTCHANGED;

  if (code != cscmdRetry)
    return false;

  Axis.Type = axUserDefined;
  return true;
}

bool MazeEditor::HandleEvent (csEvent &Event)
{
  switch (Event.Type)
  {
    case csevCommand:
      switch (Event.Command.Code)
      {
        case cscmdButtonRightClick:
          switch (((csButton *)Event.Command.Info)->id)
          {
            case cscmdMzCreateNgon:
              if (NumberOfSidesDialog (this, TEXT_NGONSIDESTITLE, NgonSides) == cscmdOK)
              {
                SetMode (cscmdMzCreateNgon, true);
                Dismiss (cscmdMzCreateNgon);
              } /* endif */
              break;
            case cscmdMzCreatePrism:
              if (NumberOfSidesDialog (this, TEXT_PRISMSIDESTITLE, PrismSides) == cscmdOK)
              {
                SetMode (cscmdMzCreatePrism, true);
                Dismiss (cscmdMzCreatePrism);
              } /* endif */
              break;
            case cscmdMzCreatePyramid:
              if (NumberOfSidesDialog (this, TEXT_PYRAMIDSIDESTITLE, PyramidSides) == cscmdOK)
              {
                SetMode (cscmdMzCreatePyramid, true);
                Dismiss (cscmdMzCreatePyramid);
              } /* endif */
              break;
            case cscmdMzCreateTorus:
              if (TorusParmDialog (this, TorusRadialDetail, TorusCrossSectionDetail) == cscmdOK)
              {
                SetMode (cscmdMzCreateTorus, true);
                Dismiss (cscmdMzCreateTorus);
              } /* endif */
              break;
            case cscmdMzCreateSphere:
              if (SphereParmDialog (this, SphereDetail) == cscmdOK)
              {
                SetMode (cscmdMzCreateSphere, true);
                Dismiss (cscmdMzCreateSphere);
              } /* endif */
              break;
            case cscmdMzScale2D:
            case cscmdMzScale3D:
            case cscmdMzRotate:
              if (SetupAxis ())
              {
                SetMode (cscmdMzSetupAxis, true);
                Dismiss (cscmdMzSetupAxis);
              } /* endif */
              break;
            default:
              BarSetupDialog ((csButton *)Event.Command.Info);
              break;
          } /* endswitch */
          return true;
        case cscmdMzVertexSelectAll:
        case cscmdMzVertexDeselectAll:
        case cscmdMzVertexInvertAll:
          if (Model)
          {
            Model->SelectAllVertices (Event.Command.Code);
            GLOBAL_DRAFTCHANGED;
          } /* endif */
          break;
        case cscmdMzPolySelectAll:
        case cscmdMzPolyDeselectAll:
        case cscmdMzPolyInvertAll:
          if (Model)
          {
            Model->SelectAllPolygons (Event.Command.Code);
            GLOBAL_DRAFTCHANGED;
          } /* endif */
          break;
        case cscmdMzToolBox:
          Toggle_ToolBox ();
          return true;
        case cscmdMzWindowManager:
          Toggle_WindowManager ();
          return true;
        case cscmdMzInformation:
          Toggle_InfoBox ();
          return true;
        case cscmdMzAbout:
          ShowAbout (app);
          return true;
        case cscmdMzWindowList:
          WindowList ();
          return true;
        case cscmdMzWindowCloseAll:
          CloseAllWindows ();
          return true;
        case cscmdMzWindowLayoutReset:
          DefaultLayout ();
          return true;
        case cscmdMzWindowLayout1:
          SetWindowLayout (0);
          return true;
        case cscmdMzWindowLayout2:
          SetWindowLayout (1);
          return true;
        case cscmdMzWindowLayout3:
          SetWindowLayout (2);
          return true;
        case cscmdMzWindowLayout4:
          SetWindowLayout (3);
          return true;
        case cscmdMzDraftView:
          Insert_DraftView ();
          return true;
        case cscmdMzDraftViewSetup:
          if (DraftSetupDialog (this) == cscmdOK)
            GLOBAL_DRAFTCHANGED;
          return true;
        case cscmdMz3DView:
          Insert_3DView ();
          return true;
        case cscmdMzVertex:
        case cscmdMzCreateVertex:
        case cscmdMzDeleteVertex:
        case cscmdMzDeleteSelVertices:
        case cscmdMzDeleteSelPolygons:
        case cscmdMzCreate2D:
        case cscmdMzCreateTriangle:
        case cscmdMzCreateSquare:
        case cscmdMzCreateHexagon:
        case cscmdMzCreateOctagon:
        case cscmdMzCreateNgon:
        case cscmdMzCreate3D:
        case cscmdMzCreateBox:
        case cscmdMzCreatePrism:
        case cscmdMzCreatePyramid:
        case cscmdMzCreateTorus:
        case cscmdMzCreateSphere:
        case cscmdMzCreateLight:
        case cscmdMzCreatePoly:
        case cscmdMzCreatePolyConnect:
        case cscmdMzCreatePolyTriFan:
        case cscmdMzCreatePolyTriStrip:
        case cscmdMzCreatePolyQuadStrip:
        case cscmdMzSelect:
        case cscmdMzVertexSelect:
        case cscmdMzVertexDeselect:
        case cscmdMzVertexInvert:
        case cscmdMzPolySelect:
        case cscmdMzPolyDeselect:
        case cscmdMzPolyInvert:
		case cscmdMzPolyChg:
        case cscmdMzModify:
        case cscmdMzMove:
        case cscmdMzScale2D:
        case cscmdMzScale3D:
        case cscmdMzRotate:
        case cscmdMzSetupAxis:
        case cscmdMzCoordRotate:
        case cscmdMzZoomInOut:
        case cscmdMzZoomToRect:
        case cscmdMzSlideView:
        case cscmdMzToggleGrid:
        case cscmdMzToggleAxis:
		case cscmdMzPortalSelect:
          SetMode (Event.Command.Code, false);
          break;
        case cscmdMzCreateRoom:
		case cscmdMzCreateSector:
        case cscmdMzCreateThing:
        case cscmdMzCreateSprite:
          CreateModel (Event.Command.Code == cscmdMzCreateRoom ? csmtRoom :
			Event.Command.Code == cscmdMzCreateSector ? csmtSector : 
            Event.Command.Code == cscmdMzCreateThing ? csmtThing : csmtSprite);
          break;
        case cscmdMzOpenWorld:
        case cscmdMzOpenModel:
          Load (Event.Command.Code == cscmdMzOpenWorld);
          break;
        case cscmdMzSaveWorld:
        case cscmdMzSaveModel:
          Save (Event.Command.Code == cscmdMzSaveWorld);
          break;
        case cscmdNewWorld:
          DeleteAllModels ();
          DeleteAllTextures ();
          GLOBAL_FITALL;
          break;
        case cscmdMzNextModelFrame:
          if (Model && (Model->GetFrame () < Model->Frames () - 1))
            if (Model->SetFrame (Model->GetFrame () + 1))
            {
              GLOBAL_DRAFTCHANGED;
              GLOBAL_MODELINFO;
            } /* endif */
          break;
        case cscmdMzPrevModelFrame:
          if (Model && (Model->GetFrame () > 0))
            if (Model->SetFrame (Model->GetFrame () - 1))
            {
              GLOBAL_DRAFTCHANGED;
              GLOBAL_MODELINFO;
            } /* endif */
          break;
        case cscmdMzInsertModelFrame:
          if (Model)
          {
            if (Model->SetFrame (Model->Frames ()))
            {
              GLOBAL_DRAFTCHANGED;
              GLOBAL_MODELINFO;
            }
            else
              MessageBox (this, TEXT_ERROR, TEXT_NOMODELFRAMES);
          } /* endif */
          break;
        case cscmdMzDeleteModelFrame:
          if (Model)
          {
            Model->DeleteFrame (Model->GetFrame ());
            GLOBAL_DRAFTCHANGED;
            GLOBAL_MODELINFO;
          }
          break;
        case cscmdMzClearModel:
          ClearModel ();
          break;
        case cscmdMzModelManager:
          ModelManagerDialog (this);
          break;
        case cscmdMzDeleteModel:
          break;
		case cscmdMzPortalDefine:
			PortalManagerDialog (this);
			break;
      } /* endswitch */
      break;
  } /* endswitch */
  return csApp::HandleEvent (Event);
}

void MazeEditor::ClearModel ()
{
  static bool Vertices = true, Faces = true, Lights = true, Frames = false,
    Actions = false;

  if (!GLOBAL_MODEL)
    return;
  if (ClearModelDialog (app, Vertices, Faces, Lights, Frames, Actions) != cscmdOK)
    return;
  GLOBAL_MODEL->Clear (Vertices, Faces, Lights, Frames, Actions);
  GLOBAL_DRAFTCHANGED;
  GLOBAL_MODELINFO;
}

void MazeEditor::SetMode (int iMode, bool Quiet)
{
  switch (iMode)
  {
    case cscmdMzVertex:
      iMode = VertexMode;
      break;
    case cscmdMzCreate2D:
      iMode = Create2Dmode;
      break;
    case cscmdMzCreate3D:
      iMode = Create3Dmode;
      break;
    case cscmdMzCreatePoly:
      iMode = CreatePolyMode;
      break;
    case cscmdMzSelect:
      iMode = SelectMode;
      break;
    case cscmdMzModify:
      iMode = ModifyMode;
      break;
  } /* endswitch */

  int defbutton1 = cscmdNothing;
  int defbutton2 = cscmdNothing;
  switch (iMode)
  {
    case cscmdMzCreateVertex:
      SetOpDescription (TEXT_CREATEVERTEX);
      VertexMode = Mode = iMode;
      defbutton1 = cscmdMzVertex;
      break;
    case cscmdMzDeleteVertex:
      SetOpDescription (TEXT_DELETEVERTEX);
      VertexMode = Mode = iMode;
      defbutton1 = cscmdMzVertex;
      break;
    case cscmdMzDeleteSelVertices:
      if (Model)
      {
        Model->DelSelectedVertices ();
        GLOBAL_DRAFTCHANGED;
      } /* endif */
      return;
    case cscmdMzDeleteSelPolygons:
      if (Model)
      {
        Model->DelSelectedPolygons ();
        GLOBAL_DRAFTCHANGED;
      } /* endif */
      return;
    case cscmdMzCreateTriangle:
      SetOpDescription (TEXT_CREATETRIANGLE);
      Create2Dmode = Mode = iMode;
      defbutton1 = cscmdMzCreate2D;
      break;
    case cscmdMzCreateSquare:
      SetOpDescription (TEXT_CREATESQUARE);
      Create2Dmode = Mode = iMode;
      defbutton1 = cscmdMzCreate2D;
      break;
    case cscmdMzCreateHexagon:
      SetOpDescription (TEXT_CREATEHEXAGON);
      Create2Dmode = Mode = iMode;
      defbutton1 = cscmdMzCreate2D;
      break;
    case cscmdMzCreateOctagon:
      SetOpDescription (TEXT_CREATEOCTAGON);
      Create2Dmode = Mode = iMode;
      defbutton1 = cscmdMzCreate2D;
      break;
    case cscmdMzCreateNgon:
      SetOpDescription (TEXT_CREATENGON);
      Create2Dmode = Mode = iMode;
      defbutton1 = cscmdMzCreate2D;
      break;
    case cscmdMzCreateBox:
      SetOpDescription (TEXT_CREATEBOX);
      Create3Dmode = Mode = iMode;
      defbutton1 = cscmdMzCreate3D;
      break;
    case cscmdMzCreatePrism:
      SetOpDescription (TEXT_CREATEPRISM);
      Create3Dmode = Mode = iMode;
      defbutton1 = cscmdMzCreate3D;
      break;
    case cscmdMzCreatePyramid:
      SetOpDescription (TEXT_CREATEPYRAMID);
      Create3Dmode = Mode = iMode;
      defbutton1 = cscmdMzCreate3D;
      break;
    case cscmdMzCreateTorus:
      SetOpDescription (TEXT_CREATETORUS);
      Create3Dmode = Mode = iMode;
      defbutton1 = cscmdMzCreate3D;
      break;
    case cscmdMzCreateSphere:
      SetOpDescription (TEXT_CREATESPHERE);
      Create3Dmode = Mode = iMode;
      defbutton1 = cscmdMzCreate3D;
      break;
    case cscmdMzCreateLight:
      SetOpDescription (TEXT_CREATELIGHT);
      Mode = iMode;
      defbutton1 = cscmdMzCreateLight;
      break;
    case cscmdMzCreatePolyConnect:
      SetOpDescription (TEXT_CREATEPOLYCONNECT);
      CreatePolyMode = Mode = iMode;
      defbutton1 = cscmdMzCreatePoly;
      break;
    case cscmdMzCreatePolyTriFan:
      SetOpDescription (TEXT_CREATEPOLYTRIFAN);
      CreatePolyMode = Mode = iMode;
      defbutton1 = cscmdMzCreatePoly;
      break;
    case cscmdMzCreatePolyTriStrip:
      SetOpDescription (TEXT_CREATEPOLYTRISTRIP);
      CreatePolyMode = Mode = iMode;
      defbutton1 = cscmdMzCreatePoly;
      break;
    case cscmdMzCreatePolyQuadStrip:
      SetOpDescription (TEXT_CREATEPOLYQUADSTRIP);
      CreatePolyMode = Mode = iMode;
      defbutton1 = cscmdMzCreatePoly;
      break;
    case cscmdMzVertexSelect:
      SetOpDescription (TEXT_VERTEXSELECT);
      SelectMode = Mode = iMode;
      defbutton1 = cscmdMzSelect;
      break;
    case cscmdMzVertexDeselect:
      SetOpDescription (TEXT_VERTEXDESELECT);
      SelectMode = Mode = iMode;
      defbutton1 = cscmdMzSelect;
      break;
    case cscmdMzVertexInvert:
      SetOpDescription (TEXT_VERTEXINVERT);
      SelectMode = Mode = iMode;
      defbutton1 = cscmdMzSelect;
      break;
    case cscmdMzPolySelect:
      SetOpDescription (TEXT_POLYSELECT);
      SelectMode = Mode = iMode;
      defbutton1 = cscmdMzSelect;
      break;
    case cscmdMzPolyDeselect:
      SetOpDescription (TEXT_POLYDESELECT);
      SelectMode = Mode = iMode;
      defbutton1 = cscmdMzSelect;
      break;
    case cscmdMzPolyInvert:
      SetOpDescription (TEXT_POLYINVERT);
      SelectMode = Mode = iMode;
      defbutton1 = cscmdMzSelect;
      break;
    case cscmdMzPolyChg:
      SetOpDescription (TEXT_POLYCHG);
      SelectMode = Mode = iMode;
      defbutton1 = cscmdMzSelect;
      break;
    case cscmdMzMove:
      SetOpDescription (TEXT_MOVESELECTED);
      ModifyMode = Mode = iMode;
      defbutton1 = cscmdMzModify;
      break;
    case cscmdMzScale2D:
      SetOpDescription (TEXT_SCALE2DSELECTED);
      ModifyMode = Mode = iMode;
      defbutton1 = cscmdMzModify;
      break;
    case cscmdMzScale3D:
      SetOpDescription (TEXT_SCALE3DSELECTED);
      ModifyMode = Mode = iMode;
      defbutton1 = cscmdMzModify;
      break;
    case cscmdMzRotate:
      SetOpDescription (TEXT_ROTATESELECTED);
      ModifyMode = Mode = iMode;
      defbutton1 = cscmdMzModify;
      break;
    case cscmdMzSetupAxis:
      if (!Quiet && !SetupAxis ())
        return;
      SetOpDescription (TEXT_SETUPMODIFYAXIS);
      ModifyMode = Mode = iMode;
      defbutton1 = cscmdMzModify;
      break;
    case cscmdMzCoordRotate:
      SetOpDescription (TEXT_COORDROTATE);
      Mode = iMode;
      defbutton2 = cscmdMzCoordRotate;
      break;
    case cscmdMzZoomInOut:
      SetOpDescription (TEXT_ZOOMINOUT);
      Mode = iMode;
      defbutton2 = cscmdMzZoomInOut;
      break;
    case cscmdMzZoomToRect:
      SetOpDescription (TEXT_ZOOMTORECT);
      Mode = iMode;
      defbutton2 = cscmdMzZoomToRect;
      break;
    case cscmdMzSlideView:
      SetOpDescription (TEXT_SLIDEVIEW);
      Mode = iMode;
      defbutton2 = cscmdMzSlideView;
      break;
    case cscmdMzPortalSelect:
      SetOpDescription (TEXT_PORTALSELECT);
      SelectMode = Mode = iMode;
      defbutton1 = cscmdMzSelect;
      break;
    case cscmdMzToggleGrid:
    {
      // Perform the operation right here
      fDraftGrid = !fDraftGrid;
      csComponent *toolbar = GetChild (MZID_WINDOWBAR);
      if (toolbar)
      {
        csButton *but = (csButton *)toolbar->GetChild ((unsigned int)cscmdMzToggleGrid);
        but->SetPressed (fDraftGrid);
      } /* endif */
      GLOBAL_DRAFTCHANGED;
      return;
    }
    case cscmdMzToggleAxis:
    {
      // Perform the operation right here
      fDraftAxis = !fDraftAxis;
      csComponent *toolbar = GetChild (MZID_WINDOWBAR);
      if (toolbar)
      {
        csButton *but = (csButton *)toolbar->GetChild ((unsigned int)cscmdMzToggleAxis);
        but->SetPressed (fDraftAxis);
      } /* endif */
      GLOBAL_DRAFTCHANGED;
      return;
    }
  } /* endswitch */
  mzFloatingToolBar *toolbar = (mzFloatingToolBar *)GetChild (MZID_TOOLBOX);
  if (toolbar)
  {
    if (defbutton1 == cscmdNothing)
    {
      csButton *oldbut = (csButton *)toolbar->GetChild (ActiveButton);
      if (oldbut)
        oldbut->SetPressed (false);
    }
    else
    {
      csButton *defbut = (csButton *)toolbar->GetChild (defbutton1);
      if (defbut)
      {
        defbut->SetPressed (true);
        defbut->SetBitmap (GetBitmap (app, Mode), NULL);
      } /* endif */
    } /* endif */
  } /* endif */
  toolbar = (mzFloatingToolBar *)GetChild (MZID_WINDOWBAR);
  if (toolbar)
  {
    if (defbutton2 == cscmdNothing)
    {
      csButton *oldbut = (csButton *)toolbar->GetChild (ActiveButton);
      if (oldbut)
        oldbut->SetPressed (false);
    }
    else
    {
      csButton *defbut = (csButton *)toolbar->GetChild (defbutton2);
      if (defbut)
      {
        defbut->SetPressed (true);
        defbut->SetBitmap (GetBitmap (app, Mode), NULL);
      } /* endif */
    } /* endif */
  } /* endif */
  if (defbutton1 != cscmdNothing)
    ActiveButton = defbutton1;
  else
    ActiveButton = defbutton2;
}

void MazeEditor::SetWindowLayout (int iNumber)
{
  WindowLayout = iNumber;

  int defbutton = cscmdNothing;
  if (WindowLayout == 0)
    defbutton = cscmdMzWindowLayout1;
  else if (WindowLayout == 1)
    defbutton = cscmdMzWindowLayout2;
  else if (WindowLayout == 2)
    defbutton = cscmdMzWindowLayout3;
  else if (WindowLayout == 3)
    defbutton = cscmdMzWindowLayout4;
  mzFloatingToolBar *toolbar = (mzFloatingToolBar *)GetChild (MZID_WINDOWBAR);
  if (toolbar)
  {
    csButton *defbut = (csButton *)toolbar->GetChild (defbutton);
    if (defbut)
      defbut->SetPressed (true);
  } /* endif */
}

void MazeEditor::SelectModel (int iNum)
{
  if (CurrentModel != iNum)
  {
    if ((iNum > 0)
     && (Models.Length () > 0)
     && (iNum >= Models.Length ()))
      iNum = Models.Length () - 1;
    CurrentModel = iNum;
    if ((CurrentModel >= 0) && (CurrentModel < Models.Length ()))
      Model = (mz3DModel *)Models [CurrentModel];
    else
      Model = NULL;
    if (!Model)
      CurrentModel = -1;
    // Broadcase "model changed" notification
    GLOBAL_DRAFTCHANGED;
    GLOBAL_MODELINFO;
  } /* endif */
}

void MazeEditor::DeleteModel (int iIndex)
{
  if ((iIndex >= 0)
   && (iIndex < Models.Length ()))
  {
    Models.Delete (iIndex);
    int OldCurrentModel = CurrentModel;
    CurrentModel = -1;
    SelectModel (OldCurrentModel);
  } /* endif */
}

void MazeEditor::DeleteAllModels ()
{
  Models.DeleteAll ();
  Model = NULL;
  CurrentModel = -1;
  DeleteAllTextures ();
}

void MazeEditor::DeleteAllTextures ()
{
  //@@@
  //for (int i = textures->get_num_textures () - 1; i >= ReservedTextures; i--)
    //textures->delete_texture (i);
//  GetTextures ()->Clear ();
}

void MazeEditor::ModelInfo ()
{
  if (Model)
  {
    SetInfo (MZID_INFO_VERTICES, Model->Vertices ());
    SetInfo (MZID_INFO_POLYGONS, Model->Polygons ());
    SetInfo (MZID_INFO_LIGHTS, Model->Lights ());
    char tmp[16];
    sprintf (tmp, "%d/%d", Model->GetFrame () + 1, Model->Frames ());
    SetInfo (MZID_INFO_FRAMES, tmp);
  } /* endif */
}

void MazeEditor::SetOpDescription (char *iText, ...)
{
  mzFloatingToolBar *toolbar = (mzFloatingToolBar *)GetChild (MZID_INFOBAR);
  if (toolbar && iText)
  {
    char buff [256];
    va_list arg;
    va_start (arg, iText);
    vsprintf (buff, iText, arg);
    va_end (arg);

    csComponent *txt = toolbar->GetChild (MZID_INFO_CURRENTOP);
    if (txt)
      txt->SetText (buff);
  } /* endif */
}

void MazeEditor::SetInfo (int id, char *val)
{
  mzFloatingToolBar *toolbar = (mzFloatingToolBar *)GetChild (MZID_INFOBAR);
  if (toolbar)
  {
    csComponent *txt = toolbar->GetChild (id);
    if (txt)
      txt->SetText (val);
  } /* endif */
}

void MazeEditor::SetInfo (int id, float val)
{
  char temp[30];
  char *format;
  if ((val < 1) && (val > -1))
    format = "%.3f";
  else if ((val < 100) && (val > -100))
    format = "%.2f";
  else
    format = "%.1f";
  sprintf (temp, format, val);
  SetInfo (id, temp);
}

void MazeEditor::SetInfo (int id, int val)
{
  char temp[30];
  sprintf (temp, "%d", val);
  SetInfo (id, temp);
}

void MazeEditor::SetFileName (char *iFileName)
{
  if (WorldName)
    delete [] WorldName;
  WorldName = strnew (iFileName);
}

bool MazeEditor::Load (bool iEntireWorld)
{
  // Query source file name
  csWindow *d;
  if (iEntireWorld)
    d = csFileDialog (this, TEXT_LOADWORLDTITLE, WorldName, TEXT_LOADBUTTON);
  else
    d = csFileDialog (this, TEXT_LOADMODELTITLE, ModelsPath, TEXT_LOADBUTTON);
  if (!d)
    return false;

  if (Execute (d) != cscmdOK)
  {
    delete d;
    return false;
  }
  char fname [MAXPATHLEN + 1], ext[5];
  csQueryFileDialog (d, fname, sizeof (fname));
  strcpy(ext, fname+strlen(fname)-4);
  if (strcmp(ext, ".zip") && iEntireWorld) { // require zip 
	delete d;
	MessageBox (this, TEXT_ERROR, TEXT_SAVENULL);
	return false;
  }


  delete d;

  // Check if file exists
  FILE *f = fopen (fname, "rb");
  if (!f)
  {
    MessageBox (this, TEXT_ERROR, TEXT_CANTOPENREAD);
    return false;
  }
  fclose (f);

  if (iEntireWorld)
  {
    DeleteAllModels ();
    SetFileName (fname);
  }

  // Parse input file
  if (!LoadWorld (fname))
  {
    MessageBox (this, TEXT_ERROR, TEXT_LOADFILEERROR);
    return false;
  } /* endif */
  return true;
}

int fPut (void *iStream, void *iData, int iSize)
{
  return fwrite (iData, 1, iSize, (FILE *)iStream);
}

bool zPut (Archive *ar, void *iStream, char *iData, int iSize)
{
	if (ar != NULL)
		return (int)ar->append(iStream, (const char *)iData, iSize);
	else
		if ((int)fwrite (iData, 1, iSize, (FILE *)iStream) == iSize)
			return 1;
		else 
			return 0;
}

bool MazeEditor::Save (bool iEntireWorld)
{
  if (Models.Length () == 0)
    return false;

  // Query destination file name
  csWindow *d;
  if (iEntireWorld)
    d = csFileDialog (this, TEXT_SAVEWORLDTITLE, WorldName, TEXT_SAVEBUTTON);
  else
    d = csFileDialog (this, TEXT_SAVEMODELTITLE, GLOBAL_MODEL->GetFileName (),
      TEXT_SAVEBUTTON);
  if (!d)
    return false;

  if (Execute (d) != cscmdOK)
  {
    delete d;
    return false;
  }
  void *f;
  char fullname [MAXPATHLEN + 1], ext[5];
  char fname[80];
  csComponent *d2 = d->GetChild (CSWID_CLIENT);
  if (d2)
  {
    csComponent *f = d2->GetChild (CSWID_FILENAME);
	f->GetText (fname , 80);
	System->Printf (MSG_INITIALIZATION, "Name of file query result %s\n", fname);
	if (strlen(fname) == 0) {
//		System->Printf (MSG_INITIALIZATION, "Name of file is zero\n");
		delete d;
		MessageBox (this, TEXT_ERROR, TEXT_SAVENULL);
		return false;
	}

  }

  csQueryFileDialog (d, fullname, MAXPATHLEN + 1);
  strcpy(ext, fullname+strlen(fullname)-4);
//  System->Printf (MSG_INITIALIZATION, "Extension : %s\n", ext);
  delete d;

  csWorld *new_world=NULL; 
  Archive *ar;
  if (iEntireWorld) {
	// we save the world
	new_world = new csWorld();
	// LanguageLayer layer (new_world, NULL);
	ar = new_world->OpenWorldFile ( fullname);
    if (ar == NULL) {
		// unable to create/open archive
		System->Printf (MSG_INITIALIZATION, "Error opening archive %s, now try to create new one\n", fname);
		ar = new Archive(fname, TRUE);
		if (ar == NULL) {
			System->Printf (MSG_INITIALIZATION, "Cannot create new archive!\n", fname);
			return false;
		}
	}
	size_t fsize;
	// check for existence of file (not needed?)
	if (!(ar->file_exists("world", &fsize))) {
		System->Printf (MSG_INITIALIZATION, "world file doesn't exists in archive!\n");
		f = ar->new_file ("world", 100000);
	}
	else {
		System->Printf (MSG_INITIALIZATION, "original world file size is %d\n", fsize);
		if ((f = ar->new_file("world",100000))== NULL) {
			System->Printf (MSG_INITIALIZATION, "'world' file doesn't exists in archive!\n");
			return false;
		}
	}
  }
  else 
	  if (!strcmp(ext, ".zip")) { // require zip 
	  // We save the model in zip file
		new_world = new csWorld();
	//	LanguageLayer layer (new_world, NULL);
		ar = new_world->OpenWorldFile ( fullname);
		if (ar == NULL) {
			// unable to create/open archive
			System->Printf (MSG_INITIALIZATION, "Error opening archive %s, now try to create new one\n", fname);
			ar = new Archive(fname, TRUE);
			if (ar == NULL) {
				System->Printf (MSG_INITIALIZATION, "Cannot create new archive!\n", fname);
				delete new_world;
				return false;
			}
		}
		size_t fsize;
		// check for existence of file (not needed?)
		if (!(ar->file_exists(GLOBAL_MODEL->Name, &fsize))) {
			System->Printf (MSG_INITIALIZATION, "model file doesn't exists in archive!\n");
			f = ar->new_file (GLOBAL_MODEL->Name, 100000);
		}
		else {
			System->Printf (MSG_INITIALIZATION, "original world file size is %d\n", fsize);
			if ((f = ar->new_file(GLOBAL_MODEL->Name,100000))== NULL) {
				System->Printf (MSG_INITIALIZATION, "%s file doesn't exists in archive!\n",GLOBAL_MODEL->Name);
				delete new_world;
				return false;
			}
		}
	  } else {
		System->Printf (MSG_INITIALIZATION, "string compare return false\n");
		ar = NULL; // we don't save as zip archive
		GLOBAL_MODEL->SetFileName (fullname);
		f = fopen (fullname, "w");
		if (!f)
		{
			MessageBox (this, TEXT_ERROR, TEXT_CANTOPENWRITE);
			return false;
		}

	  }

#define PUTS(ar, s)					\
  {						\
    int _len = strlen (s);			\
    retcode =  (zPut (ar, f, s, _len));		\
  }

  bool retcode = true;
  d = PleaseWaitBox (this, TEXT_SAVINGWORLD);
  SetMouse (csmcWait);
  NextFrame ();

  char tmp[30];

  // Start output by writing the world header
  if (iEntireWorld) {
	ar->write (f, "WORLD\n(\n", strlen("WORLD\n(\n"));
	if (world->start_sector) {
	PUTS(ar, "START(  '");
	PUTS(ar, world->start_sector);
	sprintf (tmp, "', %.2f, %.2f, %.2f)\n", world->start_vec.x, world->start_vec.y, world->start_vec.z);                                          \
	PUTS(ar, tmp);
	}
  }
  // Query the used textures
  csStrVector Tex (8, 8);
  GetUsedTextures (Tex, iEntireWorld);

 
  // Output the textures first
  if (Tex.Length ())
  {
    PUTS (ar, "  TEXTURES (\n");
    for (int i = 0; i < Tex.Length (); i++)
    {
      PUTS (ar, "    ");
      PUTS (ar, (char *)Tex [i]);
      PUTS (ar, "\n");
    } /* endfor */
    PUTS (ar, "  )\n");
  } /* endif */

  // Now output the sprites/things/sectors
  if (iEntireWorld)
  {
    int i;
	  // save room/thing/sprite description first
	  // basically room is obsolete and not portal-able
	  for (i = 0; i < Models.Length (); i++) 
		if (((mz3DModel *)Models [i])->Type != csmtSector)
      if (!((mz3DModel *)Models [i])->Save (zPut, ar, f))
      {
        retcode = false;
        break;
      } /* endif */
	  // save Sector description last
    for (i = 0; i < Models.Length (); i++) 
		if (((mz3DModel *)Models [i])->Type == csmtSector)
	      if (!((mz3DModel *)Models [i])->Save (zPut, ar, f))
		  {
			retcode = false;
			break;
		  } /* endif */
	}
  else
    retcode = GLOBAL_MODEL->Save (zPut, ar, f);

  if (iEntireWorld)
	PUTS (ar, ")\n");

  if (ar == NULL) 
	fclose ((FILE *)f);
  else {
		ar->write_archive();
		delete new_world;
	}

  delete d;

  if (!retcode)
    MessageBox (this, TEXT_ERROR, TEXT_SAVEERROR);

  return retcode;
}

void MazeEditor::GetUsedTextures (csStrVector &Tex, bool iEntireWorld)
{
  int i, j;
  if (iEntireWorld)
    for (i = 0; i < Models.Length (); i++) {
      ((mz3DModel *)Models [i])->GetTextures (Tex);
    }
  else
    GLOBAL_MODEL->GetTextures (Tex);

  for (i = 0; i < Tex.Length (); i++) 
    for (j = i + 1; j < Tex.Length ();j++) 
      if (strcasecmp ((char *)Tex [i], (char *)Tex [j]) == 0)
      {
        Tex.Delete (j);
	j--;
      }
}

void MazeEditor::CreateModel (csModelType Type)
{
  if (Type != csmtRoom
   && Type != csmtSector
   && Type != csmtThing
   && Type != csmtSprite)
    return;
  CHK (Models.Push (new mz3DModel (Type, ModelsPath)));
  SelectModel (Models.Length () - 1);

  // Query texture file name
  csWindow *d;
  d = csFileDialog (this, TEXT_LOADTEXTURETITLE, WorldName, TEXT_LOADBUTTON);
  if (!d)
    return;

  if (Execute (d) != cscmdOK)
  {
    delete d;
    return ;
  }
  char fname [80], fullname[256];
  csComponent *d2 = d->GetChild (CSWID_CLIENT);
  if (d2)
  {
    csComponent *f = d2->GetChild (CSWID_FILENAME);
	f->GetText (fname , 80);
	//System->Printf (MSG_INITIALIZATION, "Name of file query result %s\n", fname);
  }

  csQueryFileDialog (d, fullname, 256);
  delete d2;
  delete d;

  if (strlen(fname) != 0) {
    Model->coating  = CSLoader::LoadTexture (world, fname, fullname);
	csNameObject::AddName(*(Model->coating),fname);
  } else {
    Model->coating  = CSLoader::LoadTexture (world, "default", "default");
	csNameObject::AddName(*(Model->coating),"default");
  }		
  // Addition Jul 2, 1999 by S.H.Or
  ITextureManager* txtmgr;
  System->piG3D->GetTextureManager (&txtmgr);

  csTextureHandle* th = Model->coating;
  ITextureHandle* handle;

  th->for_2d = false;
  th->for_3d = true;
  txtmgr->RegisterTexture (GetIImageFileFromImageFile (th->GetImageFile ()),
    &handle, th->for_3d, th->for_2d);
  th->SetTextureHandle (handle);
  // After loading the textures but BEFORE using them the
  // texture manager needs to be prepared.
  // the 9 is the number of reserved textures
  //txtmgr->Prepare (9);
  txtmgr->AllocPalette ();

  switch (Type) {
    case csmtSector:
      {
//      System->Printf(MSG_INITIALIZATION, "Creating sector\n");
        CHK (csSector* s = new csSector ());
        world->sectors.Push (s); 
      }
      break;
    case csmtThing:
      {
        CHK (csThing* t = new csThing ());
        world->thing_templates.Push (t);
      }
      break;
    case csmtSprite:
      {
        CHK (csSpriteTemplate* tmpl = new csSpriteTemplate ());
        world->sprite_templates.Push (tmpl);
      }
      break;
    default:
      break;
  }
// System->Printf (MSG_INITIALIZATION, "Current model %d\n", CurrentModel);
}
