/**********************************************************************
This file is part of Crack dot Com's free source code release of Golgotha.
for information about compiling & licensing issues visit this URL
 If that doesn't help, contact Jonathan Clark at 
  golgotha_source@usa.net (Subject should have "GOLG" in it) 
***********************************************************************/

#ifndef G1_MAP_CELL_HH
#define G1_MAP_CELL_HH

#include "arch.hh"
#include "rotation.hh"
#include "player_type.hh"
#include "file/file.hh"
#include "range.hh"
#include "math/point.hh"
#include "obj3d.hh"
#include "g1_limits.hh"
#include "math/point.hh"
#include "f_tables.hh"
#include "map_man.hh"

class g1_object_class;
class g1_solid_class;
class g1_bullet_class;
class g1_map_view_class;
class i4_image_class;
class g1_solid_class;
class g1_saver_class;
class g1_loader_class;
class g1_path_manager_class;
class g1_map_class;
class g1_draw_context_class;
class g1_object_chain_class;
class i4_loader_class;
class i4_saver_class;

struct g1_visible_cell
{
  w8 x,y;
};


class g1_map_cell_class
{
public:
  enum { NO_CRITICAL=0 };


  g1_object_chain_class *object_list;  // lists of objects that are standing on this tile
                                       
  void add_object(g1_object_chain_class &c);
  i4_bool remove_object(g1_object_chain_class &c);

  //  g1_graph_node nearest_critical[G1_GRADE_LEVELS][2][G1_CRITICALS_PER_CELL];
  // tells which critical points are near this cell
  w16 scratch_x, scratch_y;

  w16   type;     // index into an array of ground object types (see tile.hh)
  
  w16   flags;
  w16   top_left_normal;        // 1-5-5-5  top bit indicates needs recalc
  w16   bottom_right_normal;    // 1-5-5-5  top bit indicates needs recalc

  void recalc_top_left(int cx, int cy);
  void recalc_bottom_right(int cx, int cy);

  void  get_top_left_normal(i4_3d_vector &v, int cx, int cy)
  {
    if (top_left_normal & 0x8000)
      recalc_top_left(cx, cy);

    v.x=g1_table_0_31_to_n1_1[(top_left_normal>>10)&31];
    v.y=g1_table_0_31_to_n1_1[(top_left_normal>>5)&31];
    v.z=g1_table_0_31_to_n1_1[(top_left_normal)&31];
  }

  void get_bottom_right_normal(i4_3d_vector &v, int cx, int cy)
  {
    if (bottom_right_normal & 0x8000)
      recalc_bottom_right(cx, cy);

    v.x=g1_table_0_31_to_n1_1[(bottom_right_normal>>10)&31];
    v.y=g1_table_0_31_to_n1_1[(bottom_right_normal>>5)&31];
    v.z=g1_table_0_31_to_n1_1[(bottom_right_normal)&31];
  }


  void  get_bottom_right_normal(float &x, float &y, float &z, int current_cell_offset);

  enum 
  {    
    FOGGED=             (1<<0),             // if fogged from local player's view 
    MIRRORED=           (1<<1),  // if building cell is mirrored across the x-axis

    ROTATE_BITS=        (1<<2)|(1<<3),

    IS_GROUND=          (1<<4),  // if this square doesn't block movement

    RADAR_NEEDS_RESTORE= (1<<5),            // if this spot on radar has been drawn
    RADAR_NEEDS_STACKED_RESTORE = (1<<6),   // if spot has been drawn more than once

    PLANAR=1<<13,      // if surface is planar and can be drawn with one quad rather than 2 tri's
    SCRATCH1=1<<14,
    SCRATCH2=1<<15
  };

  enum { SAVED_FLAGS = FOGGED | MIRRORED | ROTATE_BITS };

  void clear_restore_bits() { flags &= ~(RADAR_NEEDS_STACKED_RESTORE | RADAR_NEEDS_RESTORE); }
  i4_bool mirrored() { return (i4_bool)flags & MIRRORED; }
  

//   g1_player_type owner() 
//   { 
//     w32 ret=(flags&OWNER_BITS)>>8; 
//     //if (ret==31) return g1_no_player;
//     //else
//     return ret;
//   }

//   void set_owner(g1_player_type player)
//   {
//     //if (player==g1_no_player)
//     //  flags|=OWNER_BITS;
//     //else
//       flags=((flags & (~OWNER_BITS)) | (player<<8));
//   }

  void load_v2(i4_file_class *fp, w16 *tile_remap);   // version 2
  void load_v3(i4_file_class *fp, w16 *tile_remap);   // version 3
  void load_v4(i4_file_class *fp, w16 *tile_remap);   // version 4
  void load_v5(i4_file_class *fp, w16 *tile_remap);   // version 5
  void load_v6(i4_file_class *fp, w16 *tile_remap);   // version 6

  void set_rotation(g1_rotation_type type) { flags=(flags & (~ROTATE_BITS))|(type<<2); }
  g1_rotation_type get_rotation() { return (g1_rotation_type)((flags&ROTATE_BITS)>>2); }
  
  void init(w16 _type, g1_rotation_type rotation, i4_bool mirrored);

  i4_bool is_blocking() const { return !(flags&IS_GROUND); }

  void set_planar_flag(w8 h1, w8 h2, w8 h3, w8 h4)
  { 
    if (h1==h2 && h2==h3 && h3==h4)
      flags|=PLANAR;
    else
      flags&=~PLANAR;
  }

  i4_bool list_capable() { return i4_T; }

  g1_object_chain_class *get_solid_list();
  g1_object_chain_class *get_non_solid_list();
  g1_object_chain_class *get_obj_list()  { return object_list; }

  void unfog(int x, int y);
};


i4_bool g1_load_map_cells(g1_map_cell_class *list, int lsize, 
                          w16 *tile_remap, i4_loader_class *fp);

void g1_save_map_cells(g1_map_cell_class *list, int lsize, i4_saver_class *fp);

inline g1_map_cell_class *g1_get_cell(int x, int y)
{
  return g1_cells + y*g1_map_width + x;
}

#endif