/*
  Maze editor: 3D model 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.
*/

#ifndef __ME_MODEL_H__
#define __ME_MODEL_H__

#include "csws/csws.h"
#include "csengine/csobjvec.h"
#include "csengine/thing.h"
#include "csengine/cssprite.h"
#include "me_vertx.h"
#include "me_light.h"
#include "me_spact.h"
#include "me_poly.h"

class mzDraftEditor;
class csThingTemplate;
class mzCameraView;
class csSector;
class csThing;
class csSprite3D;

/// Model type: room, thing or sprite
enum csModelType
{
  csmtUnknown,
  csmtRoom,
  csmtSector,
  csmtThing,
  csmtSprite
};

/**
 * This class encapsulates a 3D model object. It contains all routines
 * needed for editing, manipulating, loading and storing an abstract
 * 3D model.
 */
class mz3DModel : public csBase
{
  /**
   * Private class which defines a private vector used to store 3D vertices.
   * The model can contain very many vertices, so adding a virtual destructor
   * to mz3DVertex is worse than just making a replacement for csObjVector
   * which works exclusively with mz3DVertex.
   */
  class csVertexVector : public csVector
  {
  public:
    /// Initialize vertex vector object
    csVertexVector (int iLimit, int iStep) : csVector (iLimit, iStep) { }

    /// Delete all vertices before deleting object
    virtual ~csVertexVector ();

    /// Virtual function which frees a vector element
    virtual bool FreeItem (csSome Item);
  };

  /// An array of (csVertexVector *): vertices for all frames
  csObjVector V;
  /// All polygons in this model
  csObjVector P;
  /// All lights in this model
  csObjVector L;
  /// All 3D sprite actions
  csObjVector A;
  /// All 3D sprite templates
  csObjVector Sp;
  /// All 3D thing templates
  csObjVector T;
  /// All sectors in this model
  csObjVector S;
  /// Model file pathname
  char *FileName;
  // Active model frame
  int Frame;
  /// Frame names
  csStrVector FrameName;
  /// Texture (only for sprites)
  csTextureHandle *Texture;


public:
  /// Model type: room, thing or sprite
  csModelType Type;
  /// Model color (one of cs_Color_XXX) or -1 for default
  int Color;
  /// Model name
  char *Name;
  /// For level
  csTextureHandle  *coating;;

  /// We assumed that during level editing, even if the user delete a sector/sprite/thing
  /// the program only deletes the model but not the entry inside world 
  /// This is because we don't want to update the index into world accordingly
  /// Index into world for sector
  int sector_ind;
  /// Index into world for thing
  int thing_ind;
  /// Index into world for sprite
  int sprite_ind;
  /// Create the 3D model object, optionaly read from text buffer
  mz3DModel (csModelType iType, char *iFileName);
  /// Destroy the 3D model object
  virtual ~mz3DModel ();

  /// Query total number of vertices
  inline int Vertices (int iFrame = 0)
  { return ((csVertexVector *)V [iFrame])->Length (); }
  /// Query total number of polygons
  inline int Polygons ()
  { return P.Length (); }
  /// Query total number of sectors
  inline int Sectors ()
  { return S.Length (); }
  /// Query total number of things
  inline int Things ()
  { return T.Length (); }
  /// Query total number of sprite templates
  inline int Sprites ()
  { return Sp.Length (); }
  /// Query total number of lights
  inline int Lights ()
  { return L.Length (); }
  /// Query total number of frames
  inline int Frames ()
  { return V.Length (); }
  inline int Actions ()
  { return A.Length (); }

  /// Return a reference to given light
  inline mz3DLight& Light (int iLight)
  { return *(mz3DLight *) L [iLight]; }
  /// Return a reference to given vertex
  inline mz3DVertex& Vertex (int iVertex, int iFrame = -1)
  { return *(mz3DVertex *) (*(csVertexVector *)V [iFrame >= 0 ? iFrame : Frame])[iVertex]; }
  /// Return a reference to given polygon
  inline mz3DPolygon& Polygon (int iPolygon)
  { return *(mz3DPolygon *) P [iPolygon]; }
  /// Return a reference to given sector
  inline csSector Sector (int iSector)
  { return *(csSector *) S [iSector]; }
  /// Return a reference to given thing
  inline csThing Thing (int iThing)
  { return *(csThing *) T [iThing]; }
  /// Return a reference to given sprite template
  inline csSpriteTemplate SpriteTemplate (int iTemplate)
  { return *(csSpriteTemplate *) Sp [iTemplate]; }
  /// Return a reference to given action
  inline mzSpriteAction& Action (int iAction)
  { return *(mzSpriteAction *) A [iAction]; }

