/* $NoKeywords: $ */
/*
//
// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
// McNeel & Associates.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
//				
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
*/

#include "opennurbs.h"

#if !defined(ON_COMPILING_OPENNURBS)
// This check is included in all opennurbs source .c and .cpp files to insure
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined 
// and the opennurbs .h files alter what is declared and how it is declared.
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
#endif

ON_COMPONENT_INDEX::ON_COMPONENT_INDEX() 
                   : m_type(ON_COMPONENT_INDEX::invalid_type),
                     m_index(-1)
{
}

ON_COMPONENT_INDEX::ON_COMPONENT_INDEX(
                                       ON_COMPONENT_INDEX::TYPE type,
                                       int index
                                       ) 
                   : m_type(type),
                     m_index(index)
{
}

ON_COMPONENT_INDEX::TYPE ON_COMPONENT_INDEX::Type(int i)
{
  TYPE t = invalid_type;
  switch((unsigned int)i)
  {
  case ON_COMPONENT_INDEX::no_type:            t = ON_COMPONENT_INDEX::no_type;            break;
  
  case ON_COMPONENT_INDEX::brep_vertex:        t = ON_COMPONENT_INDEX::brep_vertex;        break;
  case ON_COMPONENT_INDEX::brep_edge:          t = ON_COMPONENT_INDEX::brep_edge;          break;
  case ON_COMPONENT_INDEX::brep_face:          t = ON_COMPONENT_INDEX::brep_face;          break;
  case ON_COMPONENT_INDEX::brep_trim:          t = ON_COMPONENT_INDEX::brep_trim;          break;
  case ON_COMPONENT_INDEX::brep_loop:          t = ON_COMPONENT_INDEX::brep_loop;          break;
  
  case ON_COMPONENT_INDEX::mesh_vertex:        t = ON_COMPONENT_INDEX::mesh_vertex;        break;
  case ON_COMPONENT_INDEX::meshtop_vertex:     t = ON_COMPONENT_INDEX::meshtop_vertex;     break;
  case ON_COMPONENT_INDEX::meshtop_edge:       t = ON_COMPONENT_INDEX::meshtop_edge;       break;
  case ON_COMPONENT_INDEX::mesh_face:          t = ON_COMPONENT_INDEX::mesh_face;          break;
  case ON_COMPONENT_INDEX::mesh_ngon:          t = ON_COMPONENT_INDEX::mesh_ngon;          break;

  case ON_COMPONENT_INDEX::idef_part:          t = ON_COMPONENT_INDEX::idef_part;          break;
  case ON_COMPONENT_INDEX::polycurve_segment:  t = ON_COMPONENT_INDEX::polycurve_segment;  break;
  case ON_COMPONENT_INDEX::pointcloud_point:   t = ON_COMPONENT_INDEX::pointcloud_point;   break;
  case ON_COMPONENT_INDEX::group_member:       t = ON_COMPONENT_INDEX::group_member;       break;

  case ON_COMPONENT_INDEX::extrusion_bottom_profile: t = ON_COMPONENT_INDEX::extrusion_bottom_profile; break;
  case ON_COMPONENT_INDEX::extrusion_top_profile:    t = ON_COMPONENT_INDEX::extrusion_top_profile;    break;
  case ON_COMPONENT_INDEX::extrusion_wall_edge:      t = ON_COMPONENT_INDEX::extrusion_wall_edge;      break;
  case ON_COMPONENT_INDEX::extrusion_wall_surface:   t = ON_COMPONENT_INDEX::extrusion_wall_surface;   break;
  case ON_COMPONENT_INDEX::extrusion_cap_surface:    t = ON_COMPONENT_INDEX::extrusion_cap_surface;    break;
  case ON_COMPONENT_INDEX::extrusion_path:           t = ON_COMPONENT_INDEX::extrusion_path;           break;

  case ON_COMPONENT_INDEX::dim_linear_point:   t = ON_COMPONENT_INDEX::dim_linear_point;   break;
  case ON_COMPONENT_INDEX::dim_radial_point:   t = ON_COMPONENT_INDEX::dim_radial_point;   break;
  case ON_COMPONENT_INDEX::dim_angular_point:  t = ON_COMPONENT_INDEX::dim_angular_point;  break;
  case ON_COMPONENT_INDEX::dim_ordinate_point: t = ON_COMPONENT_INDEX::dim_ordinate_point; break;
  case ON_COMPONENT_INDEX::dim_text_point:     t = ON_COMPONENT_INDEX::dim_text_point;     break;
  }
  return t;
}


void ON_COMPONENT_INDEX::Set(
  ON_COMPONENT_INDEX::TYPE type,
  int index
  ) 
{
  m_type = type;
  m_index = index;
}

void ON_COMPONENT_INDEX::Set(
  ON_COMPONENT_INDEX::TYPE type,
  unsigned int index
  ) 
{
  m_type = type;
  m_index = (int)index;
}

void ON_COMPONENT_INDEX::UnSet()
{
  m_type = ON_COMPONENT_INDEX::invalid_type;
  m_index = -1;
}

