// Filename: dcAtomicField.cxx
// Created by:  drose (05Oct00)
//
////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////

#include "dcAtomicField.h"
#include "hashGenerator.h"
#include "dcindent.h"
#include "dcSimpleParameter.h"
#include "dcPacker.h"

#include <math.h>

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
DCAtomicField::
DCAtomicField(const string &name, DCClass *dclass) : DCField(name, dclass) {
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::Destructor
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
DCAtomicField::
~DCAtomicField() {
  Elements::iterator ei;  
  for (ei = _elements.begin(); ei != _elements.end(); ++ei) {
    delete (*ei);
  }
  _elements.clear();
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::as_atomic_field
//       Access: Published, Virtual
//  Description: Returns the same field pointer converted to an atomic
//               field pointer, if this is in fact an atomic field;
//               otherwise, returns NULL.
////////////////////////////////////////////////////////////////////
DCAtomicField *DCAtomicField::
as_atomic_field() {
  return this;
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::as_atomic_field
//       Access: Published, Virtual
//  Description: Returns the same field pointer converted to an atomic
//               field pointer, if this is in fact an atomic field;
//               otherwise, returns NULL.
////////////////////////////////////////////////////////////////////
const DCAtomicField *DCAtomicField::
as_atomic_field() const {
  return this;
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::get_num_elements
//       Access: Published
//  Description: Returns the number of elements (parameters) of the
//               atomic field.
////////////////////////////////////////////////////////////////////
int DCAtomicField::
get_num_elements() const {
  return _elements.size();
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::get_element
//       Access: Published
//  Description: Returns the parameter object describing the
//               nth element.
////////////////////////////////////////////////////////////////////
DCParameter *DCAtomicField::
get_element(int n) const {
  nassertr(n >= 0 && n < (int)_elements.size(), NULL);
  return _elements[n];
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::get_element_default
//       Access: Published
//  Description: Returns the pre-formatted default value associated
//               with the nth element of the field.  This is only
//               valid if has_element_default() returns true, in which
//               case this string represents the bytes that should be
//               assigned to the field as a default value.
//
//               If the element is an array-type element, the returned
//               value will include the two-byte length preceding the
//               array data.
//
//               This is deprecated; use get_element() instead.
////////////////////////////////////////////////////////////////////
string DCAtomicField::
get_element_default(int n) const {
  nassertr(n >= 0 && n < (int)_elements.size(), string());
  return _elements[n]->get_default_value();
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::has_element_default
//       Access: Published
//  Description: Returns true if the nth element of the field has a
//               default value specified, false otherwise.
//
//               This is deprecated; use get_element() instead.
////////////////////////////////////////////////////////////////////
bool DCAtomicField::
has_element_default(int n) const {
  nassertr(n >= 0 && n < (int)_elements.size(), false);
  return _elements[n]->has_default_value();
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::get_element_name
//       Access: Published
//  Description: Returns the name of the nth element of the field.
//               This name is strictly for documentary purposes; it
//               does not generally affect operation.  If a name is
//               not specified, this will be the empty string.
//
//               This method is deprecated; use
//               get_element()->get_name() instead.
////////////////////////////////////////////////////////////////////
string DCAtomicField::
get_element_name(int n) const {
  nassertr(n >= 0 && n < (int)_elements.size(), string());
  return _elements[n]->get_name();
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::get_element_type
//       Access: Published
//  Description: Returns the numeric type of the nth element of the
//               field.  This method is deprecated; use
//               get_element() instead.
////////////////////////////////////////////////////////////////////
DCSubatomicType DCAtomicField::
get_element_type(int n) const {
  nassertr(n >= 0 && n < (int)_elements.size(), ST_invalid);
  DCSimpleParameter *simple_parameter = _elements[n]->as_simple_parameter();
  nassertr(simple_parameter != (DCSimpleParameter *)NULL, ST_invalid);
  return simple_parameter->get_type();
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::get_element_divisor
//       Access: Published
//  Description: Returns the divisor associated with the nth element
//               of the field.  This implements an implicit
//               fixed-point system; floating-point values are to be
//               multiplied by this value before encoding into a
//               packet, and divided by this number after decoding.
//
//               This method is deprecated; use
//               get_element()->get_divisor() instead.
////////////////////////////////////////////////////////////////////
int DCAtomicField::
get_element_divisor(int n) const {
  nassertr(n >= 0 && n < (int)_elements.size(), 1);
  DCSimpleParameter *simple_parameter = _elements[n]->as_simple_parameter();
  nassertr(simple_parameter != (DCSimpleParameter *)NULL, 1);
  return simple_parameter->get_divisor();
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::output
//       Access: Public, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
void DCAtomicField::
output(ostream &out, bool brief) const {
  out << _name << "(";

  if (!_elements.empty()) {
    Elements::const_iterator ei = _elements.begin();
    output_element(out, brief, *ei);
    ++ei;
    while (ei != _elements.end()) {
      out << ", ";
      output_element(out, brief, *ei);
      ++ei;
    }
  }
  out << ")";

  output_flags(out);
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::write
//       Access: Public, Virtual
//  Description: Generates a parseable description of the object to
//               the indicated output stream.
////////////////////////////////////////////////////////////////////
void DCAtomicField::
write(ostream &out, bool brief, int indent_level) const {
  indent(out, indent_level);
  output(out, brief);
  out << ";";
  if (!brief && _number >= 0) {
    out << "  // field " << _number;
  }
  out << "\n";
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::generate_hash
//       Access: Public, Virtual
//  Description: Accumulates the properties of this field into the
//               hash.
////////////////////////////////////////////////////////////////////
void DCAtomicField::
generate_hash(HashGenerator &hashgen) const {
  DCField::generate_hash(hashgen);

  hashgen.add_int(_elements.size());
  Elements::const_iterator ei;
  for (ei = _elements.begin(); ei != _elements.end(); ++ei) {
    (*ei)->generate_hash(hashgen);
  }
  hashgen.add_int(get_flags());
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::get_nested_field
//       Access: Public, Virtual
//  Description: Returns the DCPackerInterface object that represents
//               the nth nested field.  This may return NULL if there
//               is no such field (but it shouldn't do this if n is in
//               the range 0 <= n < get_num_nested_fields()).
////////////////////////////////////////////////////////////////////
DCPackerInterface *DCAtomicField::
get_nested_field(int n) const {
  nassertr(n >= 0 && n < (int)_elements.size(), NULL);
  return _elements[n];
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::add_element
//       Access: Public
//  Description: Adds a new element (parameter) to the field.
//               Normally this is called only during parsing.  The
//               DCAtomicField object becomes the owner of the new
//               pointer and will delete it upon destruction.
////////////////////////////////////////////////////////////////////
void DCAtomicField::
add_element(DCParameter *element) {
  _elements.push_back(element);
  _num_nested_fields = (int)_elements.size();

  // See if we still have a fixed byte size.
  if (_has_fixed_byte_size) {
    _has_fixed_byte_size = element->has_fixed_byte_size();
    _fixed_byte_size += element->get_fixed_byte_size();
  }
  if (_has_fixed_structure) {
    _has_fixed_structure = element->has_fixed_structure();
  }
  if (!_has_range_limits) {
    _has_range_limits = element->has_range_limits();
  }
  if (!_has_default_value) {
    _has_default_value = element->has_default_value();
  }
  _default_value_stale = true;
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::do_check_match
//       Access: Protected, Virtual
//  Description: Returns true if the other interface is bitwise the
//               same as this one--that is, a uint32 only matches a
//               uint32, etc. Names of components, and range limits,
//               are not compared.
////////////////////////////////////////////////////////////////////
bool DCAtomicField::
do_check_match(const DCPackerInterface *other) const {
  return other->do_check_match_atomic_field(this);
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::do_check_match_atomic_field
//       Access: Protected, Virtual
//  Description: Returns true if this field matches the indicated
//               atomic field, false otherwise.
////////////////////////////////////////////////////////////////////
bool DCAtomicField::
do_check_match_atomic_field(const DCAtomicField *other) const {
  if (_elements.size() != other->_elements.size()) {
    return false;
  }
  for (size_t i = 0; i < _elements.size(); i++) {
    if (!_elements[i]->check_match(other->_elements[i])) {
      return false;
    }
  }

  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: DCAtomicField::output_element
//       Access: Private
//  Description: 
////////////////////////////////////////////////////////////////////
void DCAtomicField::
output_element(ostream &out, bool brief, DCParameter *element) const {
  element->output(out, brief);

  if (!brief && element->has_default_value()) {
    out << " = ";
    DCPacker packer;
    packer.set_unpack_data(element->get_default_value());
    packer.begin_unpack(element);
    packer.unpack_and_format(out);
    packer.end_unpack();
  }
}
