// Filename: geomVertexArrayData.h
// Created by:  drose (17Mar05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

#ifndef GEOMVERTEXARRAYDATA_H
#define GEOMVERTEXARRAYDATA_H

#include "pandabase.h"
#include "copyOnWriteObject.h"
#include "geomVertexArrayFormat.h"
#include "geomEnums.h"
#include "pta_uchar.h"
#include "updateSeq.h"
#include "cycleData.h"
#include "cycleDataReader.h"
#include "cycleDataWriter.h"
#include "cycleDataStageReader.h"
#include "cycleDataStageWriter.h"
#include "pipelineCycler.h"
#include "pmap.h"
#include "reMutex.h"
#include "simpleLru.h"
#include "vertexDataBuffer.h"
#include "config_gobj.h"
#include "bamReader.h"

class PreparedGraphicsObjects;
class VertexBufferContext;
class GraphicsStateGuardianBase;
class GeomVertexArrayDataHandle;
class VertexDataBook;
class SimpleAllocatorBlock;

////////////////////////////////////////////////////////////////////
//       Class : GeomVertexArrayData
// Description : This is the data for one array of a GeomVertexData
//               structure.  Many GeomVertexData structures will only
//               define one array, with all data elements interleaved
//               (DirectX 8.0 and before insisted on this format);
//               some will define multiple arrays.  
//
//               DirectX calls this concept of one array a "stream".
//               It also closely correlates with the concept of a
//               vertex buffer.
//
//               This object is just a block of data.  In general, you
//               should not be directly messing with this object from
//               application code.  See GeomVertexData for the
//               organizing structure, and see
//               GeomVertexReader/Writer/Rewriter for high-level tools
//               to manipulate the actual vertex data.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_GOBJ GeomVertexArrayData : public CopyOnWriteObject, public SimpleLruPage, public GeomEnums {
private:
  GeomVertexArrayData();

protected:
  virtual PT(CopyOnWriteObject) make_cow_copy();

PUBLISHED:
  GeomVertexArrayData(const GeomVertexArrayFormat *array_format,
                      UsageHint usage_hint);
  GeomVertexArrayData(const GeomVertexArrayData &copy);
  void operator = (const GeomVertexArrayData &copy);
  virtual ~GeomVertexArrayData();
  ALLOC_DELETED_CHAIN(GeomVertexArrayData);

  int compare_to(const GeomVertexArrayData &other) const;

  INLINE const GeomVertexArrayFormat *get_array_format() const;

  INLINE UsageHint get_usage_hint() const;
  void set_usage_hint(UsageHint usage_hint);

  INLINE bool has_column(const InternalName *name) const;

  INLINE int get_num_rows() const;
  INLINE bool set_num_rows(int n);
  INLINE bool unclean_set_num_rows(int n);
  INLINE void clear_rows();

  INLINE int get_data_size_bytes() const;
  INLINE UpdateSeq get_modified() const;

  void output(ostream &out) const;
  void write(ostream &out, int indent_level = 0) const;

  INLINE bool request_resident() const;

  INLINE CPT(GeomVertexArrayDataHandle) get_handle(Thread *current_thread = Thread::get_current_thread()) const;
  INLINE PT(GeomVertexArrayDataHandle) modify_handle(Thread *current_thread = Thread::get_current_thread());

  void prepare(PreparedGraphicsObjects *prepared_objects);
  bool is_prepared(PreparedGraphicsObjects *prepared_objects) const;

  VertexBufferContext *prepare_now(PreparedGraphicsObjects *prepared_objects, 
                                   GraphicsStateGuardianBase *gsg);
  bool release(PreparedGraphicsObjects *prepared_objects);
  int release_all();

  INLINE static SimpleLru *get_independent_lru();
  INLINE static SimpleLru *get_small_lru();
  static void lru_epoch();
  INLINE static VertexDataBook &get_book();

public:
  virtual void evict_lru();

private:
  INLINE void set_lru_size(size_t lru_size);

  void clear_prepared(PreparedGraphicsObjects *prepared_objects);
  void reverse_data_endianness(unsigned char *dest, 
                               const unsigned char *source, size_t size);


  CPT(GeomVertexArrayFormat) _array_format;

  // A GeomVertexArrayData keeps a list (actually, a map) of all the
  // PreparedGraphicsObjects tables that it has been prepared into.
  // Each PGO conversely keeps a list (a set) of all the Geoms that
  // have been prepared there.  When either destructs, it removes
  // itself from the other's list.
  typedef pmap<PreparedGraphicsObjects *, VertexBufferContext *> Contexts;
  Contexts *_contexts;

  // This data is only needed when reading from a bam file.
  class BamAuxData : public BamReader::AuxData {
  public:
    // set true to indicate the data must be endian-reversed in
    // finalize().
    bool _endian_reversed;
  };

  // This is the data that must be cycled between pipeline stages.
  class EXPCL_PANDA_GOBJ CData : public CycleData {
  public:
    INLINE CData();
    INLINE CData(const CData &copy);
    INLINE void operator = (const CData &copy);

    virtual ~CData();
    ALLOC_DELETED_CHAIN(CData);
    virtual CycleData *make_copy() const;
    virtual void write_datagram(BamWriter *manager, Datagram &dg,
                                void *extra_data) const;
    virtual void fillin(DatagramIterator &scan, BamReader *manager,
                        void *extra_data);
    virtual TypeHandle get_parent_type() const {
      return GeomVertexArrayData::get_class_type();
    }

    UsageHint _usage_hint;
    VertexDataBuffer _buffer;
    UpdateSeq _modified;

    // This implements read-write locking.  Anyone who gets the data for
    // reading or writing will hold this mutex during the lock.
    ReMutex _rw_lock;
    
  public:
    static TypeHandle get_class_type() {
      return _type_handle;
    }
    static void init_type() {
      register_type(_type_handle, "GeomVertexArrayData::CData");
    }
    
  private:
    static TypeHandle _type_handle;

    friend class GeomVertexArrayData;
  };

  PipelineCycler<CData> _cycler;
  typedef CycleDataReader<CData> CDReader;
  typedef CycleDataWriter<CData> CDWriter;
  typedef CycleDataStageReader<CData> CDStageReader;
  typedef CycleDataStageWriter<CData> CDStageWriter;

  static SimpleLru _independent_lru;
  static SimpleLru _small_lru;
  static VertexDataBook _book;

