/*
//
// Copyright (c) 1993-2015 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_OBJECT_IMPLEMENT(ON_ModelComponent,ON_Object,"CF37CFB5-8E6F-435F-98FE-D20FA9E7DB6F");


bool ON_ModelComponent::IsValid(
  ON_TextLog* text_log
  ) const
{
  return ( m_runtime_serial_number > 0 );
}

const ON_wString ON_ModelComponent::ComponentTypeToString(
  ON_ModelComponent::Type component_type
  )
{
  switch (component_type)
  {
  case ON_ModelComponent::Type::Unset:
    return ON_wString("Unset");
    break;
  case ON_ModelComponent::Type::ModelGeometry:
    return ON_wString("ModelGeometry");
    break;
  case ON_ModelComponent::Type::Image:
    return ON_wString("EmbeddedFile");
    break;
  case ON_ModelComponent::Type::LinePattern:
    return ON_wString("LinePattern");
    break;
  case ON_ModelComponent::Type::Layer:
    return ON_wString("Layer");
    break;
  case ON_ModelComponent::Type::Group:
    return ON_wString("Group");
    break;
  case ON_ModelComponent::Type::TextStyle:
    return ON_wString("TextStyle");
    break;
  case ON_ModelComponent::Type::DimStyle:
    return ON_wString("AnnotationStyle");
    break;
  case ON_ModelComponent::Type::HatchPattern:
    return ON_wString("HatchPattern");
    break;
  case ON_ModelComponent::Type::InstanceDefinition:
    return ON_wString("InstanceDefinition");
    break;
  case ON_ModelComponent::Type::HistoryRecord:
    return ON_wString("HistoryRecord");
    break;
  case ON_ModelComponent::Type::TextureMapping:
    return ON_wString("TextureMapping");
    break;
  case ON_ModelComponent::Type::RenderMaterial:
    return ON_wString("RenderMaterial");
    break;
  case ON_ModelComponent::Type::RenderLight:
    return ON_wString("RenderLight");
    break;
  case ON_ModelComponent::Type::Mixed:
    return ON_wString("Mixed");
    break;
  default:
    ON_ERROR("invalid m_component_type value");
    break;
  }
  return ON_wString::EmptyString;
}

ON_ModelComponentTypeIterator::ON_ModelComponentTypeIterator() ON_NOEXCEPT
{
  const size_t types_capacity = sizeof(m_types)/sizeof(m_types[0]);
  for (size_t k = 0; k < types_capacity; k++)
    m_types[k] = ON_ModelComponent::Type::Unset;
}

ON_ModelComponentTypeIterator::ON_ModelComponentTypeIterator(
  size_t type_count,
  const ON_ModelComponent::Type* types
) ON_NOEXCEPT
{
  const size_t types_capacity = sizeof(m_types)/sizeof(m_types[0]);
  m_type_count
    = (nullptr != types && type_count > 0)
    ? (int)((type_count < types_capacity) ? type_count : types_capacity)
    : 0;
  memset(m_types, 0, sizeof(m_types));
  for (int i = 0; i < m_type_count; i++)
    m_types[i] = types[i];
  for (size_t k = (size_t)m_type_count; k < types_capacity; k++)
    m_types[k] = ON_ModelComponent::Type::Unset;
}

unsigned int ON_ModelComponentTypeIterator::TypeCount() const
{
  return (unsigned int)m_type_count;
}

ON_ModelComponent::Type ON_ModelComponentTypeIterator::FirstType()
{
  m_current_index = ON_UNSET_INT_INDEX;
  return NextType();
}

ON_ModelComponent::Type ON_ModelComponentTypeIterator::LastType()
{
  m_current_index = ON_UNSET_INT_INDEX;
  return PreviousType();
}

ON_ModelComponent::Type ON_ModelComponentTypeIterator::NextType()
{
  if (m_type_count > 0)
  {
    if (ON_UNSET_INT_INDEX == m_current_index)
      m_current_index = 0;
    else if (m_current_index < m_type_count)
      m_current_index++;
  }
  return CurrentType();
}
  
ON_ModelComponent::Type ON_ModelComponentTypeIterator::PreviousType()
{
  if (m_type_count > 0)
  {
    if (ON_UNSET_INT_INDEX == m_current_index)
      m_current_index = m_type_count-1;
    else if (m_current_index >= 0)
      m_current_index--;
  }
  return CurrentType();
}

ON_ModelComponent::Type ON_ModelComponentTypeIterator::CurrentType() const
{
  return (m_current_index >= 0 && m_current_index < m_type_count) ? m_types[m_current_index] : ON_ModelComponent::Type::Unset;
}

void ON_ModelComponent::Dump(
  ON_TextLog& text_log
  ) const
{
  const char* sUnset = "unset";
  if (false == text_log.IsTextHash())
  {
    // m_runtime_serial_number depends on how many components have
    // been contructed before this one. It must be suppressed when
    // calculating content hash values from text logs.
    text_log.Print("Model component %llu\n", m_runtime_serial_number);
  }
  text_log.PushIndent();

  // model serial number CRhinoDoc::RuntimeSerialNumber()
  text_log.Print("Model serial number = ");
  if (ModelSerialNumberIsSet())
  {
    const unsigned int model_sn = ModelSerialNumber();
    text_log.Print("%u", model_sn);
    text_log.PrintNewLine();
    // worksession reference model
    const unsigned int reference_model_sn = ReferenceModelSerialNumber();
    if (reference_model_sn > 0)
    {
      text_log.Print("Reference model = %u\n", reference_model_sn);
    }
    // linked instance defintion model
    const unsigned int idef_model_sn = InstanceDefinitionModelSerialNumber();
    if (idef_model_sn > 0)
    {
      text_log.Print("Instance definition = %u\n", idef_model_sn);
    }
  }
  else
  {
    text_log.Print(sUnset);
    text_log.PrintNewLine();
  }

  text_log.Print("Type = ");
  if (this->ComponentTypeIsSet())
  {
    text_log.Print(ON_ModelComponent::ComponentTypeToString(m_component_type));
  }
  else
  {
    text_log.Print(sUnset);
  }
  text_log.PrintNewLine();

  text_log.Print("Id = ");
  if (IdIsSet())
    text_log.Print(Id());
  else
    text_log.Print(sUnset);
  text_log.PrintNewLine();


  text_log.Print("Index = ");
  if (IndexIsSet())
    text_log.Print("%d", Index());
  else
    text_log.Print(sUnset);
  text_log.PrintNewLine();

  text_log.Print("Name = ");
  if (NameIsSet())
    text_log.Print(L"%ls", static_cast<const wchar_t*>(m_component_name));
  else
    text_log.Print(sUnset);
  text_log.PrintNewLine();

  const ON__UINT64 content_version_number = ContentVersionNumber();
  if (0 != content_version_number && false == text_log.IsTextHash())
  {
    // content_version_number values vary depending on how
    // the actual content was specified, even if the final content
    // is identical. It must must be suppressed when calculating 
    // content hash values from text logs.

    // Use PRIu64 to portably print 64-bit unsigned int in decimal format.
    text_log.Print("Content version number = %" PRIu64 "\n",content_version_number);
  }

  text_log.PopIndent();
}

void ON_ModelComponentReference::Dump(
  ON_TextLog& text_log
  ) const
{
  const ON_ModelComponent* model_component = ModelComponent();
  if ( nullptr == model_component )
    text_log.Print("Empty ON_ModelComponentReference\n");
  else
    model_component->Dump(text_log);
}

unsigned int ON_ModelComponent::SizeOf() const
{
  return (unsigned int)((sizeof(*this) - sizeof(ON_Object)) + ON_Object::SizeOf());
}

ON__UINT32 ON_ModelComponent::DataCRC(
  ON__UINT32 current_remainder
  ) const
{
  current_remainder = ON_CRC32(current_remainder, sizeof(m_locked_status), &m_locked_status);
  current_remainder = ON_CRC32(current_remainder, sizeof(m_set_status), &m_set_status);
  if (ModelSerialNumberIsSet())
  {
    current_remainder = ON_CRC32(current_remainder, sizeof(m_model_serial_number), &m_model_serial_number);
    current_remainder = ON_CRC32(current_remainder, sizeof(m_reference_model_serial_number), &m_reference_model_serial_number);
    current_remainder = ON_CRC32(current_remainder, sizeof(m_linked_idef_serial_number), &m_linked_idef_serial_number);
  }
  if (IdIsSet())
    current_remainder = ON_CRC32(current_remainder, sizeof(m_component_id), &m_component_id);
  if (ComponentTypeIsSet())
    current_remainder = ON_CRC32(current_remainder, sizeof(m_component_type), &m_component_type);
  if (IndexIsSet())
    current_remainder = ON_CRC32(current_remainder, sizeof(m_component_index), &m_component_index);
  if (NameIsSet())
  {
    ON_NameHash name_hash = NameHash();
    current_remainder = ON_CRC32(current_remainder, sizeof(name_hash), &name_hash);
  }
  return current_remainder;
}

ON_UUID ON_ModelComponent::ModelObjectId() const
{
  return Id();
}

static ON__UINT64 ON_ModelComponentContentVersionNumberOne(
  ON__UINT64 runtime_serial_number
  )
{
  // make it unlikely for classes created sequentually to have the same content version numbers.
  ON__UINT64 s = ON_CRC32(0, sizeof(runtime_serial_number), &runtime_serial_number );
  return (runtime_serial_number*0x100000000ULL | s);
}

ON_ModelComponent::ON_ModelComponent() ON_NOEXCEPT
  : m_runtime_serial_number(++ON_ModelComponent::Internal_RuntimeSerialNumberGenerator)
  , m_content_version_number(ON_ModelComponentContentVersionNumberOne(m_runtime_serial_number))
{}

static const ON_ModelComponent Unset;

ON_ModelComponent::Type ON_ModelComponent::ComponentTypeFromUnsigned(
  unsigned int component_type_as_unsigned
  )
{
  switch (component_type_as_unsigned)
  {
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::Unset);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::Image);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::LinePattern);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::Layer);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::Group);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::TextStyle);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::DimStyle);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::HatchPattern);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::InstanceDefinition);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::ModelGeometry);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::HistoryRecord);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::TextureMapping);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::RenderMaterial);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::RenderLight);
  ON_ENUM_FROM_UNSIGNED_CASE(ON_ModelComponent::Type::Mixed);
  }

  ON_ERROR("component_type_as_unsigned has invalid value.");
  return ON_ModelComponent::Type::Unset;
}

bool ON_ModelComponent::WriteModelComponentAttributes(
  class ON_BinaryArchive& archive,
  unsigned int attributes_filter
  ) const
{
  const int major_version = 1;
  const int minor_version = 0;
  if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version))
    return false;
  bool rc = false;

  for (;;)
  {
    unsigned int bit;
    bit = ON_ModelComponent::Attributes::IdAttribute & attributes_filter;
    const bool bWriteId = (0 != bit && IdIsSet());

    bit = ON_ModelComponent::Attributes::ParentIdAttribute & attributes_filter;
    const bool bWriteParentId = (0 != bit && ParentIdIsSet());

    bit = ON_ModelComponent::Attributes::IndexAttribute & attributes_filter;
    const bool bWriteIndex = (0 != bit && IndexIsSet());

    bit = ON_ModelComponent::Attributes::NameAttribute & attributes_filter;
    const bool bWriteName = (0 != bit && NameIsSet());

    bit = ON_ModelComponent::Attributes::ComponentStatusAttribute & attributes_filter;
    const bool bWriteComponentStatus = (0 != bit && ModelComponentStatusIsSet());

    ON__UINT32 write_bits = 0U;
    if ( bWriteId )
      write_bits |= 0x01U;
    if ( bWriteParentId )
      write_bits |= 0x02U;
    if ( bWriteIndex )
      write_bits |= 0x04U;
    if ( bWriteName )
      write_bits |= 0x08U;
    if ( bWriteComponentStatus )
      write_bits |= 0x10U;

    if (!archive.WriteInt(write_bits))
      break;
 
    if (bWriteId)
    {
      if (!archive.WriteUuid(Id()))
        break;
    }

    if (bWriteParentId)
    {
      if (!archive.WriteUuid(ParentId()))
        break;
    }

    if (bWriteIndex)
    {
      if (!archive.Write3dmReferencedComponentIndex(*this))
        break;
    }


    if (bWriteName)
    {
      if (!archive.WriteString(Name()))
        break;
    }
    
    if (bWriteComponentStatus)
    {
      const ON_ComponentStatus component_status = ModelComponentStatus();

      const ON__UINT32 locked_bit = 0x01;
      const ON__UINT32 hidden_bit = 0x02;
      // Additional states can be saved if that is reasonable.

      const ON__UINT32 mask 
        = locked_bit 
        | hidden_bit;
      ON__UINT32 value = 0U;
      if ( component_status.IsLocked() )
        value |= locked_bit;
      if ( component_status.IsHidden() )
        value |= hidden_bit;
      if (!archive.WriteInt(mask))
        break;
      if (!archive.WriteInt(value))
        break;
    }
    
    rc = true;
    break;
  }

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

bool ON_ModelComponent::ReadModelComponentAttributes(
  class ON_BinaryArchive& archive
  )
{
  int major_version = 10;
  int minor_version = 0;
  if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version))
    return false;

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

    ON__UINT32 write_bits = 0U;
    if (!archive.ReadInt(&write_bits))
      break;

    const bool bReadId = (0 != (write_bits & 0x01U));
    const bool bReadParentId (0 != (write_bits & 0x02U));
    const bool bReadIndex (0 != (write_bits & 0x04U));
    const bool bReadName (0 != (write_bits & 0x08U));
    const bool bReadComponentStatus = (0 != (write_bits & 0x10U));

    if (bReadId)
    {
      ON_UUID id = Id();
      if (!archive.ReadUuid(id))
        break;
      SetId(id);
    }

    if (bReadParentId)
    {
      ON_UUID parent_id = ParentId();
      if (!archive.ReadUuid(parent_id))
        break;
      SetParentId(parent_id);
    }

    if (bReadIndex)
    {
      int component_index = Index();
      if (!archive.ReadInt(&component_index))
        break;
      SetIndex(component_index);
    }


    if (bReadName)
    {
      ON_wString name;
      if (!archive.ReadString(name))
        break;
      SetName(name);
    }
    
    if (bReadComponentStatus)
    {
      ON__UINT32 mask = 0U;
      if (!archive.ReadInt(&mask))
        break;
      ON__UINT32 value = 0U;
      if (!archive.ReadInt(&value))
        break;

      const ON__UINT32 locked_bit = 0x01;
      const ON__UINT32 hidden_bit = 0x02;
      // Additional states can be saved if that is reasonable.

      const ON_ComponentStatus component_status0 = ModelComponentStatus();
      ON_ComponentStatus component_status = component_status0;
      if (0 != (mask & locked_bit))
      {
        const bool bIsLocked = (0 != (value & locked_bit));
        component_status.SetLockedState(bIsLocked);
      }
      if (0 != (mask & hidden_bit))
      {
        const bool bIsHidden = (0 != (value & hidden_bit));
        component_status.SetHiddenState(bIsHidden);
      }

      if ( component_status != component_status0 )
        SetModelComponentStatus(component_status);
    }
    
    rc = true;
    break;
  }

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


unsigned int ON_ModelComponent::CopyFrom(
  const ON_ModelComponent& src,
  unsigned int attributes_filter
  )
{
  unsigned int rc = 0;
  unsigned int bit;
  bool b;

  // Erase unused bits.
  attributes_filter &= ON_ModelComponent::Attributes::AllAttributes;

  // Cannot set locked attributes.
  bit = m_locked_status;
  attributes_filter &= ~bit;

  if ( 0 == attributes_filter )
    return rc; // nothing to change
  
  bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute & attributes_filter;
  if (0 != bit)
  {    
    b = (src.ModelSerialNumberIsSet())
      ? SetModelSerialNumber(src.ModelSerialNumber(),src.ReferenceModelSerialNumber(),src.InstanceDefinitionModelSerialNumber())
      : ClearModelSerialNumber();
    if (b)
      rc |= bit;
  }
  
  // component type must be set before parent id
  bit = ON_ModelComponent::Attributes::TypeAttribute & attributes_filter;
  if (0 != bit)
  {
    b = (src.ComponentTypeIsSet())
      ? SetComponentType(src.ComponentType())
      : ClearComponentType();
    if (b)
      rc |= bit;
  }

  bit = ON_ModelComponent::Attributes::IdAttribute & attributes_filter;
  if (0 != bit)
  {
    b = (src.IdIsSet())
      ? SetId(src.Id())
      : ClearId();
    if (b)
      rc |= bit;
  }

  bit = ON_ModelComponent::Attributes::ParentIdAttribute & attributes_filter;
  if (0 != bit)
  {
    b = (src.ParentIdIsSet())
      ? SetParentId(src.ParentId())
      : ClearParentId();
    if (b)
      rc |= bit;
  }

  bit = ON_ModelComponent::Attributes::IndexAttribute & attributes_filter;
  if (0 != bit)
  {
    b = (src.IndexIsSet())
      ? SetIndex(src.Index())
      : ClearIndex();
    if (b)
      rc |= bit;
  }

  bit = ON_ModelComponent::Attributes::NameAttribute & attributes_filter;
  if (0 != bit)
  {
    b = (src.NameIsSet())
      ? SetName(src.Name())
      : ClearName();
    if (b)
      rc |= bit;
  }

  bit = ON_ModelComponent::Attributes::ComponentStatusAttribute & attributes_filter;
  if (0 != bit)
  {
    b = (src.ModelComponentStatusIsSet())
      ? SetModelComponentStatus(src.ModelComponentStatus())
      : ClearModelComponentStatus();
    if (b)
      rc |= bit;
  }

  return rc;
}

ON_ModelComponent::ON_ModelComponent(const ON_ModelComponent& src)
  : ON_Object(src)
  , m_runtime_serial_number(++ON_ModelComponent::Internal_RuntimeSerialNumberGenerator)
  , m_content_version_number(ON_ModelComponentContentVersionNumberOne(m_runtime_serial_number))
{
  CopyFrom(src,ON_ModelComponent::Attributes::AllAttributes);

  // The Type attribute is a special case.  Locking it in the copy
  // constructor allows the model components that are derived from
  // ON_ModelComponent to use the default copy constructor.
  const unsigned int bit = ON_ModelComponent::Attributes::TypeAttribute;
  if (0 != (bit & m_set_status) 
    && ON_ModelComponent::ComponentTypeIsValid(m_component_type)
    && src.ComponentTypeIsLocked()
    )
    m_locked_status |= bit;
}

ON_ModelComponent& ON_ModelComponent::operator=(const ON_ModelComponent& src)
{
  if (this != &src)
  {
    // copy user data
    ON_Object::operator=(src); 

    // copy ON_ModelComponent fields
    CopyFrom(src,ON_ModelComponent::Attributes::AllAttributes);
  }
  return *this;
}

ON_ModelComponent::ON_ModelComponent(
  ON_ModelComponent::Type component_type,
  const ON_ModelComponent& src
  ) ON_NOEXCEPT
  : ON_Object(src)
  , m_runtime_serial_number(++ON_ModelComponent::Internal_RuntimeSerialNumberGenerator)
  , m_content_version_number(ON_ModelComponentContentVersionNumberOne(m_runtime_serial_number))
  , m_component_type(component_type)
{
  if (ON_ModelComponent::Type::Unset != m_component_type)
  {
    // set and lock m_component_type
    m_set_status = ON_ModelComponent::Attributes::TypeAttribute;
    m_locked_status = ON_ModelComponent::Attributes::TypeAttribute;
  }
  CopyFrom(src,ON_ModelComponent::Attributes::AllAttributes);
}


ON_ModelComponent::ON_ModelComponent(
  ON_ModelComponent::Type component_type
  ) ON_NOEXCEPT
  : m_runtime_serial_number(++ON_ModelComponent::Internal_RuntimeSerialNumberGenerator)
  , m_content_version_number(ON_ModelComponentContentVersionNumberOne(m_runtime_serial_number))
  , m_component_type(component_type)
{
  if (ON_ModelComponent::Type::Unset != m_component_type)
  {
    m_locked_status = ON_ModelComponent::Attributes::TypeAttribute;
    m_set_status = ON_ModelComponent::Attributes::TypeAttribute;
  }
}

ON__UINT64 ON_ModelComponent::RuntimeSerialNumber() const
{
  return m_runtime_serial_number;
}

ON__UINT64 ON_ModelComponent::ContentVersionNumber() const
{
  return m_content_version_number;
}

void ON_ModelComponent::IncrementContentVersionNumber() const
{
  m_content_version_number++;
}

bool ON_ModelComponent::EraseIdentification(
  bool bIgnoreLocks
  )
{
  if (false == bIgnoreLocks)
  {
    if (IdIsLocked() && !(ON_ModelComponent::Unset.Id() == m_component_id))
    {
      ON_ERROR("Cannot erase id.");
      return false;
    }
    if (ParentIdIsLocked() && !(ON_ModelComponent::Unset.Id() == m_component_parent_id))
    {
      ON_ERROR("Cannot erase parent id.");
      return false;
    }
    if (NameIsLocked() && ON_ModelComponent::Unset.Name() != m_component_name)
    {
      ON_ERROR("Cannot erase name.");
      return false;
    }
    if (IndexIsLocked() && ON_ModelComponent::Unset.Index() != m_component_index)
    {
      ON_ERROR("Cannot erase index.");
      return false;
    }
  }

  m_component_id = ON_ModelComponent::Unset.Id();
  m_component_parent_id = ON_ModelComponent::Unset.Id();
  m_component_index = ON_ModelComponent::Unset.Index();
  m_component_name = ON_ModelComponent::Unset.Name();
  m_component_name_hash = ON_ModelComponent::Unset.NameHash();

  return true;
}


bool ON_ModelComponent::IsSystemComponent() const
{
  return (
    0 == m_content_version_number
    && 0 != (ON_ModelComponent::Attributes::SystemComponentAttribute & m_locked_status)
    );
}

bool ON_ModelComponent::SetAsSystemComponent()
{
  return Internal_SetAsSystemComponent(false);
}

bool ON_ModelComponent::SetAsUnsetSystemComponent()
{
  return Internal_SetAsSystemComponent(true);
}

bool ON_ModelComponent::Internal_SetAsSystemComponent(
  bool bUnsetSystemComponent
  )
{
  if (m_locked_status == 0xFFFFU)
  {
    ON_ERROR("component is already a system component.");
    return true;
  }

  const bool bIndexRequired = ON_ModelComponent::IndexRequired(m_component_type);
  const bool bUniqueNameRequired = ON_ModelComponent::UniqueNameRequired(m_component_type);
  const bool bUniqueNameIncludesParent = ON_ModelComponent::UniqueNameIncludesParent(m_component_type);

  for (;;)
  {
    if (ParentIdIsNotNil())
      break;

    if (bUniqueNameIncludesParent != ParentIdIsSet())
      break;
    
    if (IdIsNil())
    {
      if (!bUnsetSystemComponent)
        break;
    }
    else
    {
      if (bUnsetSystemComponent)
        break;
    }
    
    if ( bIndexRequired && false == bUnsetSystemComponent )
    {
      if (false == IndexIsSet())
        break;
      if (Index() >= 0)
        break;
      if (Index() <= ON_UNSET_INT_INDEX)
        break;
    }
    else
    {
      if (IndexIsSet())
        break;
    }
    
    if ( ON_ModelComponent::m_component_status != m_component_status ) 
      break;

    if (bUniqueNameRequired && false == bUnsetSystemComponent)
    {
      if (false == NameIsNotEmpty())
        break;
      if (false == NameHash().IsValidAndNotEmpty())
        break;
    }
    else
    {
      if (NameIsSet())
        return false;
      if (false == NameHash().IsEmptyNameHash())
        break;
    }

    m_locked_status = 0xFFFFU;
    m_content_version_number = 0;
    return true;
  }

  ON_ERROR("Invalid settings for a system component.");
  return false;
}

///////////////////////////////////////////////////////////////////////////////
//
// Model Serial Number interface
//

unsigned int ON_ModelComponent::ModelSerialNumber() const
{
  return m_model_serial_number;
}

unsigned int ON_ModelComponent::ReferenceModelSerialNumber() const
{
  return m_reference_model_serial_number;
}

unsigned int ON_ModelComponent::InstanceDefinitionModelSerialNumber() const
{
  return m_linked_idef_serial_number;
}

bool ON_ModelComponent::SetModelSerialNumber(
  unsigned int model_serial_number
  )
{
  return SetModelSerialNumber(
    model_serial_number,
    ON_ModelComponent::Unset.ReferenceModelSerialNumber(),
    ON_ModelComponent::Unset.InstanceDefinitionModelSerialNumber()
    );
}

bool ON_ModelComponent::SetModelSerialNumber(
  unsigned int model_serial_number,
  unsigned int reference_model_serial_number,
  unsigned int instance_definition_model_serial_number
  )
{
  const unsigned int bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute;
  if (0 != (bit & m_locked_status))
    return false;

  m_model_serial_number = model_serial_number;
  m_reference_model_serial_number = reference_model_serial_number;
  m_linked_idef_serial_number = instance_definition_model_serial_number;
  m_set_status |= bit;
  return true;

}

bool ON_ModelComponent::ClearModelSerialNumber()
{
  const unsigned int bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute;
  return (bit == ClearModelComponentAttributes(bit));
}

void ON_ModelComponent::LockModelSerialNumber()
{
  m_locked_status |= ON_ModelComponent::Attributes::ModelSerialNumberAttribute;
}

bool ON_ModelComponent::ModelSerialNumberIsLocked() const
{
  const unsigned int bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute;
  return (0 != (bit & m_locked_status));
}

bool ON_ModelComponent::ModelSerialNumberIsSet() const
{
  const unsigned int bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute;
  return (0 != (bit & m_set_status));
}

bool ON_ModelComponent::IsReferenceComponent() const
{
  return (0 != m_reference_model_serial_number || 0 != m_linked_idef_serial_number);
}


///////////////////////////////////////////////////////////////////////////////
//
// Name interface
//
//

const wchar_t* ON_ModelComponent::GetName(
    ON_wString& component_name
    ) const
{
  component_name = static_cast<const wchar_t*>(m_component_name);
  return static_cast<const wchar_t*>(component_name);
}

const ON_wString ON_ModelComponent::Name() const
{
  return NameIsSet() ? m_component_name : ON_wString::EmptyString;
}

const ON_wString ON_ModelComponent::DeletedName() const
{
  return DeletedNameIsSet() ? m_component_name : ON_wString::EmptyString;
}

const wchar_t* ON_ModelComponent::NameAsPointer() const
{
  return static_cast<const wchar_t*>(Name());
}

const ON_wString ON_ModelComponent::RemoveAllReferencePrefixDelimiters(
  const wchar_t* name
)
{
  if ( nullptr == name || 0 == name[0] )
    return ON_wString::EmptyString;

  ON_wString local(name);
  local.Replace(ON_ModelComponent::ReferencePrefixDelimiter, nullptr);

  return local.IsNotEmpty() ? local : ON_wString::EmptyString;
}

static const ON_wString Internal_RemoveTrailingNameToken(
  const wchar_t* name,
  const ON_wString& token
)
{
  if ( nullptr == name || 0 == name[0] || token.IsEmpty() )
    return ON_wString::EmptyString;

  ON_wString local(name);

  wchar_t* s0 = local.Array();
  if (nullptr == s0)
    return ON_wString::EmptyString;

  // remove any reference name delimiters
  const unsigned int d_length = token.Length();
  const unsigned int local_length = (unsigned int)local.Length();
  if (local_length < d_length )
    return local;

  if (ON_wString::EqualOrdinal(s0 + (local_length - d_length), token, false))
  {
    local.SetLength((local_length - d_length));
  }

  return local;
}



const ON_wString ON_ModelComponent::RemoveTrailingReferencePrefixDelimiter(
  const wchar_t* name
)
{
  return Internal_RemoveTrailingNameToken(name, ON_ModelComponent::ReferencePrefixDelimiter);
}

const ON_wString ON_ModelComponent::RemoveTrailingReferencePrefixSeparator(
  const wchar_t* name
)
{
  return Internal_RemoveTrailingNameToken(name, ON_ModelComponent::ReferencePrefixSeparator);
}

const ON_wString ON_ModelComponent::RemoveTrailingNamePathSeparator(
  const wchar_t* name
)
{
  return Internal_RemoveTrailingNameToken(name, ON_ModelComponent::NamePathSeparator);
}

//const ON_wString ON_ModelComponent::WorksessionReferenceNamePrefix(
//  const wchar_t* reference_prefix,
//  bool bAppendReferenceNamePrefixDelimiter
//)
//{
//  ON_wString s(reference_prefix);
//
//  for (;;)
//  {
//    const int length0 = s.Length();
//    s.TrimLeftAndRight();
//    s.TrimLeftAndRight(L"[]");
//    const int length1 = s.Length();
//    if (length1 <= 0 || length1 >= length0)
//      break;
//  }
//
//  if (s.IsEmpty())
//    return ON_wString::EmptyString;
//
//  ON_wString worksession_prefix = L"[ ";
//  worksession_prefix += s;
//  worksession_prefix += L" ]";
//  return worksession_prefix;
//}

static const wchar_t* InternalStringBeginsWithToken(
  const wchar_t* str,
  const wchar_t* token
)
{
  if (nullptr == str || nullptr == token || 0 == token[0])
    return nullptr;
  while (*str == *token && 0 != *token)
  {
    str++;
    token++;
  }
  return (0 == *token) ? str : nullptr;
}

const wchar_t* ON_ModelComponent::IsReferencePrefixDelimiter(
  const wchar_t* s
)
{
  return InternalStringBeginsWithToken(s, ON_ModelComponent::ReferencePrefixDelimiter);
}

const wchar_t* ON_ModelComponent::IsReferencePrefixSeparator(
  const wchar_t* s
)
{
  return InternalStringBeginsWithToken(s, ON_ModelComponent::ReferencePrefixSeparator);
}

const wchar_t* ON_ModelComponent::IsNamePathSeparator(
  const wchar_t* s
)
{
  return InternalStringBeginsWithToken(s, ON_ModelComponent::NamePathSeparator);
}

void ON_ModelComponent::SplitName(
  const wchar_t* name,
  ON_wString& reference_prefix,
  ON_wString& name_parent,
  ON_wString& name_leaf
)
{
  ON_wString local(name);
  wchar_t* str = local.Array();
  wchar_t* tail = str + local.Length();

  reference_prefix = ON_wString::EmptyString;
  name_parent = ON_wString::EmptyString;
  name_leaf = ON_wString::EmptyString;
  if (nullptr == str || 0 == str[0])
    return;

  bool bHaveReference = false;
  bool bHaveLeaf = false;
  while (tail > str)
  {
    tail--;

    if (false == bHaveLeaf)
    {
      const wchar_t* s = ON_ModelComponent::IsNamePathSeparator(tail);
      if (nullptr != s)
      {
        bHaveLeaf = true;
        name_leaf = s;
        *tail = 0;
        continue;
      }
    }

    if (false == bHaveReference)
    {
      const wchar_t* s = ON_ModelComponent::IsReferencePrefixDelimiter(tail);
      if (nullptr != s)
      {
        bHaveReference = true;
        if (false == bHaveLeaf)
        {
          bHaveLeaf = true;
          name_leaf = s;
        }
        else
        {
          name_parent = s;
        }
        *tail = 0;
        reference_prefix = ON_ModelComponent::RemoveAllReferencePrefixDelimiters(str);
        break;
      }
    }
  }

  if (false == bHaveReference)
  {
    if (false == bHaveLeaf)
      name_leaf = name;
    else
      name_parent = str;
  }

  reference_prefix.TrimLeftAndRight();
  name_parent.TrimLeftAndRight();
  name_leaf.TrimLeftAndRight();
}

const ON_wString ON_ModelComponent::NameReferencePrefix(const wchar_t * name)
{
  if (nullptr == name || 0 == name[0])
    return ON_wString::EmptyString;
  const wchar_t* tail = name;
  while (0 != *tail)
    tail++;

  while (tail > name)
  {
    tail--;
    const wchar_t* s = ON_ModelComponent::IsReferencePrefixDelimiter(tail);
    if (nullptr != s)
    {
      ON_wString name_reference = name;
      name_reference.SetLength(tail - name);
      name_reference = ON_ModelComponent::RemoveAllReferencePrefixDelimiters(name_reference);
      name_reference.TrimLeftAndRight();
      return name_reference;
    }
  }

  return ON_wString::EmptyString;  
}

const ON_wString ON_ModelComponent::NameParent(
  const wchar_t * name, 
  bool bIncludeReference
)
{
  if (nullptr == name || 0 == name[0])
    return ON_wString::EmptyString;

  if (false == bIncludeReference)
  {
    ON_wString name_reference;
    ON_wString name_parent;
    ON_wString name_leaf;
    SplitName(name, name_reference, name_parent, name_leaf);
    return name_parent;
  }

  const wchar_t* tail = name;
  while (0 != *tail)
    tail++;

  while (tail > name)
  {
    tail--;
    const wchar_t* s = ON_ModelComponent::IsNamePathSeparator(tail);
    if (nullptr != s)
    {
      ON_wString name_parent = name;
      name_parent.SetLength(tail - name);
      name_parent.TrimLeftAndRight();
      if (bIncludeReference)
        return name_parent;
    }
  }

  return ON_wString::EmptyString;    
}

const ON_wString ON_ModelComponent::NameLeaf(
  const wchar_t* name
)
{
  if (nullptr == name || 0 == name[0])
    return ON_wString::EmptyString;
  const wchar_t* tail = name;
  while (0 != *tail)
    tail++;

  while( tail > name )
  {
    tail--;

    const wchar_t* s = ON_ModelComponent::IsNamePathSeparator(tail);
    if ( nullptr == s )
      s = ON_ModelComponent::IsReferencePrefixDelimiter(tail);
    if (nullptr != s)
    {
      ON_wString name_leaf = s;
      name_leaf.TrimLeftAndRight();
      return name_leaf; // can be empty when name ends in a delimiter or separator or spaces
    }
  }

  ON_wString name_leaf = name;
  name_leaf.TrimLeftAndRight();

  return name_leaf;
}

const ON_wString ON_ModelComponent::RemoveReferencePrefix(const wchar_t * name)
{
  if (nullptr == name || 0 == name[0])
    return ON_wString::EmptyString;
  const wchar_t* tail = name;
  while (0 != *tail)
    tail++;

  while (tail > name)
  {
    tail--;
    const wchar_t* s = ON_ModelComponent::IsReferencePrefixDelimiter(tail);
    if (nullptr != s)
    {
      ON_wString name_no_reference = s;
      name_no_reference.TrimLeftAndRight();
      return name_no_reference;
    }
  }

  ON_wString name_no_reference(name);
  name_no_reference.TrimLeftAndRight();
  return name_no_reference;
}


const ON_NameHash& ON_ModelComponent::Internal_NameHash() const
{
  if (m_component_name_hash.IsEmptyNameHash() && m_component_name.IsNotEmpty())
  {
    // lazy evaulation because SHA-1 calculation takes appreciable time

    // For components whose names are in a tree structure, like layers,
    // the parent id must be included.
    // For all other components, the parent id must be ignored.
    const ON_UUID name_parent_id 
      = ON_ModelComponent::UniqueNameIncludesParent(m_component_type)
      ? m_component_parent_id 
      : ON_nil_uuid;
    const bool bIgnoreCase = ON_ModelComponent::UniqueNameIgnoresCase(m_component_type);
    m_component_name_hash = ON_NameHash::Create(name_parent_id,m_component_name,bIgnoreCase);
  }
  return m_component_name_hash;
}

const ON_NameHash& ON_ModelComponent::NameHash() const
{
  return NameIsSet() ? Internal_NameHash() : ON_NameHash::EmptyNameHash;
}

const ON_NameHash ON_ModelComponent::NewNameHash(
  const wchar_t* new_name
) const
{
  ON_wString local_buffer(new_name);
  local_buffer.TrimLeftAndRight();
  if (local_buffer.IsNotEmpty() && false == ON_ModelComponent::IsValidComponentName(local_buffer))
  {
    return ON_NameHash::UnsetNameHash;
  }

  const ON_UUID name_parent_id 
    = ON_ModelComponent::UniqueNameIncludesParent(ComponentType())
    ? Id()
    : ON_nil_uuid;
  return ON_NameHash::Create(name_parent_id, local_buffer);
}

const ON_NameHash& ON_ModelComponent::DeletedNameHash() const
{
  return DeletedNameIsSet() ? Internal_NameHash() : ON_NameHash::EmptyNameHash;
}

bool ON_ModelComponent::NameIsEmpty() const
{
  // should return true when the name is not set.
  return NameIsSet() ? m_component_name.IsEmpty() : true;
}

bool ON_ModelComponent::NameIsNotEmpty() const
{
  return NameIsSet() ? m_component_name.IsNotEmpty() : false;
}

bool ON_ModelComponent::ChangeName(
  const wchar_t* new_name,
  class ON_ComponentManifest* manifest
)
{
  const ON_ModelComponent::Type component_type = ComponentType();

  if (nullptr != manifest)
  {
    if (manifest->ItemFromId(component_type, Id()).IsUnset())
    {
      ON_ERROR("component is not in the manifest.");
      return false;
    }
  }

  const bool bUniqueNameRequired = ON_ModelComponent::UniqueNameRequired(component_type);
  ON_wString local_name(new_name);
  local_name.TrimLeftAndRight();
  new_name = local_name;
  const ON_UUID name_parent_id
    = ON_ModelComponent::UniqueNameIncludesParent(component_type)
    ? ParentId()
    : ON_nil_uuid;
  const ON_NameHash new_name_hash = ON_NameHash::Create(name_parent_id,new_name);
  if (local_name.IsEmpty() )
  {
    if (bUniqueNameRequired)
    {
      ON_ERROR("This component cannot have an empty name.");
      return false;
    }
  }
  else if (ON_ModelComponent::IsValidComponentName(new_name))
  {
    ON_ERROR("Invalid component name.");
    return false;
  }
  else if (bUniqueNameRequired && nullptr != manifest )
  {
    if (manifest->ItemFromNameHash(component_type, new_name_hash).IsValid())
    {
      // name is already being used.
      return false;
    }
  }

  if ( 
    nullptr != manifest
    && manifest->ChangeComponentName(Id(), component_type, ParentId(), new_name).IsUnset() 
    )
  {
    ON_ERROR("Unable to update manifest.");
    return false;
  }

  const bool rc
    = (local_name.IsEmpty())
    ? ClearName()
    : SetName(new_name);

  if (false == rc)
  {
    ON_ERROR("Unable to change component name.");
    return false;
  }

  return true;
}

bool ON_ModelComponent::SetName(
  const wchar_t* component_name
  )
{
  ON_wString local_buffer(component_name);
  local_buffer.TrimLeftAndRight();

  const unsigned int bit = ON_ModelComponent::Attributes::NameAttribute;
  if (0 != (bit & m_locked_status))
    return false;

  if (local_buffer.IsNotEmpty() && false == ON_ModelComponent::IsValidComponentName(local_buffer))
  {
    ON_ERROR("Invalid component_name parameter.");
    return false;
  }

  const bool bContentChange = (false == NameIsSet() || false == m_component_name.EqualOrdinal(local_buffer,false));

  m_component_name = local_buffer;

  // lazy evaluation is used to set m_component_name_hash
  m_component_name_hash = ON_NameHash::EmptyNameHash;

  m_set_status |= bit;
  m_set_status &= ~(ON_ModelComponent::Attributes::DeletedNameAttribute);

  if ( bContentChange )
    IncrementContentVersionNumber();

  return true;
}

bool ON_ModelComponent::SetLocalizedSystemComponentName(
  const wchar_t* system_component_localized_name
  )
{
  if (false == IsSystemComponent())
  {
    ON_ERROR("not a system component.");
    return false;
  }
  if (NameIsLocked())
  {
    ON_ERROR("name cannot be changed.");
    return false;
  }
  ON_wString local_buffer(system_component_localized_name);
  local_buffer.TrimLeftAndRight();
  system_component_localized_name = local_buffer;
  if (nullptr == system_component_localized_name || 0 == system_component_localized_name[0])
  {
    ON_ERROR("system_component_localized_name cannot be empty.");
    return false;
  }
  const ON_NameHash name_hash = ON_NameHash::Create(ParentId(),system_component_localized_name);
  if (false == name_hash.IsValidAndNotEmpty())
  {
    ON_ERROR("system_component_localized_name is not valid.");
    return false;
  }

  m_component_name = local_buffer;
  m_component_name_hash = name_hash;
  return SetAsSystemComponent();
}

bool ON_ModelComponent::ClearName()
{
  const unsigned int bit = (ON_ModelComponent::Attributes::NameAttribute | ON_ModelComponent::Attributes::DeletedNameAttribute);
  return (bit == ClearModelComponentAttributes(bit));
}

void ON_ModelComponent::LockName()
{
  m_locked_status |= ON_ModelComponent::Attributes::NameAttribute;

  // If name is deleted, erase it. 
  m_set_status &= ~(ON_ModelComponent::Attributes::DeletedNameAttribute);
  if (0 == (ON_ModelComponent::Attributes::NameAttribute & m_set_status))
  {    
    m_component_name_hash = ON_NameHash::EmptyNameHash;
    m_component_name = ON_wString::EmptyString;
  }
}

void ON_ModelComponent::LockAllSettingsExceptName()
{
  const unsigned int name_bit = ON_ModelComponent::Attributes::NameAttribute;
  unsigned int mask = ~name_bit;
  mask &= ON_ModelComponent::Attributes::AllAttributes;
  m_locked_status |= mask;
}

bool ON_ModelComponent::NameIsLocked() const
{
  const unsigned int bit = ON_ModelComponent::Attributes::NameAttribute;
  return (0 != (bit & m_locked_status));
}

bool ON_ModelComponent::NameIsSet() const
{
  const unsigned int bit = ON_ModelComponent::Attributes::NameAttribute;
  return (0 != (bit & m_set_status));
}

bool ON_ModelComponent::DeletedNameIsSet() const
{
  const unsigned int bit = ON_ModelComponent::Attributes::DeletedNameAttribute;
  return (0 != (bit & m_set_status));
}

bool ON_ModelComponent::DeleteName()
{
  if ( NameIsLocked() )
    return false; // cannot modify a locked name.

  if ( false == NameIsSet() )
    return false; // nothing to delete.
  
  const ON__UINT16 name_bit = (ON__UINT16)ON_ModelComponent::Attributes::NameAttribute;
  const ON__UINT16 deleted_name_bit = (ON__UINT16)ON_ModelComponent::Attributes::DeletedNameAttribute;
  m_set_status &= ~(name_bit | deleted_name_bit);

  // do this for all m_component_name contents including empty names.
  // There is a difference between name being "unset" and name being "set to empty".
  //
  // name is now "deleted"
  m_set_status |= deleted_name_bit;
  IncrementContentVersionNumber();

  return DeletedNameIsSet();
}

bool ON_ModelComponent::UndeleteName()
{
  if ( NameIsLocked() )
    return false; // cannot modify a locked name.

  if ( false == DeletedNameIsSet() )
    return false; // nothing to delete.
  
  const ON__UINT16 name_bit = (ON__UINT16)ON_ModelComponent::Attributes::NameAttribute;
  const ON__UINT16 deleted_name_bit = (ON__UINT16)ON_ModelComponent::Attributes::DeletedNameAttribute;
  m_set_status &= ~(name_bit | deleted_name_bit);

  // do this for all m_component_name contents including empty names.
  // There is a difference between name being "unset" and name being "set to empty".
  //
  // name is now "undeleted"
  m_set_status |= name_bit;
  IncrementContentVersionNumber();

  return NameIsSet();
}

int ON_ModelComponent::CompareName(
  const wchar_t* other_name
  ) const
{
  return CompareName(ParentId(), other_name );
}

int ON_ModelComponent::CompareName(
  const ON_UUID& other_parent_id,
  const wchar_t* other_name
  ) const
{
  int rc
    = (ON_ModelComponent::UniqueNameIncludesParent(m_component_type) && NameIsSet())
    ? ON_UuidCompare(m_component_parent_id, other_parent_id)
    : 0;
  return
    (0 != rc)
    ? rc
    : ON_wString::CompareAttributeName(Name(), other_name);
}

int ON_ModelComponent::CompareId(
  const ON_ModelComponent& a,
  const ON_ModelComponent& b
  )
{
  return ON_UuidCompare(a.m_component_id, b.m_component_id);
}

int ON_ModelComponent::CompareNameAndId(
  const ON_ModelComponent& a,
  const ON_ModelComponent& b
  )
{
  int rc = ON_ModelComponent::CompareName(a, b);
  if (0 == rc)
    rc = ON_ModelComponent::CompareId(a, b);
  return rc;
}

int ON_ModelComponent::CompareIdAndName(
  const ON_ModelComponent& a,
  const ON_ModelComponent& b
  )
{
  int rc = ON_ModelComponent::CompareId(a, b);
  if (0 == rc)
    rc = ON_ModelComponent::CompareName(a, b);
  return rc;
}


int ON_ModelComponent::CompareName(
  const ON_ModelComponent& a,
  const ON_ModelComponent& b
  )
{
  const ON_UUID a_parent_id 
    = ON_ModelComponent::UniqueNameIncludesParent(a.m_component_type) && a.NameIsSet()
    ? a.ParentId()
    : ON_nil_uuid;
  const ON_UUID b_parent_id 
    = ON_ModelComponent::UniqueNameIncludesParent(b.m_component_type) && b.NameIsSet()
    ? b.ParentId()
    : ON_nil_uuid;

  int rc = ON_UuidCompare(a_parent_id,b_parent_id);

  return 
    (0 != rc)
    ? rc
    : ON_wString::CompareAttributeName(a.m_component_name,b.m_component_name);
}

int ON_ModelComponent::CompareNameExact(
  const wchar_t* other_name
  ) const
{
  return CompareNameExact(ParentId(),other_name);
}

int ON_ModelComponent::CompareNameExact(
  const ON_UUID& other_parent_id,
  const wchar_t* other_name
  ) const
{
  int rc = ON_ModelComponent::CompareName(other_parent_id,other_name);
  return 
    (0 != rc)
    ? rc
    : ON_wString::CompareOrdinal(m_component_name,other_name,false);
}

int ON_ModelComponent::CompareNameExact(
  const ON_ModelComponent& a,
  const ON_ModelComponent& b
  )
{
  int rc = ON_ModelComponent::CompareName(a,b);
  return 
    (0 != rc)
    ? rc
    : ON_wString::CompareOrdinal(a.m_component_name,b.m_component_name,false);
}

///////////////////////////////////////////////////////////////////////////////
//
// Type interface
//

ON_ModelComponent::Type ON_ModelComponent::ComponentType() const
{
  return m_component_type;
}

bool ON_ModelComponent::SetComponentType(
  ON_ModelComponent::Type component_type
  )
{
  const unsigned int bit = ON_ModelComponent::Attributes::TypeAttribute;
  if (0 != (bit & m_locked_status))
    return false;

  const bool bContentChange = (m_component_type != component_type);

  m_component_type = component_type;
  m_set_status |= bit;

  if ( bContentChange )
    IncrementContentVersionNumber();

  return true;
}

bool ON_ModelComponent::ClearComponentType()
{
  const unsigned int bit = ON_ModelComponent::Attributes::TypeAttribute;
  return (bit == ClearModelComponentAttributes(bit));
}

void ON_ModelComponent::LockComponentType()
{
  m_locked_status |= ON_ModelComponent::Attributes::TypeAttribute;
}

bool ON_ModelComponent::ComponentTypeIsLocked() const
{
  const unsigned int bit = ON_ModelComponent::Attributes::TypeAttribute;
  return (0 != (bit & m_locked_status));
}

bool ON_ModelComponent::ComponentTypeIsSet() const
{
  const unsigned int bit = ON_ModelComponent::Attributes::TypeAttribute;
  return (0 != (bit & m_set_status));
}

bool ON_ModelComponent::ComponentTypeIsValid(
  ON_ModelComponent::Type component_type
  )
{
  return (
    ON_ModelComponent::ComponentTypeFromUnsigned(static_cast<unsigned int>(component_type)) == component_type
    && ON_ModelComponent::Type::Unset != component_type
    );
}

bool ON_ModelComponent::ComponentTypeIsValidAndNotMixed(
  ON_ModelComponent::Type component_type
  )
{
  return 
    ON_ModelComponent::ComponentTypeIsValid(component_type)
    && ON_ModelComponent::Type::Mixed != component_type;
}

bool ON_ModelComponent::IsValidComponentName(
  const ON_wString& candidate_component_name
  )
{
  return ON_ModelComponent::IsValidComponentName(
    (size_t)candidate_component_name.Length(),
    static_cast<const wchar_t*>(candidate_component_name)
    );

}

bool ON_ModelComponent::IsValidComponentName(
  size_t length,
  const wchar_t* candidate_component_name
  )
{
  return 
    (length > 0 
    && length == (size_t)ON_wString::Length(candidate_component_name)
    && ON_ModelComponent::IsValidComponentName(candidate_component_name)
    );
}



bool ON_ModelComponent::IsValidComponentName(
  const class ON_ComponentManifest&  model_manfest,
  const ON_ModelComponent& model_component,
  bool bPermitReferencePrefix,
  ON_wString& valid_name
)
{
  valid_name = ON_wString::EmptyString;
  const ON_ModelComponent::Type model_component_type = model_component.ComponentType();

  const ON_wString original_name = model_component.Name();

  bool rc = false;
  for (;;)
  {
    if (false == ON_ModelComponent::IsValidComponentName(original_name))
    {
      break;
    }

    const bool bUniqueNameRequired = ON_ModelComponent::UniqueNameRequired(model_component_type);
    if (bPermitReferencePrefix && false == bUniqueNameRequired)
    {
      rc = true;
      break;
    }

    const class ON_ComponentManifestItem& item
      = (bUniqueNameRequired)
      ? model_manfest.ItemFromName(&model_component)
      : ON_ComponentManifestItem::UnsetItem;

    const bool bItemFromNameIsOK
      =  (item.ComponentRuntimeSerialNumber() == model_component.RuntimeSerialNumber())
      || item.IsUnset()
      || item.IsSystemComponent()
      ;

    if ( bItemFromNameIsOK && bPermitReferencePrefix)
    {
      rc = true;
      break;
    }

    ON_wString ref;
    ON_wString parent;
    ON_wString leaf;
    ON_ModelComponent::SplitName(
      original_name,
      ref,
      parent,
      leaf
    );
    if (bItemFromNameIsOK && ref.IsEmpty())
    {
      rc = true;
      break;
    }

    if (false == ON_ModelComponent::IsValidComponentName(leaf))
      break;

    const class ON_ComponentManifestItem& leaf_item
      = (bUniqueNameRequired)
      ? model_manfest.ItemFromName(model_component_type,model_component.ParentId(),leaf)
      : ON_ComponentManifestItem::UnsetItem;

    if (leaf_item.IsUnset() || leaf_item.IsSystemComponent())
    {
      valid_name = leaf;
      break;
    }

    valid_name = model_manfest.UnusedName(
      model_component.ComponentType(),
      model_component.ParentId(),
      leaf,
      nullptr,
      nullptr,
      0,
      nullptr
    );

    break;
  }

  if (rc)
  {
    valid_name = original_name;
  }
  else if (valid_name.IsEmpty())
  {
    valid_name = model_manfest.UnusedName(
      model_component_type,
      model_component.ParentId(),
      nullptr,
      nullptr,
      nullptr,
      0,
      nullptr
    );
  }

  return rc;
}


bool ON_ModelComponent::IsValidComponentNameFirstCodePoint(
  ON__UINT32 unicode_code_point
)
{
  return
    ON_IsValidUnicodeCodePoint(unicode_code_point)
    && unicode_code_point > ON_wString::Space
    && 127 != unicode_code_point
    && unicode_code_point != '('
    && unicode_code_point != ')'
    && unicode_code_point != '['
    && unicode_code_point != ']'
    && unicode_code_point != '{'
    && unicode_code_point != '}';
}

bool ON_ModelComponent::IsValidComponentNameFirstCodePoint(
  int length,
  const wchar_t* candidate_component_name
)
{
  if (nullptr == candidate_component_name || 0 == candidate_component_name[0])
    return false;

  if (length < 0)
  {
    for (length = 0; 0 != candidate_component_name[length]; length++)
    {
      if (length > 8)
        break; // 8 elements is plenty for all possible wchar_t encodings.
    }
  }

  ON_UnicodeErrorParameters e = ON_UnicodeErrorParameters::FailOnErrors;
  e.m_error_code_point = 0;
  ON__UINT32 unicode_code_point = 0;
  ON_DecodeWideChar(candidate_component_name, length, &e, &unicode_code_point);
  return ON_ModelComponent::IsValidComponentNameFirstCodePoint(unicode_code_point);
}

bool ON_ModelComponent::IsValidComponentName(
  const wchar_t* candidate_component_name 
  )
{
  bool bPermitInternalSpaces = true;
  bool rc = false;
  bool bLastCharWasSpace = false;
  // the first character in a component name cannot be a bracket, space or below
  if ( nullptr != candidate_component_name 
       && *candidate_component_name > ON_wString::Space 
       && *candidate_component_name != '(' 
       && *candidate_component_name != ')' 
       && *candidate_component_name != '[' 
       && *candidate_component_name != ']' 
       && *candidate_component_name != '{' 
       && *candidate_component_name != '}' 
       )
  {
    rc = true;
    for ( const wchar_t* name = candidate_component_name; 0 != *name && rc; name++ ) 
    {
#if (2 == ON_SIZEOF_WCHAR_T)
      if (0 == ON_IsValidUTF16Singleton((ON__UINT32)(name[0])))
      {
        if (ON_IsValidUTF16SurrogatePair((ON__UINT32)(name[0]), (ON__UINT32)(name[1])))
        {
          name++;
          continue;
        }
        return false;
      }
#elif (4 == ON_SIZEOF_WCHAR_T)
      if (0 == ON_IsValidUTF32Value((ON__UINT32)(name[0])))
      {
        return false;
      }
#endif
      if ( ON_wString::Space == *name )
      {
        rc = bPermitInternalSpaces;
        bLastCharWasSpace = true;
      }
      else if ( 127 == *name || *name < ON_wString::Space )
        rc = false;
      else
        bLastCharWasSpace = false;
    }
  }
  return (rc && !bLastCharWasSpace) ? true : false;
}

bool ON_ModelComponent::IndexRequired(
  ON_ModelComponent::Type component_type
  )
{
  bool bIndexRequired = false;
  switch (component_type)
  {
  case ON_ModelComponent::Type::Unset:
    ON_ERROR("Invalid component_type parameter.");
    break;
  case ON_ModelComponent::Type::Image:
    bIndexRequired = true;
    break;
  case ON_ModelComponent::Type::TextureMapping:
    bIndexRequired = true;
    break;
  case ON_ModelComponent::Type::RenderMaterial:
    // ON_Layer references render material by index
    bIndexRequired = true;
    break;
  case ON_ModelComponent::Type::LinePattern:
    // ON_Layer references line pattern by index
    bIndexRequired = true;
    break;
  case ON_ModelComponent::Type::Layer:
    bIndexRequired = true;
    break;
  case ON_ModelComponent::Type::Group:
    bIndexRequired = true;
    break;
  case ON_ModelComponent::Type::TextStyle:
    bIndexRequired = true;
    break;
  case ON_ModelComponent::Type::DimStyle:
    bIndexRequired = true;
    break;
  case ON_ModelComponent::Type::RenderLight:
    bIndexRequired = true;
    break;
  case ON_ModelComponent::Type::HatchPattern:
    bIndexRequired = true;
    break;
  case ON_ModelComponent::Type::InstanceDefinition:
    bIndexRequired = true;
    break;
  case ON_ModelComponent::Type::ModelGeometry:
    break;
  case ON_ModelComponent::Type::HistoryRecord:
    break;
  case ON_ModelComponent::Type::Mixed:
    ON_ERROR("Invalid component_type parameter.");
    break;
  default:
    ON_ERROR("Invalid component_type parameter.");
    break;
  }
  return bIndexRequired;
}

bool ON_ModelComponent::UniqueNameIncludesParent(
  ON_ModelComponent::Type component_type
  )
{
  bool bUniqueNameIncludesParent = false;
  // [Giulio] If this table is changed, then also 
  // RhinoCommon documentation in
  // Rhino.DocObjects.ModelComponent.FindName() / FindNameHash
  // will require appropriate tweaks.
  switch (component_type)
  {
  case ON_ModelComponent::Type::Unset:
    ON_ERROR("Invalid component_type parameter.");
    break;
  case ON_ModelComponent::Type::Image:
    break;
  case ON_ModelComponent::Type::TextureMapping:
    break;
  case ON_ModelComponent::Type::RenderMaterial:
    break;
  case ON_ModelComponent::Type::LinePattern:
    break;
  case ON_ModelComponent::Type::Layer:
    bUniqueNameIncludesParent = true;
    break;
  case ON_ModelComponent::Type::Group:
    break;
  case ON_ModelComponent::Type::TextStyle:
    break;
  case ON_ModelComponent::Type::DimStyle:
    break;
  case ON_ModelComponent::Type::RenderLight:
    break;
  case ON_ModelComponent::Type::HatchPattern:
    break;
  case ON_ModelComponent::Type::InstanceDefinition:
    break;
  case ON_ModelComponent::Type::ModelGeometry:
    break;
  case ON_ModelComponent::Type::HistoryRecord:
    break;
  case ON_ModelComponent::Type::Mixed:
    ON_ERROR("Invalid component_type parameter.");
    break;
  default:
    ON_ERROR("Invalid component_type parameter.");
    break;
  }
  return bUniqueNameIncludesParent;
}

bool ON_ModelComponent::UniqueNameRequired(
  ON_ModelComponent::Type component_type
  )
{
  // [Giulio] If this table is changed, then also 
  // Rhino.DocObjects.ModelComponent.ModelComponentTypeRequiresUniqueName()
  // will need the same tweak.

  bool bUniqueNameRequired = false;
  switch (component_type)
  {
  case ON_ModelComponent::Type::Unset:
    ON_ERROR("Invalid component_type parameter.");
    break;
  case ON_ModelComponent::Type::Image:
    break;
  case ON_ModelComponent::Type::TextureMapping:
    break;
  case ON_ModelComponent::Type::RenderMaterial:
    break;
  case ON_ModelComponent::Type::LinePattern:
    bUniqueNameRequired = true;
    break;
  case ON_ModelComponent::Type::Layer:
    bUniqueNameRequired = true;
    break;
  case ON_ModelComponent::Type::Group:
    bUniqueNameRequired = true;
    break;
  case ON_ModelComponent::Type::TextStyle:
    bUniqueNameRequired = true;
    break;
  case ON_ModelComponent::Type::DimStyle:
    bUniqueNameRequired = true;
    break;
  case ON_ModelComponent::Type::RenderLight:
    break;
  case ON_ModelComponent::Type::HatchPattern:
    bUniqueNameRequired = true;
    break;
  case ON_ModelComponent::Type::InstanceDefinition:
    bUniqueNameRequired = true;
    break;
  case ON_ModelComponent::Type::ModelGeometry:
    break;
  case ON_ModelComponent::Type::HistoryRecord:
    break;
  case ON_ModelComponent::Type::Mixed:
    ON_ERROR("Invalid component_type parameter.");
    break;
  default:
    ON_ERROR("Invalid component_type parameter.");
    break;
  }
  return bUniqueNameRequired;
}

bool ON_ModelComponent::UniqueNameIgnoresCase(
  ON_ModelComponent::Type component_type
)
{
  // This function must return true for components like ON_Material that do not require a unique name.
  return (
    // NO // ON_ModelComponent::UniqueNameRequired(component_type) &&
    ON_ModelComponent::Type::Group != component_type
    );
}

///////////////////////////////////////////////////////////////////////////////
//
// Id interface
//
bool ON_ModelComponent::SetIdentification(
  const class ON_ComponentManifestItem& manifest_item,
  const wchar_t* manifest_name,
  bool bSetId,
  bool bSetParentId,
  bool bSetName,
  bool bSetIndex
  )
{
  bool rc = true;

  if (bSetId && false == (manifest_item.Id() == Id()))
  {
    if (false == SetId(manifest_item.Id()))
    {
      ON_ERROR("id cannot be set");
      rc = false;
    }
  }

  const ON_UUID parent_id 
    = bSetParentId
    ? manifest_item.NameHash().ParentId()
    : ON_nil_uuid;

  if (bSetParentId && false == (parent_id == ParentId()))
  {
    if (false == SetParentId(parent_id))
    {
      ON_ERROR("parent id cannot be set");
      rc = false;
    }
  }


  if (bSetName && NameHash() != manifest_item.NameHash())
  {
    if ( manifest_item.NameHash() != ON_NameHash::Create(manifest_item.NameHash().ParentId(),manifest_name) )
    {
      ON_ERROR("manifest_name parameter is not valid.");
      rc = false;
    }
    else if (false == SetName(manifest_name) )
    {
      ON_ERROR("name cannot be set");
      rc = false;
    }
  }


  if (bSetIndex && Index() != manifest_item.Index() )
  {
    if (false == SetIndex(manifest_item.Index()))
    {
      ON_ERROR("index cannot be set");
      rc = false;
    }
  }

  return rc;
}

///////////////////////////////////////////////////////////////////////////////
//
// Id interface
//

const ON_UUID& ON_ModelComponent::Id() const
{
  return m_component_id;
}

bool ON_ModelComponent::IdIsNil() const
{
  return (ON_nil_uuid == m_component_id);
}

bool ON_ModelComponent::IdIsNotNil() const
{
  return !(ON_nil_uuid == m_component_id);
}

bool ON_ModelComponent::SetId(
  const ON_UUID& component_id
  )
{
  const unsigned int bit = ON_ModelComponent::Attributes::IdAttribute;
  if (0 != (bit & m_locked_status))
    return false;

  const bool bContentChange = (false == (m_component_id == component_id));
  m_component_id = component_id;
  m_set_status |= bit;
  if ( bContentChange )
    IncrementContentVersionNumber();

  return true;
}

const ON_UUID& ON_ModelComponent::IfIdIsNilSetId()
{
  return
    ( ON_nil_uuid == m_component_id)
    ? SetId()
    : m_component_id;
}

const ON_UUID& ON_ModelComponent::SetId()
{
  if (false == IdIsLocked())
  {
    SetId(ON_CreateId());
    return m_component_id;
  }
  return ON_nil_uuid;
}

const ON_UUID& ON_ModelComponent::SetAndLockId()
{
  const ON_UUID& new_id = SetId();
  if (!(ON_nil_uuid == new_id))
    LockId();
  return new_id;
}

bool ON_ModelComponent::ClearId()
{
  const unsigned int bit = ON_ModelComponent::Attributes::IdAttribute;
  return (bit == ClearModelComponentAttributes(bit));
}

void ON_ModelComponent::LockId()
{
  m_locked_status |= ON_ModelComponent::Attributes::IdAttribute;
}

bool ON_ModelComponent::IdIsLocked() const
{
  const unsigned int bit = ON_ModelComponent::Attributes::IdAttribute;
  return (0 != (bit & m_locked_status));
}

bool ON_ModelComponent::IdIsSet() const
{
  const unsigned int bit = ON_ModelComponent::Attributes::IdAttribute;
  return (0 != (bit & m_set_status));
}


///////////////////////////////////////////////////////////////////////////////
//
// Component Status interface
//
//

ON_ComponentStatus ON_ModelComponent::ModelComponentStatus() const
{
  return m_component_status;
}

bool ON_ModelComponent::SetModelComponentStatus(
  ON_ComponentStatus component_status
  )
{
  const unsigned int bit = ON_ModelComponent::Attributes::ComponentStatusAttribute;
  if (0 != (bit & m_locked_status))
    return false;

  const bool bContentChange = (m_component_status != component_status);

  m_component_status = component_status;
  m_set_status |= bit;

  if ( bContentChange )
    IncrementContentVersionNumber();

  return true;
}

bool ON_ModelComponent::ClearModelComponentStatus()
{
  const unsigned int bit = ON_ModelComponent::Attributes::ComponentStatusAttribute;
  return (bit == ClearModelComponentAttributes(bit));
}

void ON_ModelComponent::LockModelComponentStatus()
{
  m_locked_status |= ON_ModelComponent::Attributes::ComponentStatusAttribute;
}

bool ON_ModelComponent::ModelComponentStatusIsLocked() const
{
  const unsigned int bit = ON_ModelComponent::Attributes::ComponentStatusAttribute;
  return (0 != (bit & m_locked_status));
}

bool ON_ModelComponent::ModelComponentStatusIsSet() const
{
  const unsigned int bit = ON_ModelComponent::Attributes::ComponentStatusAttribute;
  return (0 != (bit & m_set_status));
}

bool ON_ModelComponent::IsDeleted() const
{
  return m_component_status.IsDeleted();
}

bool ON_ModelComponent::SetDeletedModelComponentState(
  bool bDeleted,
  ON_ComponentManifest* manifest
  )
{
  bDeleted = bDeleted ? true : false; // inusre exact "true"
  ON_ComponentStatus component_status = ModelComponentStatus();
  if (bDeleted == component_status.IsDeleted())
  {
    // nothing to change
    ON_ERROR("model_component deleted status = bDeleted");
  }
  else
  {
    component_status.SetDeletedState(bDeleted);
    if (false == SetModelComponentStatus(component_status))
    {
      ON_ERROR("cannot change component deleted state.");
    }
  }

  const ON_ComponentManifestItem& manifest_item
    = (nullptr != manifest)
    ? ((bDeleted || ON_nil_uuid == m_component_id) ? manifest->ItemFromComponentRuntimeSerialNumber(m_runtime_serial_number) : manifest->ItemFromId(m_component_id))
    : ON_ComponentManifestItem::UnsetItem;
  
  const bool bComponentInManifest 
    = nullptr != manifest 
    && manifest_item.Id() == m_component_id
    && (false == bDeleted || manifest_item.ComponentRuntimeSerialNumber() == this->m_runtime_serial_number);
    
  if (nullptr != manifest && false == bComponentInManifest )
  {
    ON_ERROR("component is not in manifest.");
  }

  if (bDeleted)
  {
    // delete component name
    if (DeletedNameIsSet())
    {
      ON_ERROR("component name is already deleted");
    }
    else if (NameIsSet())
    {
      if (false == DeleteName())
      {
        ON_ERROR("component name cannot be deleted.");
      }
    }

    if (bComponentInManifest)
    {
      // delete manifest item
      if (manifest_item.IsDeleted())
      {
        ON_ERROR("manifest item deleted status is already true.");
      }
      else
      {
        const ON_ComponentManifestItem& deleted_manifest_item = manifest->DeleteComponent(m_runtime_serial_number);
        if (false == deleted_manifest_item.IsDeleted())
        {
          ON_ERROR("unable to change manifest item deleted status to true.");
        }
      }
    }
  }
  else
  {
    // undelete component name
    ON_wString candidate_name;
    if (NameIsSet())
    {
      ON_ERROR("model_component name is already set");
      candidate_name = Name();
    }
    else if (DeletedNameIsSet())
    {
      candidate_name = DeletedName();
      if (false == UndeleteName())
      {
        ON_ERROR("cannot model_component deleted name cannot be restored.");
      }
    }

    if (bComponentInManifest)
    {
      // undelete manifest item
      if (false == manifest_item.IsDeleted())
      {
        ON_ERROR("doc_manifest item deleted status is already false.");
      }
      else
      {
        // When an component is being replaced, it is common for the old an new component
        // to have the same id but different RuntimeSerialNumber() values.
        ON_wString assigned_name;
        const ON_ComponentManifestItem& undeleted_manifest_item = manifest->UndeleteComponentAndChangeRuntimeSerialNumber(
          Id(),
          ParentId(),
          RuntimeSerialNumber(),
          candidate_name,
          assigned_name
          );
        if (false != undeleted_manifest_item.IsDeleted())
        {
          ON_ERROR("unable to change manifest item deleted status to false.");
        }
        SetName(assigned_name);
      }
    }
  }

  return (bDeleted == IsDeleted());
}

bool ON_ModelComponent::IsLocked() const
{
  return m_component_status.IsLocked();
}

void ON_ModelComponent::SetLockedModelComponentState(
  bool bLockedState
  )
{
  ON_ComponentStatus component_status = ModelComponentStatus();
  if (bLockedState != component_status.IsLocked())
  {
    component_status.SetLockedState(bLockedState);
    if (false == SetModelComponentStatus(component_status))
    {
      ON_ERROR("cannot change component locked state.");
    }
  }
}


bool ON_ModelComponent::IsHidden() const
{
  return m_component_status.IsHidden();
}

void ON_ModelComponent::SetHiddenModelComponentState(
  bool bHiddenState
  )
{
  ON_ComponentStatus component_status = ModelComponentStatus();
  if (bHiddenState != component_status.IsHidden())
  {
    component_status.SetHiddenState(bHiddenState);
    if (false == SetModelComponentStatus(component_status))
    {
      ON_ERROR("cannot change component hidden state.");
    }
  }
}


///////////////////////////////////////////////////////////////////////////////
//
// Index interface
//
//  The component index is intended to be unique among components with identical
//  types in the context of a model.
//

int ON_ModelComponent::Index() const
{
  return m_component_index;
}

int ON_ModelComponent::Index(
  int unset_index_value
  ) const
{
  return IndexIsSet() ? m_component_index : unset_index_value;
}

bool ON_ModelComponent::SetIndex(
  int component_index
  )
{
  const unsigned int bit = ON_ModelComponent::Attributes::IndexAttribute;
  if (0 != (bit & m_locked_status))
    return false;

  const bool bContentChange = (m_component_index !=  component_index);

  m_component_index = component_index;
  m_set_status |= bit;

  if ( bContentChange )
    IncrementContentVersionNumber();

  return true;
}

bool ON_ModelComponent::ClearIndex()
{
  const unsigned int bit = ON_ModelComponent::Attributes::IndexAttribute;
  return (bit == ClearModelComponentAttributes(bit));
}

void ON_ModelComponent::LockIndex()
{
  m_locked_status |= ON_ModelComponent::Attributes::IndexAttribute;
}

bool ON_ModelComponent::IndexIsLocked() const
{
  const unsigned int bit = ON_ModelComponent::Attributes::IndexAttribute;
  return (0 != (bit & m_locked_status));
}

bool ON_ModelComponent::IndexIsSet() const
{
  const unsigned int bit = ON_ModelComponent::Attributes::IndexAttribute;
  return (0 != (bit & m_set_status));
}  




///////////////////////////////////////////////////////////////////////////////
//
// ParentId interface
//
//  The component parent_id is intended to be unique among components with identical
//  types in the context of a model.
//

const ON_UUID& ON_ModelComponent::ParentId() const
{
  return m_component_parent_id;
}

bool ON_ModelComponent::ParentIdIsNil() const
{
  return (ON_nil_uuid == m_component_parent_id);
}

bool ON_ModelComponent::ParentIdIsNotNil() const
{
  return (!(ON_nil_uuid == m_component_parent_id));
}


bool ON_ModelComponent::SetParentId(
  const ON_UUID& component_parent_id
  )
{
  const unsigned int bit = ON_ModelComponent::Attributes::ParentIdAttribute;
  if (0 != (bit & m_locked_status))
    return false;

  const bool bContentChange = (m_component_parent_id !=  component_parent_id);

  m_component_parent_id = component_parent_id;
  m_set_status |= bit;

  if (bContentChange)
  {
    if (ON_ModelComponent::UniqueNameIncludesParent(m_component_type))
    {
      // The name hash includes the parent id for this type of component (like layers).
      // Lazy evaluation is used to set m_component_name_hash.
      m_component_name_hash = ON_NameHash::EmptyNameHash;
    }
    IncrementContentVersionNumber();
  }

  return true;
}

bool ON_ModelComponent::ClearParentId()
{
  const unsigned int bit = ON_ModelComponent::Attributes::ParentIdAttribute;
  return (bit == ClearModelComponentAttributes(bit));
}

void ON_ModelComponent::LockParentId()
{
  m_locked_status |= ON_ModelComponent::Attributes::ParentIdAttribute;
}

bool ON_ModelComponent::ParentIdIsLocked() const
{
  const unsigned int bit = ON_ModelComponent::Attributes::ParentIdAttribute;
  return (0 != (bit & m_locked_status));
}

bool ON_ModelComponent::ParentIdIsSet() const
{
  const unsigned int bit = ON_ModelComponent::Attributes::ParentIdAttribute;
  return (0 != (bit & m_set_status));
}  


///////////////////////////////////////////////////////////////////////////////
//
// General attributes interface
//
//

unsigned int ON_ModelComponent::ClearModelComponentAttributes(
  unsigned int attributes_filter
  )
{
  const unsigned int locked_status = (unsigned int)m_locked_status;
  unsigned int rc = 0;
  unsigned int bit;

  // Cannot modify locked attributes.
  attributes_filter &= ~locked_status;

  const unsigned int set_status0 = m_set_status;

  bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute & attributes_filter;
  if ( 0 != bit )
  {
    m_model_serial_number = ON_ModelComponent::Unset.m_model_serial_number;
    m_reference_model_serial_number = ON_ModelComponent::Unset.m_reference_model_serial_number;
    m_linked_idef_serial_number = ON_ModelComponent::Unset.m_linked_idef_serial_number;
    m_set_status &= ~bit;
    rc |= bit;
  }

  bit = ON_ModelComponent::Attributes::IdAttribute & attributes_filter;
  if ( 0 != bit )
  {
    m_component_id = ON_ModelComponent::Unset.m_component_id;
    m_set_status ^= bit;
    rc |= bit;
  }

  bit = ON_ModelComponent::Attributes::ParentIdAttribute & attributes_filter;
  if ( 0 != bit )
  {
    m_component_parent_id = ON_ModelComponent::Unset.m_component_id;
    m_set_status &= ~bit;
    rc |= bit;
  }

  bit = ON_ModelComponent::Attributes::TypeAttribute & attributes_filter;
  if ( 0 != bit )
  {
    m_component_type = ON_ModelComponent::Unset.m_component_type;
    m_set_status &= ~bit;
    rc |= bit;
  }

  bit = ON_ModelComponent::Attributes::ComponentStatusAttribute & attributes_filter;
  if ( 0 != bit )
  {
    m_component_status = ON_ModelComponent::Unset.m_component_status;
    m_set_status &= ~bit;
    rc |= bit;
  }

  bit = ON_ModelComponent::Attributes::IndexAttribute & attributes_filter;
  if ( 0 != bit )
  {
    m_component_index = ON_ModelComponent::Unset.m_component_index;
    m_set_status &= ~bit;
    rc |= bit;
  }

  bit = ON_ModelComponent::Attributes::NameAttribute & attributes_filter;
  if ( 0 != bit )
  {
    if (0 == (m_set_status & ON_ModelComponent::Attributes::DeletedNameAttribute))
    {
      m_component_name = ON_wString::EmptyString;
      m_component_name_hash = ON_NameHash::EmptyNameHash;
    }
    m_set_status &= ~bit;
    rc |= bit;
  }

  bit = ON_ModelComponent::Attributes::DeletedNameAttribute & attributes_filter;
  if ( 0 != bit )
  {
    if (0 == (m_set_status & ON_ModelComponent::Attributes::NameAttribute))
    {
      m_component_name = ON_wString::EmptyString;
      m_component_name_hash = ON_NameHash::EmptyNameHash;
    }
    m_set_status &= ~bit;
    rc |= bit;
  }

  if ( set_status0 != m_set_status )
    IncrementContentVersionNumber();

  return rc;
}

bool ON_BinaryArchive::ReadModelComponentAttributes(
  ON_ModelComponent& model_component,
  unsigned int* attributes_filter
  )
{
  unsigned int read_bits = 0;
  if ( nullptr != attributes_filter )
    *attributes_filter = read_bits;

  if (m_SetModelComponentSerialNumbers)
  {
    model_component.SetModelSerialNumber(
      this->m_model_serial_number,
      this->m_reference_model_serial_number,
      this->m_instance_definition_model_serial_number
      );
  }

  int major_version = 0;
  int minor_version = 0;
  if (false == BeginRead3dmChunk(TCODE_MODEL_ATTRIBUTES_CHUNK,&major_version,&minor_version))
    return false;

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

    unsigned int bit;
    unsigned char read_status;

    bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute;
    read_status = 0;
    if (!ReadChar(&read_status))
      break;
    if (2 == read_status)
      model_component.ClearModelSerialNumber();
    else if (1 == read_status)
    {
      unsigned int model_serial_number = 0;
      unsigned int reference_model_serial_number = 0;
      unsigned int instance_definition_model_serial_number = 0;
      if ( !ReadInt(&model_serial_number) )
        break;
      if ( !ReadInt(&reference_model_serial_number) )
        break;
      if ( !ReadInt(&instance_definition_model_serial_number) )
        break;
      model_component.SetModelSerialNumber(
        model_serial_number,
        reference_model_serial_number,
        instance_definition_model_serial_number
        );
      read_bits |= bit;
    }

    bit = ON_ModelComponent::Attributes::IdAttribute;
    read_status = 0;
    if (!ReadChar(&read_status))
      break;
    if (2 == read_status)
    {
      model_component.ClearId();
    }
    else if (1 == read_status)
    {
      ON_UUID component_id = ON_nil_uuid;
      if ( !ReadUuid(component_id) )
        break;
      model_component.SetId(component_id);
      read_bits |= bit;
    }

    bit = ON_ModelComponent::Attributes::TypeAttribute;
    read_status = 0;
    if (!ReadChar(&read_status))
      break;
    if (2 == read_status)
      model_component.ClearComponentType();
    else if (1 == read_status)
    {
      unsigned int component_type_as_unsigned = 0;
      if ( !ReadInt(&component_type_as_unsigned) )
        break;
      model_component.SetComponentType(ON_ModelComponent::ComponentTypeFromUnsigned(component_type_as_unsigned));
      read_bits |= bit;
    }

    bit = ON_ModelComponent::Attributes::IndexAttribute;
    read_status = 0;
    if (!ReadChar(&read_status))
      break;
    if (2 == read_status)
      model_component.ClearIndex();
    else if (1 == read_status)
    {
      int component_index = 0;
      if ( !ReadInt(&component_index) )
        break;
      model_component.SetIndex(component_index);
      read_bits |= bit;
    }

    bit = ON_ModelComponent::Attributes::NameAttribute;
    read_status = 0;
    if (!ReadChar(&read_status))
      break;
    if (2 == read_status)
      model_component.ClearName();
    else if (1 == read_status)
    {
      ON_wString component_name;
      if ( !ReadString(component_name) )
        break;
      model_component.SetName(static_cast<const wchar_t*>(component_name));
      read_bits |= bit;
    }

    rc = true;
    break;
  }

  if ( nullptr != attributes_filter)
    *attributes_filter = read_bits;

  if (false == EndRead3dmChunk())
    rc = false;

  return rc;
}

class ON_ModelComponentOverrideStackElement
{
public:
  ON_ModelComponentOverrideStackElement(
    const ON_ModelComponent& original_model_component,
    const ON_ModelComponent& model_component_override
    )
    : m_original_model_component(original_model_component)
    , m_model_component_override(model_component_override)
  {}
  ~ON_ModelComponentOverrideStackElement() = default;

  const ON_ModelComponent& m_original_model_component;
  const ON_ModelComponent& m_model_component_override;
  ON_ModelComponentOverrideStackElement* m_next = nullptr;

private:
  ON_ModelComponentOverrideStackElement(const ON_ModelComponentOverrideStackElement&) = delete;
  ON_ModelComponentOverrideStackElement& operator=(const ON_ModelComponentOverrideStackElement&) = delete;
};

bool ON_BinaryArchive::WriteModelComponentAttributes(
  const class ON_ModelComponent& model_component,
  unsigned int attributes_filter
  )
{
  if (false == BeginWrite3dmChunk(TCODE_MODEL_ATTRIBUTES_CHUNK,1,0))
    return false;

  bool rc = false;
  for (;;)
  {
    unsigned int bit = ON_ModelComponent::Attributes::ModelSerialNumberAttribute & attributes_filter;
    unsigned char write_status = (0 != bit) ? 1 : 0;
    if ( 0 != write_status && false == model_component.ModelSerialNumberIsSet() )
      write_status = 2;
    if (!WriteChar(write_status))
      break;
    if (1 == write_status )
    {
      const unsigned int model_serial_number = model_component.ModelSerialNumber();
      const unsigned int reference_model_serial_number = model_component.ReferenceModelSerialNumber();
      const unsigned int instance_definition_model_serial_number = model_component.InstanceDefinitionModelSerialNumber();
      if ( !WriteInt(model_serial_number) )
        break;
      if ( !WriteInt(reference_model_serial_number) )
        break;
      if ( !WriteInt(instance_definition_model_serial_number) )
        break;
    }

    bit = ON_ModelComponent::Attributes::IdAttribute & attributes_filter;
    write_status = (0 != bit) ? 1 : 0;
    if ( 0 != write_status && false == model_component.IdIsSet() )
      write_status = 2;
    if (!WriteChar(write_status))
      break;
    if (1 == write_status )
    {
      ON_UUID component_id = model_component.Id();
      if ( !WriteUuid(component_id) )
        break;
    }

    bit = ON_ModelComponent::Attributes::TypeAttribute & attributes_filter;
    write_status = (0 != bit) ? 1 : 0;
    if ( 0 != write_status && false == model_component.ComponentTypeIsSet() )
      write_status = 2;
    if (!WriteChar(write_status))
      break;
    if (1 == write_status )
    {
      unsigned int component_type_as_unsigned = static_cast<unsigned int>(model_component.ComponentType());
      if ( !WriteInt(component_type_as_unsigned) )
        break;
    }

    bit = ON_ModelComponent::Attributes::IndexAttribute & attributes_filter;
    write_status = (0 != bit) ? 1 : 0;
    if ( 0 != write_status && false == model_component.IndexIsSet() )
      write_status = 2;
    if (!WriteChar(write_status))
      break;
    if (1 == write_status )
    {
      if ( !Write3dmReferencedComponentIndex( model_component ) )
        break;
    }

    bit = ON_ModelComponent::Attributes::NameAttribute & attributes_filter;
    write_status = (0 != bit) ? 1 : 0;
    if ( 0 != write_status && false == model_component.NameIsSet() )
      write_status = 2;
    if (!WriteChar(write_status))
      break;
    if (1 == write_status )
    {
      if (false == this->WriteModelComponentName(model_component) )
        break;
    }

    rc = true;
    break;
  }

  if (false == EndWrite3dmChunk())
    rc = false;

  return rc;
}

bool ON_BinaryArchive::Write3dmReferencedComponentIndex(
  const ON_ModelComponent& model_component
  )
{
  if (ReferencedComponentIndexMapping())
  {
    // During writing, the m_manifest member stores 
    // the model id and index as the "Component" values and
    // the archive id and index as the "Manifest" values.
    const ON_UUID component_id = model_component.Id();
    return
      (ON_nil_uuid == component_id)
      ? Write3dmReferencedComponentIndex(model_component.ComponentType(), model_component.Index())
      : Write3dmReferencedComponentIndex(model_component.ComponentType(), component_id);
  }

  // no component mapping.
  return WriteInt(model_component.Index());
}

bool ON_BinaryArchive::Read3dmReferencedComponentIndexArray(
  ON_ModelComponent::Type component_type,
  ON_SimpleArray<int>& component_index_array
)
{
  if (!ReadArray(component_index_array))
    return false;
  if (false == ReferencedComponentIndexMapping())
    return true; // true means continue reading

  int count = 0;
  for (int i = 0; i < component_index_array.Count(); i++)
  {
    int archive_index = component_index_array[i];
    if (ON_ModelComponent::Type::TextStyle == component_type
      && archive_index >= 0 
      && archive_index < m_text_style_to_dim_style_archive_index_map.Count()
      )
    {
      const ON_2dex ts_to_ds = m_text_style_to_dim_style_archive_index_map[archive_index];
      if (archive_index == ts_to_ds.i && ts_to_ds.j >= 0)
      {
        archive_index = ts_to_ds.j;
        component_type = ON_ModelComponent::Type::DimStyle;
      }
    }
    int component_index = archive_index;
    component_index_array[i] = 0;
    const ON_ManifestMapItem& map_item = ManifestMap().MapItemFromSourceIndex(component_type, archive_index);
    if (false == map_item.SourceAndDestinationAreSet() || ON_UNSET_INT_INDEX == map_item.DestinationIndex())
    {
      ON_ERROR("Unable to update component reference index.");
      continue;
    }
    component_index = map_item.DestinationIndex();
    component_index_array[count++] = component_index;
  }
  component_index_array.SetCount(count);

  return true; // true means continue reading
}

bool ON_BinaryArchive::Read3dmReferencedComponentIndex(
  ON_ModelComponent::Type component_type,
  int* component_index
  )
{
  if (nullptr != component_index)
    *component_index = ON_UNSET_INT_INDEX;
  int archive_component_index = ON_UNSET_INT_INDEX;
  if (!ReadInt(&archive_component_index))
    return false;
  
  if (archive_component_index < 0 || false == ReferencedComponentIndexMapping() )
  {
    // system component with a persistent negative index
    *component_index = archive_component_index;
    return true;
  }

  int updated_component_index = archive_component_index;

  if (ON_ModelComponent::Type::TextStyle == component_type)
  {
    component_type = ON_ModelComponent::Type::DimStyle;
    const int archive_text_style_index = archive_component_index;
    archive_component_index = ON_UNSET_INT_INDEX;
    if (archive_text_style_index >= 0
      && archive_text_style_index < m_text_style_to_dim_style_archive_index_map.Count()
      )
    {
      const ON_2dex ts_to_ds = m_text_style_to_dim_style_archive_index_map[archive_text_style_index];
      if (archive_text_style_index == ts_to_ds.i && ts_to_ds.j >= 0)
      {
        archive_component_index = ts_to_ds.j;
      }
    }
  }

  if (ON_ModelComponent::Type::DimStyle == component_type)
  {
    if (this->Archive3dmVersion() <= 50)
    {
      *component_index = archive_component_index;
      return true;
    }
    const int archive_dim_style_index = archive_component_index;
    if (archive_dim_style_index >= 0
      && archive_dim_style_index < m_archive_dim_style_table.Count()
      )
    {
      const ON_DimStyle* archive_dim_style = m_archive_dim_style_table[archive_dim_style_index];
      if (nullptr != archive_dim_style)
      {
        if (archive_dim_style->ParentIdIsNotNil())
        {
          // override dimstyle - not in manifests or dimstyle table.
          *component_index = archive_component_index;
          return true;
        }

        const ON_ComponentManifestItem& item = m_manifest.ItemFromId(ON_ModelComponent::Type::DimStyle,archive_dim_style->Id());
        if ( item.Id() == archive_dim_style->Id() )
        {
          archive_component_index = item.Index();
        }
      }
    }
  }

  const ON_ManifestMapItem& map_item = m_manifest_map.MapItemFromSourceIndex(component_type,archive_component_index);
  if (map_item.SourceAndDestinationAreSet() && ON_UNSET_INT_INDEX != map_item.DestinationIndex())
  {
    updated_component_index = map_item.DestinationIndex();
  }
  else
  {
    ON_ERROR("Unable to update component index.");

    // Get something that might work when possible
    switch (component_type)
    {
    case ON_ModelComponent::Type::Image:
      break;
    case ON_ModelComponent::Type::TextureMapping:
      break;
    case ON_ModelComponent::Type::RenderMaterial:
      updated_component_index = ON_Material::Default.Index();
      break;
    case ON_ModelComponent::Type::LinePattern:
      updated_component_index = ON_Linetype::Continuous.Index();
      break;
    case ON_ModelComponent::Type::Layer:
      break;
    case ON_ModelComponent::Type::Group:
      break;

    case ON_ModelComponent::Type::TextStyle:
    case ON_ModelComponent::Type::DimStyle:
      if (nullptr != m_archive_current_dim_style)
      {
        const ON_ManifestMapItem& archive_current_dim_style_map_item = m_manifest_map.MapItemFromSourceIndex(ON_ModelComponent::Type::DimStyle,m_archive_current_dim_style->Index());
        if (archive_current_dim_style_map_item.SourceAndDestinationAreSet()
          && ON_UNSET_INT_INDEX != archive_current_dim_style_map_item.DestinationIndex()
          )
        {
          updated_component_index = archive_current_dim_style_map_item.DestinationIndex();
        }
      }
      break;

    case ON_ModelComponent::Type::RenderLight:
      break;
    case ON_ModelComponent::Type::HatchPattern:
      break;
    case ON_ModelComponent::Type::InstanceDefinition:
      break;
    case ON_ModelComponent::Type::ModelGeometry:
      break;
    case ON_ModelComponent::Type::HistoryRecord:
      break;
    }
  }

  if (nullptr != component_index)
    *component_index = updated_component_index;
  return true; // true indicates the read worked, even if the component index cannot be updated.
}

bool ON_BinaryArchive::Write3dmReferencedComponentIndex(
  ON_ModelComponent::Type component_type,
  int model_index
  )
{
  // When writing 3dm archives, the model index needs to be converted
  // to an archive index.  This function is used when the model component
  // id is not known (for example, when writing ON_Layer material and
  // line pattern indexes.
  int archive_index = model_index;

  if (model_index >= 0)
  {
    if (ON_ModelComponent::Type::TextStyle == component_type)
    {
      // In version 6, August 2016, text style information was moved to ON_DimStyle.
      //
      // In order to write files that can be read by earlier versions of Rhino,
      // a text style table that is parallel to the dimstyle table is saved in the 3d archive.

      if (ON_3dmArchiveTableType::dimension_style_table == m_3dm_active_table)
      {
        // The text style index of a dimstyle is being written. This is needed
        // so V5 and V6 Rhino WIPs built before August 2016 will be able to read
        // the file.
        const int text_style_count = m_manifest.ActiveComponentCount(ON_ModelComponent::Type::TextStyle);
        const int dim_style_count = m_manifest.ActiveComponentCount(ON_ModelComponent::Type::DimStyle);
        if (dim_style_count > 0 && dim_style_count <= text_style_count)
          archive_index = dim_style_count - 1;
        else if (text_style_count > 0)
          archive_index = 0;
        else
          archive_index = -1;
        return WriteInt(archive_index);
      }

      if (ON_3dmArchiveTableType::text_style_table != m_3dm_active_table)
      {
        // We are finished writing the text style and dim style tables.
        // There is a parallel set of text styles and dimension styles.
        // To get the right index in the archive for older versions of Rhino, change the component type to dimstyle.
        component_type = ON_ModelComponent::Type::DimStyle;
      }
    }

    if ( ReferencedComponentIndexMapping() )
    {
      const ON_ManifestMapItem& map_item = ManifestMap().MapItemFromSourceIndex(component_type, model_index);
      if (
        component_type == map_item.ComponentType()
        && ON_UNSET_INT_INDEX != map_item.DestinationIndex()
        )
      {
        archive_index = map_item.DestinationIndex();
      }
      else
      {
        ON_ERROR("unable to convert model index to archive index.");
        archive_index = model_index;
      }
    }
  }
  return WriteInt(archive_index);
}

bool ON_BinaryArchive::Write3dmReferencedComponentIndex(
  ON_ModelComponent::Type component_type,
  ON_UUID model_id
  )
{
  // When writing 3dm archives, the model index needs to be converted
  // to an archive index.  This function is used when the model component
  // id is known.
  int archive_index = ON_UNSET_INT_INDEX;
  if (ON_nil_uuid == model_id)
  {
    ON_ERROR("model_id is nil.");
  }
  else
  {
    const ON_ManifestMapItem& map_item = ManifestMap().MapItemFromSourceId(model_id);
    if (component_type != map_item.ComponentType() 
      || false == map_item.SourceIsSet()
      )
    {
      ON_ERROR("model_id not in arcive manifest map.");
    }
    else if (false == ReferencedComponentIndexMapping())
    {
      // component reference mapping is disabled (rare)
      archive_index = map_item.SourceIndex();
    }
    else
    {
      // adjust component reference to use the index in the archive (common)
      if (ON_UNSET_INT_INDEX != map_item.DestinationIndex())
      {
        archive_index = map_item.DestinationIndex();
      }
      else
      {
        ON_ERROR("Unable to get archive component reference index.");
      }
    }
  }
  return WriteInt(archive_index);
}

bool ON_BinaryArchive::ReferencedComponentIndexMapping() const
{
  return m_bReferencedComponentIndexMapping;
}

void ON_BinaryArchive::SetReferencedComponentIndexMapping(
  bool bEnableReferencedComponentIndexMapping
  )
{
  const bool b = bEnableReferencedComponentIndexMapping ? true : false;
  if (b != m_bReferencedComponentIndexMapping)
    m_bReferencedComponentIndexMapping = b;
}

bool ON_BinaryArchive::Write3dmReferencedComponentId(
  ON_ModelComponent::Type component_type,
  ON_UUID model_component_id
)
{
  return WriteUuid(model_component_id);
}

bool ON_BinaryArchive::Write3dmReferencedComponentId(
  const ON_ModelComponent& model_component
)
{
  return WriteUuid(model_component.Id());
}

bool ON_BinaryArchive::Read3dmReferencedComponentId(
  ON_ModelComponent::Type component_type,
  ON_UUID* component_id
)
{
  ON_UUID id = ON_nil_uuid;
  const bool rc = ReadUuid(id);

  ON_UUID default_id = ON_nil_uuid;

  bool bConvertToModelId = true;
  switch (component_type)
  {
  case ON_ModelComponent::Type::Unset:
    bConvertToModelId = false;
    break;
  case ON_ModelComponent::Type::Image:
    break;
  case ON_ModelComponent::Type::TextureMapping:
    break;
  case ON_ModelComponent::Type::RenderMaterial:
    break;
  case ON_ModelComponent::Type::LinePattern:
    break;
  case ON_ModelComponent::Type::Layer:
    break;
  case ON_ModelComponent::Type::Group:
    break;
  case ON_ModelComponent::Type::TextStyle:
    break;
  case ON_ModelComponent::Type::DimStyle:
    default_id = this->ArchiveCurrentDimStyleId();
    break;
  case ON_ModelComponent::Type::RenderLight:
    break;
  case ON_ModelComponent::Type::HatchPattern:
    break;
  case ON_ModelComponent::Type::InstanceDefinition:
    break;
  case ON_ModelComponent::Type::ModelGeometry:
    break;
  case ON_ModelComponent::Type::HistoryRecord:
    break;
  case ON_ModelComponent::Type::Mixed:
    bConvertToModelId = false;
    break;
  default:
    bConvertToModelId = false;
    break;
  }

  if (false == rc || ON_nil_uuid == id )
  {
    id = default_id;
  }
  else if ( ON_nil_uuid != id && bConvertToModelId )
  {
    const ON_ComponentManifestItem& item = this->Manifest().ItemFromId(component_type, id);
    if (component_type == item.ComponentType() && item.Id() == id )
    {
      // expected ...
      const ON_ManifestMapItem& map_item = this->ManifestMap().MapItemFromSourceId(id);
      if (component_type == map_item.ComponentType()
        && map_item.SourceId() == id
        && ON_nil_uuid != map_item.DestinationId()
        )
      {
        // ... everything is as it should be. Assign id used in model.
        id = map_item.DestinationId();
      }
      // else 
      // {
      //   Either the id was unchanged or the reading code failed 
      //   to set as (source,destination) pair in the manifest map.
      //   It's not an error condition.
      // }
    }
    else
    {
      // complicated situations 
      switch (component_type)
      {
      case ON_ModelComponent::Type::Unset:
        break;
      case ON_ModelComponent::Type::Image:
        break;
      case ON_ModelComponent::Type::TextureMapping:
        break;
      case ON_ModelComponent::Type::RenderMaterial:
        break;
      case ON_ModelComponent::Type::LinePattern:
        break;
      case ON_ModelComponent::Type::Layer:
        break;
      case ON_ModelComponent::Type::Group:
        break;
      case ON_ModelComponent::Type::TextStyle:
        break;
      case ON_ModelComponent::Type::DimStyle:
        if (ON_ModelComponent::Type::DimStyle != item.ComponentType() 
          && ON_DimStyle::SystemDimstyleFromId(id).Id() != id
          )
        {
          const unsigned int fixed_version = ON_VersionNumberConstruct(6, 0, 2017, 1, 15, 0);
          const unsigned int archive_version = this->ArchiveOpenNURBSVersion();
          if (archive_version >= fixed_version)
          {
            // Should not happen
            ON_ERROR("dimstyle id not in archive.");
          }
          id = default_id;
        }
        break;
      case ON_ModelComponent::Type::RenderLight:
        break;
      case ON_ModelComponent::Type::HatchPattern:
        break;
      case ON_ModelComponent::Type::InstanceDefinition:
        break;
      case ON_ModelComponent::Type::ModelGeometry:
        break;
      case ON_ModelComponent::Type::HistoryRecord:
        break;
      case ON_ModelComponent::Type::Mixed:
        break;
      default:
        break;
      }
    }
  }

  if (nullptr != component_id)
    *component_id = id;

  return rc;
}

bool ON_BinaryArchive::ReferencedComponentIdMapping() const
{
  return m_bReferencedComponentIdMapping;
}

void ON_BinaryArchive::SetReferencedComponentIdMapping(
  bool bEnableReferenceComponentIdMapping
)
{
  m_bReferencedComponentIdMapping
    = bEnableReferenceComponentIdMapping
    ? true
    : false;
}

const ON_ComponentManifest& ON_BinaryArchive::Manifest() const
{
  return m_manifest;
}

const ON_ManifestMap& ON_BinaryArchive::ManifestMap() const
{
  return m_manifest_map;
}

bool ON_BinaryArchive::UpdateManifestMapItemDestination(
  const class ON_ManifestMapItem& map_item
  )
{
  if (ON::archive_mode::read3dm != this->Mode())
  {
    ON_ERROR("archive mode != ON::archive_mode::read3dm");
    return false;
  }
  if (map_item.SourceIsUnset())
  {
    ON_ERROR("map_item source information is not set.");
    return false;
  }

  const bool bIgnoreSourceIndex = (ON_ModelComponent::Type::Group == map_item.ComponentType());

  return m_manifest_map.UpdatetMapItemDestination(map_item, bIgnoreSourceIndex);
}


bool ON_BinaryArchive::Internal_Write3dmUpdateManifest(
  const ON_ModelComponent& model_component
  )
{
  bool rc = false;
  for (;;)
  {
    const ON_ComponentManifestItem& manifest_item = m_manifest.AddComponentToManifest(model_component, false, nullptr);
    if (manifest_item.IsUnset())
      break;
    ON_ManifestMapItem map_item;
    if (!map_item.SetSourceIdentification(&model_component))
      break;
    if (!map_item.SetDestinationIdentification(&manifest_item))
      break;

    rc = m_manifest_map.AddMapItem(map_item);
    break;
  }
  if (!rc)
  {
    ON_ERROR("Unable to update archive manifest and map.");
  }
  return rc;
}

bool ON_BinaryArchive::Internal_Write3dmLightOrGeometryUpdateManifest(
  ON_ModelComponent::Type component_type,
  ON_UUID component_id,
  int component_index,
  const ON_wString& component_name
  )
{
  bool rc = false;
  for (;;)
  {
    const ON__UINT64 no_serial_number_yet = 0;
    const ON_NameHash component_name_hash = ON_NameHash::Create(ON_nil_uuid, component_name);
    const ON_ComponentManifestItem& manifest_item = m_manifest.AddComponentToManifest(component_type, no_serial_number_yet, component_id, component_name_hash);
    if (manifest_item.IsUnset())
      break;

    if (ON_ModelComponent::IndexRequired(component_type))
    {
      if (component_index < 0)
      {
        ON_ERROR("component_index should have value >= 0");
        component_index = manifest_item.Index();
      }
    }
    else
    {
      if (ON_UNSET_INT_INDEX != component_index)
      {
        ON_ERROR("component_index should have value ON_UNSET_INT_INDEX");
        component_index = ON_UNSET_INT_INDEX;
      }
    }

    ON_ManifestMapItem map_item;
    if (!map_item.SetSourceIdentification(component_type, component_id, component_index))
      break;
    if (!map_item.SetDestinationIdentification(&manifest_item))
      break;

    rc = m_manifest_map.AddMapItem(map_item);
    break;
  }
  if (!rc)
  {
    ON_ERROR("Unable to update archive manifest and map.");
  }
  return rc;
}

bool ON_BinaryArchive::Internal_Read3dmUpdateManifest(
  ON_ModelComponent& model_component
  )
{
  const bool bResolveIdAndNameCollisions = true;
  bool rc = false;
  for (;;)
  {
    const ON_ComponentManifestItem& item_from_id
      = model_component.IdIsNotNil()
      ? m_manifest.ItemFromId(model_component.Id())
      : ON_ComponentManifestItem::UnsetItem;

    const ON_ComponentManifestItem& item_from_name
      = ( model_component.NameIsNotEmpty() && ON_ModelComponent::UniqueNameRequired(model_component.ComponentType()) )
      ? m_manifest.ItemFromName(&model_component)
      : ON_ComponentManifestItem::UnsetItem;

    if (item_from_id.IsValid())
    {
      // id conflict - this is bad
      ON_WARNING("id conflict - 3dm file is not valid.");
      if (item_from_id.ComponentType() == model_component.ComponentType())
      {
        // duplicate components - first one wins and no remapping the id
        model_component.ClearId();
        model_component.SetId(); // generates new id
      }
    }

    if (item_from_name.IsValid() && false == item_from_name.IsSystemComponent() )
    {
      // name conflict but not id conflict - still bad
      // first name wins - no remapping
      if (false == item_from_id.IsValid())
      {
        // one warning is enough
        ON_WARNING("name conflict - 3dm file is not valid.");
      }
      model_component.SetName(m_manifest.UnusedName(model_component));
    }

    const bool bIndexRequired = ON_ModelComponent::IndexRequired(model_component.ComponentType());
    const int archive_index = model_component.Index();

    ON_wString assigned_name;
    const ON_ComponentManifestItem& manifest_item = m_manifest.AddComponentToManifest(model_component, bResolveIdAndNameCollisions, &assigned_name);
    if (manifest_item.IsUnset())
      break;
    if (bResolveIdAndNameCollisions && manifest_item.IsValid())
    {
      if (manifest_item.Id() != model_component.Id())
      {
        ON_WARNING("new id assigned to model component.");
        model_component.SetId(manifest_item.Id());
      }
      if (false == ON_wString::EqualOrdinal(model_component.Name(),assigned_name,false))
      {
        ON_WARNING("new name assigned to model component.");
        model_component.SetName(assigned_name);
      }

      // In damaged files, the index values are a mess.
      // Fix them here.
      const int manifest_index = manifest_item.Index();
      const int assigned_index
        = bIndexRequired
        ? (manifest_index >= 0 ? manifest_index : archive_index)
        : ON_UNSET_INT_INDEX;

      if (assigned_index != archive_index)
      {
        if (bIndexRequired) // This is a good location for a debugger breakpoint when debugging file reading index issues.
          model_component.SetIndex(manifest_item.Index());
        else
          model_component.ClearIndex();
      }
    }

    ON_ComponentManifestItem source_item(manifest_item);
    if (ON_ModelComponent::Type::Group == model_component.ComponentType()
      && archive_index >= 0
      && manifest_item.Index() >= 0
      && archive_index != manifest_item.Index()
      )
    {
      // group index values are not always the zero based index of the
      // group element's location in the archive.
      source_item.SetIndex(archive_index);
    }

    ON_ManifestMapItem map_item;
    // Rhino will update destination identification if it changes when added to the model.
    if (!map_item.SetSourceIdentification(&source_item))
      break;
    if (!map_item.SetDestinationIdentification(&model_component))
      break;
    rc = m_manifest_map.AddMapItem(map_item);
    break;
  }
  if (!rc)
  {
    ON_ERROR("Unable to update archive manifest and map.");
  }
  return rc;
}

bool ON_BinaryArchive::Internal_Read3dmLightOrGeometryUpdateManifest(
  ON_ModelComponent::Type component_type,
  ON_UUID component_id,
  int component_index,
  const ON_wString& component_name
  )
{
  bool rc = false;
  for (;;)
  {
    const ON__UINT64 no_serial_number_yet = 0;
    const ON_NameHash component_name_hash = ON_NameHash::Create(ON_nil_uuid, component_name);
    const ON_ComponentManifestItem& manifest_item = m_manifest.AddComponentToManifest(component_type, no_serial_number_yet, component_id, component_name_hash);
    if (manifest_item.IsUnset())
      break;

    if (ON_ModelComponent::IndexRequired(component_type))
    {
      if (component_index < 0)
      {
        ON_ERROR("component_index should have value >= 0");
        component_index = manifest_item.Index();
      }
    }
    else
    {
      if (ON_UNSET_INT_INDEX != component_index)
      {
        ON_ERROR("component_index should have value ON_UNSET_INT_INDEX");
        component_index = ON_UNSET_INT_INDEX;
      }
    }

    ON_ManifestMapItem map_item;
    // Rhino will update destination if it changes id or index
    if (!map_item.SetDestinationIdentification(component_type, component_id, component_index))
      break;
    if (!map_item.SetSourceIdentification(&manifest_item))
      break;

    rc = m_manifest_map.AddMapItem(map_item);
    break;
  }
  if (!rc)
  {
    ON_ERROR("Unable to update archive manifest and map.");
  }
  return rc;

}


bool ON_BinaryArchive::AddManifestMapItem(
	const class ON_ManifestMapItem& map_item
  )
{
  if (false == map_item.SourceAndDestinationAreSet() )
  {
    ON_ERROR("map_item source and destination are not set.");
    return false;
  }
  const bool bIndexRequired = ON_ModelComponent::IndexRequired(map_item.ComponentType());
  if (bIndexRequired && map_item.SourceIndex() < 0)
  {
    ON_ERROR("map_item.m_source_component_index is not set.");
    return false;
  }
  switch (Mode())
  {
  case ON::archive_mode::read3dm:
    {
      const ON_ComponentManifestItem& archive_item_from_id = m_manifest.ItemFromId(map_item.SourceId());
      if (
        map_item.ComponentType() != archive_item_from_id.ComponentType()
        || map_item.SourceId() != archive_item_from_id.Id()
        || map_item.SourceIndex() != archive_item_from_id.Index()
        )
      {
        ON_ERROR("map_item.m_source_component_id is not in the archive manifest.");
        return false;
      }
      if (bIndexRequired)
      {
        const ON_ComponentManifestItem& archive_item_from_index = m_manifest.ItemFromIndex(map_item.ComponentType(), map_item.SourceIndex());
        if (
          map_item.ComponentType() != archive_item_from_index.ComponentType()
          || map_item.SourceId() != archive_item_from_index.Id()
          || map_item.SourceIndex() != archive_item_from_index.Index()
          )
        {
          ON_ERROR("map_item.m_source_component_index is not in the archive manifest.");
          return false;
        }
      }
    }
    break;
  case ON::archive_mode::write3dm:
    {
      const ON_ComponentManifestItem& archive_item_from_id = m_manifest.ItemFromId(map_item.DestinationId());
      if (
        map_item.ComponentType() != archive_item_from_id.ComponentType()
        || map_item.DestinationId() != archive_item_from_id.Id()
        || map_item.DestinationIndex() != archive_item_from_id.Index()
        )
      {
        ON_ERROR("map_item.m_destination_component_id is not in the archive manifest.");
        return false;
      }
      if (bIndexRequired)
      {
        const ON_ComponentManifestItem& archive_item_from_index = m_manifest.ItemFromIndex(map_item.ComponentType(), map_item.DestinationIndex());
        if (
          map_item.ComponentType() != archive_item_from_index.ComponentType()
          || map_item.DestinationId() != archive_item_from_index.Id()
          || map_item.DestinationIndex() != archive_item_from_index.Index()
          )
        {
          ON_ERROR("map_item.m_destination_component_index is not in the archive manifest.");
          return false;
        }
      }
    }
    break;
  }

  if (!m_manifest_map.AddMapItem(map_item))
  {
    ON_ERROR("m_manifest_map.AddMapItem(map_item) failed.");
  }

  return true;
}

//////////////////////////////////////////////////////////////////////////////////////////////
//
//
//

// Explicit implementation to insure m_sp is completely managed in the openurbs DLL.
ON_ModelComponentWeakReference::ON_ModelComponentWeakReference() ON_NOEXCEPT
{}

// Explicit implementation to insure m_sp is completely managed in the openurbs DLL.
ON_ModelComponentWeakReference::~ON_ModelComponentWeakReference()
{}

// Explicit implementation to insure m_sp is completely managed in the openurbs DLL.
ON_ModelComponentWeakReference::ON_ModelComponentWeakReference(const ON_ModelComponentWeakReference& src) ON_NOEXCEPT
  : m_wp(src.m_wp)
{}

// Explicit implementation to insure m_sp is completely managed in the openurbs DLL.
ON_ModelComponentWeakReference& ON_ModelComponentWeakReference::operator=(const ON_ModelComponentWeakReference& src)
{
  if ( this != &src)
    m_wp = src.m_wp;
  return *this;
}
  
#if defined(ON_HAS_RVALUEREF)
ON_ModelComponentWeakReference::ON_ModelComponentWeakReference( ON_ModelComponentWeakReference&& src ) ON_NOEXCEPT
  : m_wp(std::move(src.m_wp))
{}

ON_ModelComponentWeakReference& ON_ModelComponentWeakReference::operator=(ON_ModelComponentWeakReference&& src)
{
  if ( this != &src )
  {
    m_wp.reset();
    m_wp = std::move(src.m_wp);
  }
  return *this;
}

#endif

ON_ModelComponentWeakReference::ON_ModelComponentWeakReference(const ON_ModelComponentReference& src)  ON_NOEXCEPT
  : m_wp(src.m_sp)
{}

ON_ModelComponentWeakReference& ON_ModelComponentWeakReference::operator=(const ON_ModelComponentReference& src)
{
  m_wp = src.m_sp;
  return *this;
}


//////////////////////////////////////////////////////////////////////////////////////////////
//
//
//

// Explicit implementation to insure m_sp is completely managed in the openurbs DLL.
ON_ModelComponentReference::ON_ModelComponentReference() ON_NOEXCEPT
{}

// Explicit implementation to insure m_sp is completely managed in the openurbs DLL.
ON_ModelComponentReference::~ON_ModelComponentReference()
{}

// Explicit implementation to insure m_sp is completely managed in the openurbs DLL.
ON_ModelComponentReference::ON_ModelComponentReference(const ON_ModelComponentReference& src) ON_NOEXCEPT
  : m_sp(src.m_sp)
{}

// Explicit implementation to insure m_sp is completely managed in the openurbs DLL.
ON_ModelComponentReference& ON_ModelComponentReference::operator=(const ON_ModelComponentReference& src)
{
  if ( this != &src)
    m_sp = src.m_sp;
  return *this;
}
  
#if defined(ON_HAS_RVALUEREF)
ON_ModelComponentReference::ON_ModelComponentReference( ON_ModelComponentReference&& src ) ON_NOEXCEPT
  : m_sp(std::move(src.m_sp))
{}

ON_ModelComponentReference& ON_ModelComponentReference::operator=(ON_ModelComponentReference&& src)
{
  if ( this != &src )
  {
    m_sp.reset();
    m_sp = std::move(src.m_sp);
  }
  return *this;
}

#endif

ON_ModelComponentReference::ON_ModelComponentReference(const ON_ModelComponentWeakReference& src) ON_NOEXCEPT
  : m_sp(src.m_wp.lock())
{
  // NOTE WELL: 
  //   std::shared_ptr<T>(std::weak_ptr<T>) throws an exception when weak_ptr is empty.
  //   std::shared_ptr<T>(std::weak_ptr<T>.lock()) constructs and empty shared_ptr when weak_ptr is empty.
}


ON_ModelComponentReference& ON_ModelComponentReference::operator=(const ON_ModelComponentWeakReference& src)
{
  m_sp = src.m_wp.lock();
  return *this;
}


ON_ModelComponentReference::ON_ModelComponentReference(
  std::shared_ptr<ON_ModelComponent>& sp
  ) ON_NOEXCEPT
  : m_sp(sp)
{}

ON_ModelComponentReference& ON_ModelComponentReference::operator=(std::shared_ptr<ON_ModelComponent>& sp)
{
  m_sp = sp;
  return *this;
}

ON_ModelComponentReference ON_ModelComponentReference::CreateConstantSystemComponentReference(
  const class ON_ModelComponent& system_model_component
  ) ON_NOEXCEPT
{
  if ( system_model_component.IsSystemComponent())
    return CreateForExperts(const_cast<ON_ModelComponent*>(&system_model_component),false);

  ON_ERROR("Invalid system_model_component parameter.");
  return ON_ModelComponentReference::Empty;
}


ON_ModelComponentReference ON_ModelComponentReference::CreateForExperts(
  class ON_ModelComponent* model_component,
  bool bManagedComponentReference
  ) ON_NOEXCEPT
{
  if ( nullptr == model_component )
    return ON_ModelComponentReference::Empty;
  if (bManagedComponentReference && model_component->IsSystemComponent())
  {
    // If you get this error, you have const cast a system component
    // and there will be a hard to diagnose crash when the the
    // last shared pointer attempts to delete the constant system component.
    //
    // You should be using ON_ModelComponentReference::CreateConstantSystemComponentReference.
    ON_ERROR("The future attempt to delete model_component will crash the application.");
    return ON_ModelComponentReference::Empty;
  }
  ON_ModelComponentReference r;
  r.m_sp 
    = bManagedComponentReference
    ? ON_MANAGED_SHARED_PTR(ON_ModelComponent,model_component) // default calls delete model_component
    : ON_UNMANAGED_SHARED_PTR(ON_ModelComponent,model_component);
  return r;
}

const class ON_ModelComponent* ON_ModelComponentReference::ModelComponent() const ON_NOEXCEPT
{
  return m_sp.get();
}

class ON_ModelComponent* ON_ModelComponentReference::ExclusiveModelComponent() const ON_NOEXCEPT
{
  return 
    (1 == m_sp.use_count())
    ? m_sp.get()
    : nullptr;
}

ON__UINT64 ON_ModelComponentReference::ModelComponentRuntimeSerialNumber() const ON_NOEXCEPT
{
  const ON_ModelComponent* model_component = m_sp.get(); 
  return (nullptr != model_component) ? model_component->RuntimeSerialNumber() : 0;
}

const ON_UUID ON_ModelComponentReference::ModelComponentId() const ON_NOEXCEPT
{
  const ON_ModelComponent* model_component = m_sp.get(); 
  return (nullptr != model_component) ? model_component->Id() : ON_nil_uuid;
}

int ON_ModelComponentReference::ModelComponentIndex() const ON_NOEXCEPT
{
  const ON_ModelComponent* model_component = m_sp.get(); 
  return (nullptr != model_component) ? model_component->Index() : ON_UNSET_INT_INDEX;
}

const ON_NameHash ON_ModelComponentReference::ModelComponentNameHash() const ON_NOEXCEPT
{
  const ON_ModelComponent* model_component = m_sp.get(); 
  return (nullptr != model_component) ? model_component->NameHash() : ON_NameHash::UnsetNameHash;
}

bool ON_ModelComponentReference::IsEmpty() const ON_NOEXCEPT
{
  return (nullptr == m_sp.get());
}

unsigned int ON_ModelComponentReference::ReferenceCount() const ON_NOEXCEPT
{
  return (unsigned int)(m_sp.use_count());
}

ON_ModelComponentContentMark::ON_ModelComponentContentMark(
  const ON_ModelComponent& model_component
)
{
  Set(model_component);
}

ON_ModelComponentContentMark::ON_ModelComponentContentMark(
  const ON_ModelComponent* model_component
)
{
  Set(model_component);
}

void ON_ModelComponentContentMark::Set(
  const class ON_ModelComponent& model_component
)
{
  m_component_id = model_component.Id();
  m_component_serial_number = model_component.RuntimeSerialNumber();
  m_component_content_version_number = model_component.ContentVersionNumber();
  m_component_type = model_component.ComponentType();
}

void ON_ModelComponentContentMark::Set(
  const class ON_ModelComponent* model_component
)
{
  if (nullptr == model_component)
    *this = ON_ModelComponentContentMark::Unset;
  else
    Set(*model_component);
}

bool ON_ModelComponentContentMark::EqualContent(
  const class ON_ModelComponent& model_component
) const
{
  const ON_ModelComponentContentMark rhs(model_component);
  return EqualContent(*this, rhs);
}

bool ON_ModelComponentContentMark::EqualContent(
  const class ON_ModelComponent* model_component
) const
{
  const ON_ModelComponentContentMark rhs(model_component);
  return EqualContent(*this, rhs);
}
  
bool ON_ModelComponentContentMark::EqualContent(
  const ON_ModelComponentContentMark& lhs,
  const ON_ModelComponentContentMark& rhs
)
{
  return
    lhs.m_component_serial_number == rhs.m_component_serial_number
    && lhs.m_component_content_version_number == rhs.m_component_content_version_number
    && lhs.m_component_id == rhs.m_component_id
    && lhs.m_component_type == rhs.m_component_type
    ;
}

bool operator==(
  const ON_ModelComponentContentMark& lhs,
  const ON_ModelComponentContentMark& rhs
  )
{
  return ON_ModelComponentContentMark::EqualContent(lhs, rhs);
}

bool operator!=(
const ON_ModelComponentContentMark& lhs,
const ON_ModelComponentContentMark& rhs
)
{
  return ON_ModelComponentContentMark::EqualContent(lhs, rhs) ? false : true;
}

ON_UUID ON_ModelComponentContentMark::ComponentId() const
{
  return m_component_id;
}

ON__UINT64 ON_ModelComponentContentMark::ComponentRuntimeSerialNumber() const
{
  return m_component_serial_number;
}

ON__UINT64 ON_ModelComponentContentMark::ComponentContentVersionNumber() const
{
  return m_component_content_version_number;
}

ON_ModelComponent::Type ON_ModelComponentContentMark::ComponentType() const
{
  return m_component_type;
}

bool ON_ModelComponentContentMark::IsSet() const
{
  return (0 != m_component_serial_number);
}

bool ON_ModelComponentContentMark::IsUnset() const
{
  return (0 == m_component_serial_number);
}