  /// Insert a Light and return its number
  bool InsertLight (mz3DLight *iLight, int *iNum = NULL);
  /// Delete a Light
  bool DeleteLight (int iLight);

  /// Insert a vertex and return its number
  bool InsertVertex (mz3DVertex *iVertex, int *iNum = NULL);
  /// Delete a vertex
  bool DeleteVertex (int iVertex);

  /// Insert a polygon and return its number
  bool InsertPolygon (mz3DPolygon *iPolygon, int *iNum = NULL);
  /// Delete a polygon
  bool DeletePolygon (int iPolygon);

  /// Insert a sector and return its number
  bool InsertSector (csSector *iSector, int *iNum = NULL);
  /// Delete a polygon
  bool DeleteSector (int iSector);

  /// Insert a thing and return its number
  bool InsertThing (csThing *iThing, int *iNum = NULL);
  /// Delete a polygon
  bool DeleteThing (int iThing);

  /// Insert a sprite action
  bool InsertAction (mzSpriteAction *iAction, int *iNum = NULL);
  /// Delete a sprite action
  bool DeleteAction (int iAction);

  /// Save this model into a stream
  bool Save (bool (*Put) (Archive *ar, void *iStream, char *iData, int iSize), Archive *ar, void *iStream);
  /// Load the model from a text buffer
  bool Load (char *iBuffer);

  /// Set name of this model
  void SetName (const char *iName);
  /// Get model file name
  char *GetFileName ()
  { return FileName; }
  /// Set file name and path of this model
  void SetFileName (char *iFileName);

  /// Get texture (sprites only)
  csTextureHandle *GetTexture () { return Texture; }
  /// Set texture (sprites only)
  void SetTexture (csTextureHandle *iTexture) { Texture = iTexture; }

  /// Set name of given frame
  void SetFrameName (int iFrame, char *iName);
  /// Get name of given frame
  inline char *GetFrameName (int iFrame)
  { return (char *) FrameName [iFrame]; }

  /// Set active frame number
  bool SetFrame (int iFrame, char *iFrameName = NULL);
  /// Get active frame number
  int GetFrame () { return Frame; }
  /// Delete a frame or clear frame if there are no more frames
  void DeleteFrame (int iFrame);

  /// Clear model
  void Clear (bool iVertices, bool iFaces, bool iLights, bool iFrames, bool iActions);

  /// Draw the model in draft view
  virtual void Draw (mzDraftEditor *Canvas, bool iActive, bool iModified, bool iCopy);

  /// Draw the model in 3D view
  virtual void Draw (mzCameraView *Canvas);

  /// Modify selected model vertices/polys using Canvas's modification parameters
  void Modify (mzDraftEditor *Canvas, bool iCopy);

  /// Load model from a light object
  bool Load (csLight *iSprite);
  /// Load model from a sector object
  bool Load (csSector *iSector);
  /// Load model from a thing template object
  bool Load (csThingTemplate *iThing);
  /// Load model from a sprite object
  bool Load (csSpriteTemplate *iSprite);

  /// Join a light to this model 
  bool Join (csLight *iSector);
  /// Join a sector to this model (if it is a room)
  bool Join (csSector *iSector, bool iForce = false);
  /// Join a thing to this model (if it is a thing)
  bool Join (csThingTemplate *iThing);
  /// Join a sprite to this model (if it is a sprite)
  bool Join (csSpriteTemplate *iSprite);

  /// Select, deselect or invert all vertices of model
  void SelectAllVertices (int iCode);
  /// Select, deselect or invert all polygons of model
  void SelectAllPolygons (int iCode);

  /// Delete all selected vertices
  void DelSelectedVertices ();
  /// Delete all selected polygons
  void DelSelectedPolygons ();

  /// Check if model has any selected elements
  bool HasSelection ();

  /// Find the minimal and maximal coordinates of model's vertices
  void GetBoundingBox (csVector3 &iMin, csVector3 &iMax, bool iOnlySelected);
  /// Find the minimal and maximal coordinates of model's vertices in window space
  void GetBoundingBox (mzDraftEditor *Canvas, csVector3 &iMin, csVector3 &iMax,
    bool iOnlySelected);

  /// Add all textures used in model to a list
  void GetTextures (csStrVector &iTexList);

private:
  void AddNewTexture (csStrVector &iTexList, csTextureHandle *iTexture);
};

#endif // __ME_MODEL_H__