bool ON_COMPONENT_INDEX::IsMeshComponentIndex() const
{
  bool rc = false;
  switch(m_type)
  {
  case ON_COMPONENT_INDEX::mesh_vertex:
  case ON_COMPONENT_INDEX::meshtop_vertex:
  case ON_COMPONENT_INDEX::meshtop_edge:
  case ON_COMPONENT_INDEX::mesh_face:
  case ON_COMPONENT_INDEX::mesh_ngon:
    if ( m_index >= 0 )
    {
      rc = true;
    }
    break;
  default:
    // intentionally skipping other ON_COMPONENT_INDEX::TYPE enum values
    break;
  }
  return rc;
}

bool ON_COMPONENT_INDEX::IsSubDComponentIndex() const
{
  bool rc = false;
  switch(m_type)
  {
  case ON_COMPONENT_INDEX::subd_vertex:
  case ON_COMPONENT_INDEX::subd_edge:
  case ON_COMPONENT_INDEX::subd_face:
    if ( -1 != m_index && 0 != m_index )
    {
      rc = true;
    }
    break;
  default:
    // intentionally skipping other ON_COMPONENT_INDEX::TYPE enum values
    break;
  }
  return rc;
}


bool ON_COMPONENT_INDEX::IsAnnotationComponentIndex() const
{
  bool rc = false;
  switch(m_type)
  {
  case ON_COMPONENT_INDEX::dim_linear_point:
  case ON_COMPONENT_INDEX::dim_radial_point:
  case ON_COMPONENT_INDEX::dim_angular_point:
  case ON_COMPONENT_INDEX::dim_ordinate_point:
  case ON_COMPONENT_INDEX::dim_text_point:
    if ( m_index >= 0 )
    {
      rc = true;
    }
    break;
  default:
    // intentionally skipping other ON_COMPONENT_INDEX::TYPE enum values
    break;
  }
  return rc;
}

bool ON_COMPONENT_INDEX::IsBrepComponentIndex() const
{
  bool rc = false;
  switch(m_type)
  {
  case ON_COMPONENT_INDEX::brep_vertex:
  case ON_COMPONENT_INDEX::brep_trim:
  case ON_COMPONENT_INDEX::brep_loop:
  case ON_COMPONENT_INDEX::brep_edge:
  case ON_COMPONENT_INDEX::brep_face:
    if ( m_index >= 0 )
    {
      rc = true;
    }
    break;
  default:
    // intentionally skipping other ON_COMPONENT_INDEX::TYPE enum values
    break;
  }
  return rc;
}

bool  ON_COMPONENT_INDEX::IsIDefComponentIndex() const
{
  return ( ON_COMPONENT_INDEX::idef_part == m_type && m_index >= 0 );
}

bool  ON_COMPONENT_INDEX::IsPolyCurveComponentIndex() const
{
  return ( ON_COMPONENT_INDEX::polycurve_segment == m_type && m_index >= 0 );
}

bool  ON_COMPONENT_INDEX::IsGroupMemberComponentIndex() const
{
  return ( ON_COMPONENT_INDEX::group_member == m_type && m_index >= 0 );
}

bool  ON_COMPONENT_INDEX::IsExtrusionProfileComponentIndex() const
{
  return ( (   ON_COMPONENT_INDEX::extrusion_bottom_profile  == m_type 
            || ON_COMPONENT_INDEX::extrusion_top_profile     == m_type
            )
            && m_index >= 0 
         );
}

bool  ON_COMPONENT_INDEX::IsExtrusionPathComponentIndex() const
{
  return ( ON_COMPONENT_INDEX::extrusion_path  == m_type 
           && m_index >= -1 
           && m_index <= 1
         );
}

bool  ON_COMPONENT_INDEX::IsExtrusionWallEdgeComponentIndex() const
{
  return ( ON_COMPONENT_INDEX::extrusion_wall_edge  == m_type 
           && m_index >= 0 
         );
}

bool  ON_COMPONENT_INDEX::IsExtrusionWallSurfaceComponentIndex() const
{
  return ( ON_COMPONENT_INDEX::extrusion_wall_surface  == m_type 
           && m_index >= 0
         );
}

bool  ON_COMPONENT_INDEX::IsExtrusionWallComponentIndex() const
{
  return ( (   ON_COMPONENT_INDEX::extrusion_wall_edge == m_type 
             || ON_COMPONENT_INDEX::extrusion_wall_surface == m_type
           )
           && m_index >= 0
         );
}

bool  ON_COMPONENT_INDEX::IsExtrusionComponentIndex() const
{
  return ( (   ON_COMPONENT_INDEX::extrusion_bottom_profile  == m_type 
            || ON_COMPONENT_INDEX::extrusion_top_profile     == m_type
            || ON_COMPONENT_INDEX::extrusion_wall_edge       == m_type
            || ON_COMPONENT_INDEX::extrusion_wall_surface    == m_type
            || ON_COMPONENT_INDEX::extrusion_cap_surface     == m_type
            || ON_COMPONENT_INDEX::extrusion_path            == m_type
            )
            && 
            (  m_index >= 0 
              || (-1 == m_index && ON_COMPONENT_INDEX::extrusion_path == m_type)
            )
         );
}