public:
  static void register_with_read_factory();
  virtual void write_datagram(BamWriter *manager, Datagram &dg);
  PTA_uchar read_raw_data(BamReader *manager, DatagramIterator &source);
  virtual int complete_pointers(TypedWritable **plist, BamReader *manager);

  virtual void finalize(BamReader *manager);

protected:
  static TypedWritable *make_from_bam(const FactoryParams &params);
  void fillin(DatagramIterator &scan, BamReader *manager);

public:
  static TypeHandle get_class_type() {
    return _type_handle;
  }
  static void init_type() {
    CopyOnWriteObject::init_type();
    register_type(_type_handle, "GeomVertexArrayData",
                  CopyOnWriteObject::get_class_type());
    CData::init_type();
  }
  virtual TypeHandle get_type() const {
    return get_class_type();
  }
  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}

private:
  static TypeHandle _type_handle;

  friend class GeomCacheManager;
  friend class GeomVertexData;
  friend class PreparedGraphicsObjects;
  friend class GeomVertexArrayDataHandle;
};

////////////////////////////////////////////////////////////////////
//       Class : GeomVertexArrayDataHandle
// Description : This data object is returned by
//               GeomVertexArrayData::get_handle() or modify_handle().
//               As long as it exists, the data is locked; when the
//               last of these destructs, the data is unlocked.
//
//               Only one thread at a time may lock the data; other
//               threads attempting to lock the data will block.  A
//               given thread may simultaneously lock the data
//               multiple times.
//
//               This class serves in lieu of a pair of
//               GeomVertexArrayDataPipelineReader and
//               GeomVertexArrayDataPipelineWriter classes
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_GOBJ GeomVertexArrayDataHandle : public ReferenceCount, public GeomEnums {
private:
  INLINE GeomVertexArrayDataHandle(const GeomVertexArrayData *object, 
                                   Thread *current_thread,
                                   const GeomVertexArrayData::CData *_cdata, 
                                   bool writable);
  INLINE GeomVertexArrayDataHandle(const GeomVertexArrayDataHandle &);
  INLINE void operator = (const GeomVertexArrayDataHandle &);
  
PUBLISHED:
  INLINE ~GeomVertexArrayDataHandle();

public:
  ALLOC_DELETED_CHAIN_DECL(GeomVertexArrayDataHandle);

  INLINE Thread *get_current_thread() const;

  INLINE const unsigned char *get_read_pointer(bool force) const;
  unsigned char *get_write_pointer();

PUBLISHED:
  INLINE const GeomVertexArrayData *get_object() const;
  INLINE GeomVertexArrayData *get_object();

  INLINE const GeomVertexArrayFormat *get_array_format() const;
  INLINE UsageHint get_usage_hint() const;

  INLINE int get_num_rows() const;
  bool set_num_rows(int n);
  bool unclean_set_num_rows(int n);
  INLINE void clear_rows();

  INLINE int get_data_size_bytes() const;
  INLINE UpdateSeq get_modified() const;

  INLINE bool request_resident() const;

  void copy_data_from(const GeomVertexArrayDataHandle *other);
  void copy_subdata_from(size_t to_start, size_t to_size,
                         const GeomVertexArrayDataHandle *other,
                         size_t from_start, size_t from_size);

  INLINE string get_data() const;
  void set_data(const string &data);
  INLINE string get_subdata(size_t start, size_t size) const;
  void set_subdata(size_t start, size_t size, const string &data);

  INLINE void mark_used() const;
  
private:
  PT(GeomVertexArrayData) _object;
  Thread *_current_thread;
  GeomVertexArrayData::CData *_cdata;
  bool _writable;

public:
  static TypeHandle get_class_type() {
    return _type_handle;
  }
  static void init_type() {
    ReferenceCount::init_type();
    register_type(_type_handle, "GeomVertexArrayDataHandle",
                  ReferenceCount::get_class_type());
  }

private:
  static TypeHandle _type_handle;

  friend class GeomVertexArrayData;
};

INLINE ostream &operator << (ostream &out, const GeomVertexArrayData &obj);

#include "geomVertexArrayData.I"

#endif