bool  ON_COMPONENT_INDEX::IsPointCloudComponentIndex() const
{
  return ( ON_COMPONENT_INDEX::pointcloud_point == m_type && m_index >= 0 );
}

static void ToStringHelper( ON_COMPONENT_INDEX ci, char* buffer, size_t sizeof_buffer )
{
  char* str = buffer;
  size_t str_capacity = sizeof_buffer/sizeof(buffer[0]);
  const char* s;
  const char* str_end;
  char ubuffer[32]; // unsigned int to string storage ubuffer
  unsigned int i, j;

  if ( nullptr == str || str_capacity <= 0 )
    return;

  str[--str_capacity] = 0;
  if (str_capacity <= 0)
    return;

  str_end = str + str_capacity;

  s = "m_type=";
  while ( str < str_end )
  {
    const char c = *s++;
    if (0 == c)
      break;

    *str++ = c;
  }

  switch(ci.m_type)
  {
  case ON_COMPONENT_INDEX::no_type:            s = "ON_COMPONENT_INDEX::no_type";            break;
  
  case ON_COMPONENT_INDEX::brep_vertex:        s = "ON_COMPONENT_INDEX::brep_vertex";        break;
  case ON_COMPONENT_INDEX::brep_edge:          s = "ON_COMPONENT_INDEX::brep_edge";          break;
  case ON_COMPONENT_INDEX::brep_face:          s = "ON_COMPONENT_INDEX::brep_face";          break;
  case ON_COMPONENT_INDEX::brep_trim:          s = "ON_COMPONENT_INDEX::brep_trim";          break;
  case ON_COMPONENT_INDEX::brep_loop:          s = "ON_COMPONENT_INDEX::brep_loop";          break;
  
  case ON_COMPONENT_INDEX::mesh_vertex:        s = "ON_COMPONENT_INDEX::mesh_vertex";        break;
  case ON_COMPONENT_INDEX::meshtop_vertex:     s = "ON_COMPONENT_INDEX::meshtop_vertex";     break;
  case ON_COMPONENT_INDEX::meshtop_edge:       s = "ON_COMPONENT_INDEX::meshtop_edge";       break;
  case ON_COMPONENT_INDEX::mesh_face:          s = "ON_COMPONENT_INDEX::mesh_face";          break;
  case ON_COMPONENT_INDEX::mesh_ngon:          s = "ON_COMPONENT_INDEX::mesh_ngon";          break;

  case ON_COMPONENT_INDEX::idef_part:          s = "ON_COMPONENT_INDEX::idef_part";          break;
  case ON_COMPONENT_INDEX::polycurve_segment:  s = "ON_COMPONENT_INDEX::polycurve_segment";  break;
  case ON_COMPONENT_INDEX::pointcloud_point:   s = "ON_COMPONENT_INDEX::pointcloud_point";   break;
  case ON_COMPONENT_INDEX::group_member:       s = "ON_COMPONENT_INDEX::group_member";       break;

  case ON_COMPONENT_INDEX::extrusion_bottom_profile: s = "ON_COMPONENT_INDEX::extrusion_bottom_profile"; break;
  case ON_COMPONENT_INDEX::extrusion_top_profile:    s = "ON_COMPONENT_INDEX::extrusion_top_profile";    break;
  case ON_COMPONENT_INDEX::extrusion_wall_edge:      s = "ON_COMPONENT_INDEX::extrusion_wall_edge";      break;
  case ON_COMPONENT_INDEX::extrusion_wall_surface:   s = "ON_COMPONENT_INDEX::extrusion_wall_surface";   break;
  case ON_COMPONENT_INDEX::extrusion_cap_surface:    s = "ON_COMPONENT_INDEX::extrusion_cap_surface";    break;
  case ON_COMPONENT_INDEX::extrusion_path:           s = "ON_COMPONENT_INDEX::extrusion_path";           break;

  case ON_COMPONENT_INDEX::dim_linear_point:   s = "ON_COMPONENT_INDEX::dim_linear_point";   break;
  case ON_COMPONENT_INDEX::dim_radial_point:   s = "ON_COMPONENT_INDEX::dim_radial_point";   break;
  case ON_COMPONENT_INDEX::dim_angular_point:  s = "ON_COMPONENT_INDEX::dim_angular_point";  break;
  case ON_COMPONENT_INDEX::dim_ordinate_point: s = "ON_COMPONENT_INDEX::dim_ordinate_point"; break;
  case ON_COMPONENT_INDEX::dim_text_point:     s = "ON_COMPONENT_INDEX::dim_text_point";     break;

  default: s = 0; break;
  }

  if ( 0 != s )
  {
    while ( str < str_end )
    {
      const char c = *s++;
      if (0 == c)
        break;

      *str++ = c;
    }
  }

  s = " m_index=";
  while ( str < str_end )
  {
    const char c = *s++;
    if (0 == c)
      break;

    *str++ = c;
  }

  if ( ci.m_index < 1 )
  {
    i = (unsigned int)(-ci.m_index);
    s = "-";
    while ( str < str_end )
    {
      const char c = *s++;
      if (0 == c)
        break;

      *str++ = c;
    }
  }
  else
  {
    i = (unsigned int)(ci.m_index);
  }
  
  j = sizeof(ubuffer)/sizeof(ubuffer[0]);
  j--;
  ubuffer[j] = 0;
  while(j > 0)
  {
    j--;
    ubuffer[j] = (char)('0'+i%10);
    i /= 10;
    if ( 0 == i )
      break;
  }

  s = &ubuffer[j];
  while ( str < str_end )
  {
    if ( 0 == (*str++ = *s++))
      break;
  }
}

void ON_COMPONENT_INDEX::Dump( 
  class ON_TextLog& text_log 
  )const
{
  char buffer[128];
  ToStringHelper(*this,buffer,sizeof(buffer));
  text_log.Print(buffer);
}

void ON_COMPONENT_INDEX::AppendToString( 
  class ON_String& s
  )const
{
  char buffer[128];
  ToStringHelper(*this,buffer,sizeof(buffer));
  s += buffer;
}

void ON_COMPONENT_INDEX::AppendToString( 
  class ON_wString& s
  )const
{
  char buffer[128];
  ToStringHelper(*this,buffer,sizeof(buffer));
  s += buffer;
}

bool ON_COMPONENT_INDEX::IsNotSet() const
{
  return (false == IsSet());
}

bool ON_COMPONENT_INDEX::IsSet() const
{
  bool rc = false;
  switch(m_type)
  {
  case ON_COMPONENT_INDEX::invalid_type:
    rc = false;
    break;

  case ON_COMPONENT_INDEX::no_type:
    rc = false;
    break;

  case ON_COMPONENT_INDEX::brep_vertex:
  case ON_COMPONENT_INDEX::brep_edge:
  case ON_COMPONENT_INDEX::brep_face:
  case ON_COMPONENT_INDEX::brep_trim:
  case ON_COMPONENT_INDEX::brep_loop:

  case ON_COMPONENT_INDEX::mesh_vertex:
  case ON_COMPONENT_INDEX::meshtop_vertex:
  case ON_COMPONENT_INDEX::meshtop_edge:
  case ON_COMPONENT_INDEX::mesh_face:
  case ON_COMPONENT_INDEX::mesh_ngon:

  case ON_COMPONENT_INDEX::idef_part:
  case ON_COMPONENT_INDEX::polycurve_segment:
  case ON_COMPONENT_INDEX::pointcloud_point:
  case ON_COMPONENT_INDEX::group_member:

  case ON_COMPONENT_INDEX::subd_vertex:
  case ON_COMPONENT_INDEX::subd_edge:
  case ON_COMPONENT_INDEX::subd_face:

    rc = (m_index != -1);
    break;

  default:
    rc = false;
    break;
  }
  return rc;
}


int ON_COMPONENT_INDEX::Compare( const ON_COMPONENT_INDEX* a, const ON_COMPONENT_INDEX* b )
{
  int i = ((int)a->m_type) - ((int)b->m_type);
  if ( 0 == i )
  {
    i = a->m_index - b->m_index;
  }
  return i;
}

bool ON_COMPONENT_INDEX::operator==(const ON_COMPONENT_INDEX& other) const
{
  return (m_type == other.m_type && m_index == other.m_index);
}

bool ON_COMPONENT_INDEX::operator!=(const ON_COMPONENT_INDEX& other) const
{
  return (m_type != other.m_type || m_index != other.m_index);
}

bool ON_COMPONENT_INDEX::operator<(const ON_COMPONENT_INDEX& other) const
{
  return (ON_COMPONENT_INDEX::Compare(this,&other) < 0);
}

bool ON_COMPONENT_INDEX::operator<=(const ON_COMPONENT_INDEX& other) const
{
  return (ON_COMPONENT_INDEX::Compare(this,&other) <= 0);
}

bool ON_COMPONENT_INDEX::operator>(const ON_COMPONENT_INDEX& other) const
{
  return (ON_COMPONENT_INDEX::Compare(this,&other) > 0);
}

bool ON_COMPONENT_INDEX::operator>=(const ON_COMPONENT_INDEX& other) const
{
  return (ON_COMPONENT_INDEX::Compare(this,&other) >= 0);
}

ON_ObjRefEvaluationParameter::ON_ObjRefEvaluationParameter()
: m_t_type(0)
, m_reserved(0)
{
  m_t[0] = ON_UNSET_VALUE;
  m_t[1] = ON_UNSET_VALUE;
  m_t[2] = ON_UNSET_VALUE;
  m_t[3] = ON_UNSET_VALUE;
}

void ON_ObjRefEvaluationParameter::Default()
{
  ON_ObjRefEvaluationParameter d;
  *this = d;
}

ON_ObjRefEvaluationParameter::~ON_ObjRefEvaluationParameter()
{
}

bool ON_ObjRefEvaluationParameter::Write( ON_BinaryArchive& archive ) const
{
  bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
  if (!rc)
    return rc;

  for(;;)
  {
    rc = archive.WriteInt(m_t_type);
    if (!rc) break;

    rc = archive.WriteComponentIndex(m_t_ci);
    if (!rc) break;

    rc = archive.WriteDouble(4,m_t);
    if (!rc) break;

    rc = archive.WriteInterval(m_s[0]);
    if (!rc) break;

    rc = archive.WriteInterval(m_s[1]);
    if (!rc) break;

    rc = archive.WriteInterval(m_s[2]);
    if (!rc) break;

    break;
  }

  if ( !archive.EndWrite3dmChunk() )
    rc = false;

  return rc;
}

bool ON_ObjRefEvaluationParameter::Read( ON_BinaryArchive& archive )
{
  Default();

  int major_version = 0;
  int minor_version = 0;
  bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
  if (!rc)
    return rc;

  for(;;)
  {
    rc = (1 == major_version);
    if (!rc) break;

    rc = archive.ReadInt(&m_t_type);
    if (!rc) break;

    rc = archive.ReadComponentIndex(m_t_ci);
    if (!rc) break;

    rc = archive.ReadDouble(4,m_t);
    if (!rc) break;

    rc = archive.ReadInterval(m_s[0]);
    if (!rc) break;

    rc = archive.ReadInterval(m_s[1]);
    if (!rc) break;

    rc = archive.ReadInterval(m_s[2]);
    if (!rc) break;

    break;
  }

  if ( !archive.EndRead3dmChunk() )
    rc = false;

  return rc;
}

ON_ObjRef::ON_ObjRef() 
          : m_uuid(ON_nil_uuid),
            m_geometry(0),
            m_parent_geometry(0),
            m_geometry_type(ON::unknown_object_type),
            m_runtime_sn(0),
            m_point(ON_3dPoint::UnsetPoint),
            m_osnap_mode(ON::os_none),
            m__proxy1(0),
            m__proxy2(0),
            m__proxy_ref_count(0)
{
}

void ON_ObjRef::Destroy()
{
  DecrementProxyReferenceCount();
  m_uuid = ON_nil_uuid;
  m_geometry = 0;
  m_parent_geometry = 0;
  m_geometry_type = ON::unknown_object_type;
  m_runtime_sn = 0;
  m_point = ON_3dPoint::UnsetPoint;
  m_osnap_mode = ON::os_none;
  m__proxy1 = 0;
  m__proxy2 = 0;
  m__proxy_ref_count = 0;
}


ON_ObjRef::ON_ObjRef( const ON_ObjRef& src ) 
          : m_uuid(src.m_uuid),
            m_geometry(src.m_geometry),
            m_parent_geometry(src.m_parent_geometry),
            m_component_index(src.m_component_index),
            m_geometry_type(src.m_geometry_type),
            m_runtime_sn(src.m_runtime_sn),
            m_point(src.m_point),
            m_osnap_mode(src.m_osnap_mode),
            m_evp(src.m_evp),
            m__iref(src.m__iref),
            m__proxy1(src.m__proxy1),
            m__proxy2(src.m__proxy2),
            m__proxy_ref_count(src.m__proxy_ref_count)
{
  if ( m__proxy_ref_count && *m__proxy_ref_count > 0 )
  {
    *m__proxy_ref_count = *m__proxy_ref_count + 1;
  }
}

ON_ObjRef& ON_ObjRef::operator=( const ON_ObjRef& src ) 
{
  if ( this != &src )
  {
    // Remove any reference this ON_ObjRef class 
    // may currently have.
    DecrementProxyReferenceCount();

    // copy the values from src
    m_uuid = src.m_uuid;
    m_geometry = src.m_geometry;
    m_parent_geometry = src.m_parent_geometry;
    m_component_index = src.m_component_index;
    m_geometry_type = src.m_geometry_type;
    m_runtime_sn = src.m_runtime_sn;
    m_point = src.m_point;
    m_osnap_mode = src.m_osnap_mode;
    m_evp = src.m_evp;
    m__iref = src.m__iref;
    m__proxy1 = src.m__proxy1;
    m__proxy2 = src.m__proxy2;
    m__proxy_ref_count = src.m__proxy_ref_count;

    if ( m__proxy_ref_count && *m__proxy_ref_count > 0 )
    {
      *m__proxy_ref_count = *m__proxy_ref_count + 1;
    }
  }

  return *this;
}

bool ON_ObjRef_IRefID::Write( ON_BinaryArchive& archive ) const
{
  bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 1 );
  if ( !rc )
    return false;

  for(;;)
  {
    rc = archive.WriteUuid(m_iref_uuid);
    if (!rc) break;

    rc = archive.WriteXform(m_iref_xform);
    if (!rc) break;

    rc = archive.WriteUuid(m_idef_uuid);
    if (!rc) break;

    rc = archive.WriteInt(m_idef_geometry_index);
    if (!rc) break;

    // 13 July 2006 - 1.1 - added m_component_index and m_evp
    rc = archive.WriteComponentIndex(m_component_index);
    if (!rc) break;

    rc = m_evp.Write(archive);
    if (!rc) break;

    break;
  }

  if ( !archive.EndWrite3dmChunk() )
    rc = false;
  return rc;
}

bool ON_ObjRef_IRefID::Read( ON_BinaryArchive& archive )
{
  Default();

  int major_version = 0;
  int minor_version = 0;
  bool rc = archive.BeginRead3dmChunk( 
                          TCODE_ANONYMOUS_CHUNK, 
                          &major_version, 
                          &minor_version );
  if ( !rc )
    return false;

  for(;;)
  {
    rc = (1 == major_version);
    if (!rc) break;

    rc = archive.ReadUuid(m_iref_uuid);
    if (!rc) break;

    rc = archive.ReadXform(m_iref_xform);
    if (!rc) break;

    rc = archive.ReadUuid(m_idef_uuid);
    if (!rc) break;

    rc = archive.ReadInt(&m_idef_geometry_index);
    if (!rc) break;

    if ( minor_version >= 1 )
    {
      // 13 July 2006 - 1.1 - added m_component_index and m_evp
      rc = archive.ReadComponentIndex(m_component_index);
      if (!rc) break;

      rc = m_evp.Read(archive);
      if (!rc) break;
    }

    break;
  }

  if ( !archive.EndRead3dmChunk() )
    rc = false;
  return rc;
}


bool ON_ObjRef::Write( ON_BinaryArchive& archive ) const
{
  bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 2 );
  if ( !rc )
    return false;

  for(;;)
  {
    rc = archive.WriteUuid(m_uuid);
    if (!rc) break;

    rc = archive.WriteComponentIndex(m_component_index);
    if (!rc) break;

    rc = archive.WriteInt(m_geometry_type);
    if (!rc) break;

    // Do not save the value of m_runtime_sn in the
    // archive.  When the file is read in, the object
    // will have a different value of m_runtime_sn.

    rc = archive.WritePoint(m_point);
    if (!rc) break;

    // Prior to 13 July 2006, the evaluation parameters
    // m_evp were members of ON_ObjRef.  That's why the
    // m_evp fields are written directly rather than
    // using m_evp.Write().
    rc = archive.WriteInt(m_evp.m_t_type);
    if (!rc) break;

    rc = archive.WriteComponentIndex(m_evp.m_t_ci);
    if (!rc) break;

    rc = archive.WriteDouble(4,m_evp.m_t);
    if (!rc) break;

    rc = archive.WriteArray(m__iref);
    if (!rc) break;

    // 1.1 IO fields
    rc = archive.WriteInterval(m_evp.m_s[0]);
    if (!rc) break;

    rc = archive.WriteInterval(m_evp.m_s[1]);
    if (!rc) break;

    // 1.2 IO fields
    rc = archive.WriteInterval(m_evp.m_s[2]);
    if (!rc) break;

    break;
  }

  if ( !archive.EndWrite3dmChunk() )
    rc = false;
  return rc;
}

bool ON_ObjRef::Read( ON_BinaryArchive& archive )
{
  int major_version = 0;
  int minor_version = 0;
  bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version );
  if ( !rc )
    return false;

  for(;;)
  {
    rc = (1 == major_version);
    if (!rc) break;

    rc = archive.ReadUuid(m_uuid);
    if (!rc) break;

    rc = archive.ReadComponentIndex(m_component_index);
    if (!rc) break;

    rc = archive.ReadInt(&m_geometry_type);
    if (!rc) break;

    rc = archive.ReadPoint(m_point);
    if (!rc) break;

    // Prior to 13 July 2006, the evaluation parameters
    // m_evp were members of ON_ObjRef.  That's why the
    // m_evp fields are read directly rather than
    // using m_evp.Read().
    rc = archive.ReadInt(&m_evp.m_t_type);
    if (!rc) break;

    rc = archive.ReadComponentIndex(m_evp.m_t_ci);
    if (!rc) break;

    rc = archive.ReadDouble(4,m_evp.m_t);
    if (!rc) break;

    rc = archive.ReadArray(m__iref);
    if (!rc) break;

    if ( minor_version >= 1 )
    {
      // 1.1 IO fields
      rc = archive.ReadInterval(m_evp.m_s[0]);
      if (!rc) break;
      rc = archive.ReadInterval(m_evp.m_s[1]);
      if (!rc) break;
      if ( minor_version >= 2 )
      {
        rc = archive.ReadInterval(m_evp.m_s[2]);
        if (!rc) break;
      }
    }

    break;
  }

  if ( !archive.EndRead3dmChunk() )
    rc = false;
  return rc;
}


ON_ObjRef::~ON_ObjRef()
{
  DecrementProxyReferenceCount();
}


void ON_ObjRef::RemapObjectId( const ON_SimpleArray<ON_UuidPair>& id_remap )
{
  // The cast is a lie but it works because ON_UuidPair::CompareFirstUuid
  // looks for an id in the first 16 bytes of the ON_UuidPair.
  int i = id_remap.BinarySearch((const ON_UuidPair*)&m_uuid,ON_UuidPair::CompareFirstUuid);
  if ( i >= 0 )
    m_uuid = id_remap[i].m_uuid[1];
}

int ON_ObjRef::ProxyReferenceCount() const
{
  return m__proxy_ref_count ? *m__proxy_ref_count : 0;
}

const ON_Brep* ON_BrepParent( const ON_Geometry* geo )
{
  const ON_Brep* brep = 0;

  if ( geo == nullptr )
    return nullptr;

  if  ( ON::brep_object == geo->ObjectType() )
  {
    brep = ON_Brep::Cast(geo);
  }
  else
  {
    // ComponentIndex() is the fastest way
    switch( geo->ComponentIndex().m_type )
    {
    case ON_COMPONENT_INDEX::brep_edge:
      {
        const ON_BrepEdge* edge = ON_BrepEdge::Cast(geo);
        if ( edge )
          brep = edge->Brep();
      }
      break;

    case ON_COMPONENT_INDEX::brep_face:
      {
        const ON_BrepFace* face = ON_BrepFace::Cast(geo);
        if ( face )
          brep = face->Brep();
      }
      break;

    case ON_COMPONENT_INDEX::brep_trim:
      {
        const ON_BrepTrim* trim = ON_BrepTrim::Cast(geo);
        if ( trim )
          brep = trim->Brep();
      }
      break;

    case ON_COMPONENT_INDEX::brep_loop:
      {
        const ON_BrepLoop* loop = ON_BrepLoop::Cast(geo);
        if ( loop )
          brep = loop->Brep();
      }
      break;

    default:
      // intentionally skipping other ON_COMPONENT_INDEX::TYPE enum values
      break;
    }
  }

  return brep;
}


const ON_Mesh* ON_MeshParent( const ON_Geometry* geo )
{
  const ON_Mesh* mesh = 0;

  if ( geo == nullptr )
    return nullptr;

  if  ( ON::mesh_object == geo->ObjectType() )
  {
    mesh = ON_Mesh::Cast(geo);
  }
  else if ( geo->ComponentIndex().IsMeshComponentIndex() )
  {
    const ON_MeshComponentRef* cref = ON_MeshComponentRef::Cast(geo);
    if ( cref )
      mesh = cref->Mesh();
  }

  return mesh;
}

bool ON_ObjRef::SetParentIRef( const ON_InstanceRef& iref,
                               ON_UUID iref_id,
                               int idef_geometry_index
                               )
{
  bool rc = false;

  if ( m__iref.Count() > 0 )
  {
    // nested irefs
    if (    0 == m__proxy2
         || 0 == m__proxy_ref_count 
         || *m__proxy_ref_count <= 0 )
    {
      return false;
    }
    ON_Geometry* proxy_geo = ON_Geometry::Cast(m__proxy2);
    if ( !proxy_geo )
      return false;
    if ( !proxy_geo->Transform(iref.m_xform) )
      return false;
    rc = true;
  }
  else if ( ON_COMPONENT_INDEX::invalid_type == m_component_index.m_type )
  {
    // handle top level objects    
    while ( m__proxy1 || m__proxy2 || m__proxy_ref_count )
    {
      // It it's an brep proxy for an extrusion object, then keep going.
      if (    0 != m__proxy1
           && 0 == m__proxy2 
           && 0 != m__proxy_ref_count 
           && 1 == *m__proxy_ref_count 
           && m__proxy1 != m_geometry
           && 0 != ON_Brep::Cast(m_geometry)
           )
      {
        // 13 July 2011 - Part of the fix for bug 87827
        // is to break here instead of returning false
        // because we have something like a brep proxy 
        // of an extrusion.
        break;        
      }
      return false;
    }

    if ( !m_geometry )
    {
      return false;
    }
    if ( m_geometry->ComponentIndex().m_type != ON_COMPONENT_INDEX::invalid_type )
    {
      return false;
    }
    if ( m_parent_geometry && m_geometry != m_parent_geometry )
    {
      return false;
    }
    ON_Geometry* proxy_geo = m_geometry->Duplicate();
    if ( !proxy_geo->Transform(iref.m_xform) )
    {
      delete proxy_geo;
      return false;
    }

    // 13 July 2011 - Part of the fix for bug 87827
    // was to put the m_geometry and m_parent_geometry
    // assignments after the call to SetProxy() which
    // was zeroing m_geometry and m_parent_geometry.
    SetProxy(0,proxy_geo,true);
    m_geometry = proxy_geo;
    m_parent_geometry = proxy_geo;
    rc = true;
  }
  else
  {
    // handle brep and mesh subobjects
    // create proxy object
    if ( m__proxy2 )
      return false;

    const ON_Brep* parent_brep = ON_BrepParent(m_parent_geometry);
    if ( !parent_brep)
      parent_brep = ON_BrepParent(m_geometry);
    if ( parent_brep )
    {
      // handle breps and their parts

      // 6 June 2013, Mikko, RH-9846:
      // This is a near facsimile of the 87827 fix above.
      // If it's a brep proxy component for an extrusion object, then keep going.
      if (    0 != m__proxy1
           && 0 == m__proxy2 
           && 0 != m__proxy_ref_count 
           && 1 == *m__proxy_ref_count 
           && m__proxy1 != m_geometry
           && m_geometry->ComponentIndex().IsBrepComponentIndex()
           )
      {
        // brep proxy component for an extrusion object, keep going
      }
      else
      if ( m__proxy1 || m__proxy_ref_count )
      {
        return false;
      }
      if ( m_parent_geometry != parent_brep && 0 != m_parent_geometry )
      {
        return false;
      }
      if ( m_geometry != parent_brep->BrepComponent(m_component_index) )
      {
        return false;
      }
      ON_Brep* proxy_brep = parent_brep->Duplicate();
      if ( !proxy_brep->Transform(iref.m_xform) )
      {
        delete proxy_brep;
        return false;
      }
      const ON_Geometry* brep_component = proxy_brep->BrepComponent(m_component_index);
      if ( !brep_component )
      {
        delete brep_component;
        return false;
      }
      SetProxy(0,proxy_brep,true);
      m_geometry        = brep_component;
      m_parent_geometry = proxy_brep;
      rc = true;
    }
    else
    {
      const ON_Mesh* parent_mesh = ON_MeshParent(m_parent_geometry);
      if ( !parent_mesh)
        parent_mesh = ON_MeshParent(m_geometry);
      if ( parent_mesh  )
      {
        // handle meshes and their parts
        if ( m_component_index.IsMeshComponentIndex() )
        {
          if ( m_geometry->ComponentIndex() != m_component_index )
            return false;
          ON_Mesh* proxy_mesh = parent_mesh->Duplicate();
          if ( !proxy_mesh->Transform(iref.m_xform) )
          {
            delete proxy_mesh;
            return false;
          }
          ON_Geometry* proxy_component = proxy_mesh->MeshComponent(m_component_index);
          if( !proxy_component )
          {
            delete proxy_mesh;
            return false;
          }
          m_geometry = proxy_component;
          m_parent_geometry = proxy_mesh;
          SetProxy(proxy_component,proxy_mesh,true);
          rc = true;
        }
        else
        {
          return false;
        }
      }
    }
  }

  if ( rc )
  {
    // This is a valid reference to a piece of geometry
    // in an instance definition.

    ON_Xform geometry_xform(ON_Xform::IdentityTransformation);
    if ( m__iref.Count() > 0 )
      geometry_xform = m__iref.Last()->m_geometry_xform;

    ON_ObjRef_IRefID& this_ref     = m__iref.AppendNew();
    this_ref.m_iref_uuid           = iref_id;
    this_ref.m_iref_xform          = iref.m_xform;
    this_ref.m_idef_uuid           = iref.m_instance_definition_uuid;
    this_ref.m_idef_geometry_index = idef_geometry_index;
    this_ref.m_geometry_xform      = iref.m_xform*geometry_xform;

    m_uuid = this_ref.m_iref_uuid;
  }

  return rc;
}

const ON_Object* ON_ObjRef::ProxyObject(int proxy_object_index) const
{
  return ( (1 == proxy_object_index) 
           ? m__proxy1 
           : ((2==proxy_object_index) ? m__proxy2 : 0) 
         );
}

void ON_ObjRef::SetProxy( 
          ON_Object* proxy1, 
          ON_Object* proxy2, 
          bool bCountReferences 
          )
{
  if ( m__proxy1 || m__proxy2 || m__proxy_ref_count )
  {
    // Remove any reference this ON_ObjRef class 
    // may currently have.
    DecrementProxyReferenceCount();
  }

  m__proxy1 = proxy1;
  m__proxy2 = proxy2;
  if ( bCountReferences && (m__proxy1 || m__proxy2) )
  {
    m__proxy_ref_count = (int*)onmalloc( sizeof(*m__proxy_ref_count) );
    *m__proxy_ref_count = 1;
  }
}

void ON_ObjRef::DecrementProxyReferenceCount()
{
  if ( 0 != m__proxy_ref_count ) 
  {
    if (*m__proxy_ref_count > 1) 
    {
      // Including this class, there are *m__proxy_ref_count
      // ON_ObjRef classes using m__proxy and m_geometry.
      // Decrement the reference counter and set the
      // pointers to zero.
      *m__proxy_ref_count = *m__proxy_ref_count - 1;
    }
    else if ( 1 == *m__proxy_ref_count )
    {
      // This is the only ON_ObjRef class using
      // m__proxy and m_geometry.  Set *m__proxy_ref_count
      // to zero (in case some rogue reference still exists),
      // delete m__proxy and m__proxy_ref_count, and
      // set m_geometry (which points to some part of m__proxy)
      // to nullptr.

      // Setting *m__proxy_ref_count to zero, prevents crashes
      // if somebody incorrectly uses memcpy() instead of the 
      // copy constructor or operator= to duplicate this class.
      *m__proxy_ref_count = 0;
      if ( m__proxy1 )
      {
        // delete proxy geometry
        delete m__proxy1;
      }
      if ( m__proxy2 )
      {
        // delete proxy geometry
        delete m__proxy2;
      }
      onfree(m__proxy_ref_count);
    }
    else
    {
      // Somebody did something along the lines of using
      // memcpy() instead of the copy constructor or operator=
      // to duplicate this class.
      ON_ERROR("ON_ObjRef::DecrementReferenceCount() *m__proxy_ref_count <= 0");
    }
  }

  // In all cases, setting these pointers to zero indicates this
  // ON_ObjRef is no longer referencing any runtime geometry.
  m__proxy_ref_count = 0;
  m__proxy1 = 0;
  m__proxy2 = 0;
  m_geometry = 0;
}

void ON_ObjRef_IRefID::Default()
{
  ON_ObjRef_IRefID d;
  *this = d;
}
