/* $NoKeywords: $ */
/*
//
// Copyright (c) 1993-2011 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"

#define ON_BOZO_VACCINE_11EE2C1FF90D4C6AA7CDEC8532E1E32D
#define ON_BOZO_VACCINE_F42D967121EB46929B9ABC3507FF28F5

ON_OBJECT_IMPLEMENT( ON_InstanceDefinition, ON_Geometry, "26F8BFF6-2618-417f-A158-153D64A94989" );

ON_InstanceDefinition::ON_InstanceDefinition()
{
  m_uuid = ON_nil_uuid;
  m_idef_update_type = ON_InstanceDefinition::static_def;
  m_idef_update_depth = 0;
  m_us.m_unit_system = ON::no_unit_system;
  m_us.m_custom_unit_scale = 0.0;
  m_us.m_custom_unit_name.Destroy();
  m_source_bRelativePath = false;
  m_reserved1 = 0;
  m_reserved2[0] = 0;
  m_reserved2[1] = 0;
  m_reserved2[2] = 0;
}

ON_InstanceDefinition::~ON_InstanceDefinition()
{
}


void ON_InstanceDefinition::Dump( ON_TextLog& text_log ) const
{
  const wchar_t* s;

  s = m_name;
  if ( 0 == s ) 
    s = L"";
  text_log.Print(L"Name: \"%s\"\n",s);


  text_log.Print("Type: ");
  switch(m_idef_update_type)
  {
  case ON_InstanceDefinition::static_def:
    text_log.Print("embedded.");
    break;
  case ON_InstanceDefinition::embedded_def:
    text_log.Print("embedded - definition from source archive.");
    break;
  case ON_InstanceDefinition::linked_and_embedded_def:
    text_log.Print("embedded and linked - definition from source archive.");
    break;
  case ON_InstanceDefinition::linked_def:
    text_log.Print("linked - definition from source archive.");
    break;
  default:
    text_log.Print("not valid");
    break;
  }
  text_log.Print("\n");


  text_log.Print(L"Id: "); text_log.Print(m_uuid); text_log.Print(L"\n");

  s = m_description;
  if ( 0 != s && 0 != s[0]) 
    text_log.Print(L"Description: \"%s\"\n",s);

  s = m_url;
  if ( 0 != s && 0 != s[0]) 
    text_log.Print(L"URL: \"%s\"\n",s);

  s = m_url_tag;
  if ( 0 != s && 0 != s[0]) 
    text_log.Print(L"URL tag: \"%s\"\n",s);

  m_us.Dump(text_log);

  s = SourceArchive();
  text_log.Print(L"Source archive: ");
  if ( 0 == s || 0 == s[0] )
  {
    text_log.Print(L"none.\n");
  }
  else
  {
    bool bRel = m_source_bRelativePath;
    text_log.Print(L"\"%s\" (%s)\n",s,bRel?L"relative":L"absolute");

    text_log.PushIndent();
    ON_wString str;
    bRel = false;
    if ( GetAlternateSourceArchivePath(str,bRel) )
    {
      s = str;
      if ( 0 == s || 0 == s[0] )
        s = L"";
      text_log.Print(L"Alternate archive: \"%s\" (%s)\n",s,bRel?L"relative":L"absolute");
    }

    text_log.Print(L"Update depth: %d\n",m_idef_update_type);

    text_log.Print(L"Archive ");
    m_source_archive_checksum.Dump(text_log);    
    text_log.PopIndent();
  }

  const int id_count = m_object_uuid.Count();
  text_log.Print(L"Contains: %d objects\n",id_count);

  if ( id_count > 0 )
  {
    text_log.PushIndent();

    text_log.Print(m_object_uuid[0]); 
    text_log.Print(L"\n");

    if ( id_count > 4 )
    {
      text_log.Print(L"...\n");
    }
    else
    {
      for ( int i = 1; i < id_count; i++ )
      {
        text_log.Print(m_object_uuid[i]);
        text_log.Print(L"\n");
      }
    }

    text_log.PopIndent();
  }

  m_bbox.Dump(text_log);
}

ON_BOOL32 ON_InstanceDefinition::IsValid( ON_TextLog* text_log ) const
{
  if ( 0 == ON_UuidCompare( m_uuid, ON_nil_uuid) )
  {
    if (text_log)
    {
      text_log->Print("ON_InstanceDefinition has nil uuid.\n");
    }
    return false;
  }
  if ( !m_bbox.IsValid() )
  {
    if (text_log)
    {
      text_log->Print("ON_InstanceDefinition has invalid bounding box.\n");
    }
    return false;
  }
  switch( m_idef_update_type)
  {
    case embedded_def:
    case linked_def:
    case linked_and_embedded_def:
      if( m_source_archive.IsEmpty())
      {
        if (text_log)
        {
          text_log->Print("ON_InstanceDefinition is linked or embedded but m_source_archive is empty.\n");
        }
        return false;
      }
      break;
    default:
      // do nothing on rest of cases
      break;
  }

  // TODO
  return true;
}


unsigned int ON_InstanceDefinition::SizeOf() const
{
  unsigned int sz = sizeof(*this) - sizeof(ON_Geometry);
  sz += ON_Geometry::SizeOf();
  sz += this->m_object_uuid.SizeOfArray();
  sz += this->m_name.SizeOf();
  sz += this->m_description.SizeOf();
  sz += this->m_url.SizeOf();
  sz += this->m_url_tag.SizeOf();
  sz += this->m_source_archive.SizeOf();
  return sz;
}


ON_BOOL32 ON_InstanceDefinition::Write(
       ON_BinaryArchive& binary_archive
     ) const
{
  bool rc = binary_archive.Write3dmChunkVersion(1,5);

  // version 1.0 fields
  if ( rc )
    rc = binary_archive.WriteUuid( m_uuid );
  if ( rc )
  {
    if (    binary_archive.Archive3dmVersion() >= 4
         && ON_InstanceDefinition::linked_def == m_idef_update_type )
    {
      // linked instance definition geometry is never in the file
      ON_SimpleArray<ON_UUID> empty_uuid_list;
      rc = binary_archive.WriteArray( empty_uuid_list );
    }
    else
    {
      rc = binary_archive.WriteArray( m_object_uuid );
    }
  }
  if ( rc )
    rc = binary_archive.WriteString( m_name );
  if ( rc )
    rc = binary_archive.WriteString( m_description );
  if ( rc )
    rc = binary_archive.WriteString( m_url );
  if ( rc )
    rc = binary_archive.WriteString( m_url_tag );
  if ( rc )
    rc = binary_archive.WriteBoundingBox( m_bbox );
  // m_idef_update_type was an unsigned int and got changed to an enum.  Read and write
  // as an unsigned int to support backwards compatibility
  if ( rc )
    rc = binary_archive.WriteInt( (unsigned int)m_idef_update_type );
  if ( rc )
    rc = binary_archive.WriteString( m_source_archive );
  
  // version 1.1 fields
  if (rc)
    rc = m_source_archive_checksum.Write( binary_archive );
  
  // version 1.2 fields
  if (rc)
    rc = binary_archive.WriteInt( m_us.m_unit_system );

  // version 1.3 fields - added 6 March 2006
  if (rc)
    rc = binary_archive.WriteDouble( m_us.m_custom_unit_scale );

  if ( rc )
    rc = binary_archive.WriteBool( m_source_bRelativePath );

  // version 1.4 fields
  if (rc)
    rc = m_us.Write(binary_archive);

  // version 1.5 fields
  if (rc)
    rc = binary_archive.WriteInt(m_idef_update_depth);

  return rc;
}


ON_InstanceDefinition::IDEF_UPDATE_TYPE ON_InstanceDefinition::IdefUpdateType(int i)
{
  IDEF_UPDATE_TYPE t;
  switch(i)
  {
  case ON_InstanceDefinition::static_def:
    t = ON_InstanceDefinition::static_def;
    break;
  case ON_InstanceDefinition::embedded_def:
    t = ON_InstanceDefinition::embedded_def;
    break;
  case ON_InstanceDefinition::linked_and_embedded_def:
    t = ON_InstanceDefinition::linked_and_embedded_def;
    break;
  case ON_InstanceDefinition::linked_def:
    t = ON_InstanceDefinition::linked_def;
    break;
  default:
    t = ON_InstanceDefinition::static_def;
    break;
  }
  return t;
}


ON_BOOL32 ON_InstanceDefinition::Read(
       ON_BinaryArchive& binary_archive
     )
{
  int major_version = 0;
  int minor_version = 0;

  m_us.m_custom_unit_scale = 0.0;
  m_us.m_custom_unit_name.Destroy();
  m_us.m_unit_system = ON::no_unit_system;
  m_source_bRelativePath = false;
  m_source_archive.Destroy();

  bool rc = binary_archive.Read3dmChunkVersion(&major_version,&minor_version);
  if ( rc )
  {
    if ( major_version != 1 )
      rc = false;
    // version 1.0 fields
    if ( rc )
      rc = binary_archive.ReadUuid( m_uuid );
    if ( rc )
      rc = binary_archive.ReadArray( m_object_uuid );
    if ( rc )
      rc = binary_archive.ReadString( m_name );
    if ( rc )
      rc = binary_archive.ReadString( m_description );
    if ( rc )
      rc = binary_archive.ReadString( m_url );
    if ( rc )
      rc = binary_archive.ReadString( m_url_tag );
    if ( rc )
      rc = binary_archive.ReadBoundingBox( m_bbox );
    // m_idef_update_type was an unsigned int and got changed to an enum.  Read and write
    // as an unsigned int to support backwards compatibility
    unsigned int source = m_idef_update_type;
    if ( rc )
      rc = binary_archive.ReadInt( &source );
    if( rc)
      m_idef_update_type = ON_InstanceDefinition::IdefUpdateType(source);
    if ( rc )
      rc = binary_archive.ReadString( m_source_archive );

    // version 1.1 fields
    if ( minor_version >= 1 )
    {
      if ( rc )
        rc = m_source_archive_checksum.Read( binary_archive );
    }

    // version 1.2 fields
    if ( minor_version >= 2 )
    {
      int us = ON::no_unit_system;
      if ( rc )
        rc = binary_archive.ReadInt( &us );
      m_us.m_unit_system = ON::UnitSystem(us);
      if ( ON::custom_unit_system != m_us.m_unit_system && ON::no_unit_system != m_us.m_unit_system )
      {
        m_us.m_custom_unit_scale = ON::UnitScale( m_us.m_unit_system, ON::meters );
      }
      else
      {
        m_us.m_custom_unit_scale = 0.0;
      }

      if ( minor_version >= 3 )
      {
        // version 1.3 fields - added 6 March 2006
        //int us = ON::no_unit_system;
        if ( rc )
          rc = binary_archive.ReadDouble( &m_us.m_custom_unit_scale );
        if ( rc )
          rc = binary_archive.ReadBool( &m_source_bRelativePath );
        if ( rc && minor_version >= 4 )
        {
          rc = m_us.Read(binary_archive);
          if (rc && minor_version >= 5 )
          {
            rc = binary_archive.ReadInt(&m_idef_update_depth);
          }
        }
      }
    }
  }
  return rc;
}

ON::object_type ON_InstanceDefinition::ObjectType() const
{
  return ON::instance_definition;
}


// virtual ON_Geometry overrides
int ON_InstanceDefinition::Dimension() const
{
  return 3;
}

ON_BOOL32 ON_InstanceDefinition::GetBBox(
       double* boxmin,
       double* boxmax,
       ON_BOOL32 bGrowBox
       ) const
{
  if ( boxmin )
  {
    boxmin[0] = m_bbox.m_min.x;
    boxmin[1] = m_bbox.m_min.y;
    boxmin[2] = m_bbox.m_min.z;
  }
  if ( boxmax )
  {
    boxmax[0] = m_bbox.m_max.x;
    boxmax[1] = m_bbox.m_max.y;
    boxmax[2] = m_bbox.m_max.z;
  }
  return m_bbox.IsValid();
}

ON_BOOL32 ON_InstanceDefinition::Transform( 
       const ON_Xform& xform
       )
{
  // instance DEFs cannot be transformed
  return false;
}

const wchar_t* ON_InstanceDefinition::Name() const
{
  return m_name;
}

void ON_InstanceDefinition::SetName( const wchar_t* name )
{
  m_name = name;
  m_name.TrimLeft();
  m_name.TrimRight();
}


const wchar_t* ON_InstanceDefinition::URL() const
{
  return m_url;
}

void ON_InstanceDefinition::SetURL( const wchar_t* url )
{
  m_url = url;
  m_url.TrimLeft();
  m_url.TrimRight();
}

const wchar_t* ON_InstanceDefinition::URL_Tag() const
{
  return m_url_tag;
}

void ON_InstanceDefinition::SetURL_Tag( const wchar_t* url_tag )
{
  m_url_tag = url_tag;
  m_url_tag.TrimLeft();
  m_url_tag.TrimRight();
}

ON_UUID ON_InstanceDefinition::Uuid() const
{
  return m_uuid;
}

void ON_InstanceDefinition::SetUuid( ON_UUID uuid )
{
  m_uuid = uuid;
}

const wchar_t* ON_InstanceDefinition::Description() const
{
  return m_description;
}

void ON_InstanceDefinition::SetDescription( const wchar_t* description )
{
  m_description = description;
  m_description.TrimLeft();
  m_description.TrimRight();
}

void ON_InstanceDefinition::SetBoundingBox( ON_BoundingBox bbox )
{
  m_bbox = bbox;
}

void ON_InstanceDefinition::SetSourceArchive( const wchar_t* source_archive, 
                                              ON_CheckSum checksum,
                                              ON_InstanceDefinition::IDEF_UPDATE_TYPE idef_update_type)
{
  ON_wString s = source_archive;
  s.TrimLeftAndRight();
  m_source_archive = s;
  m_source_archive_checksum = checksum;
  if ( m_source_archive.IsEmpty() )
    m_idef_update_type = ON_InstanceDefinition::static_def;
  else
    m_idef_update_type = ON_InstanceDefinition::IdefUpdateType(idef_update_type);
}

const wchar_t* ON_InstanceDefinition::SourceArchive() const
{
  return m_source_archive;
}

ON_CheckSum ON_InstanceDefinition::SourceArchiveCheckSum() const
{
  return m_source_archive_checksum;
}

const ON_UnitSystem& ON_InstanceDefinition::UnitSystem() const
{
  return m_us;
}

void ON_InstanceDefinition::SetUnitSystem( ON::unit_system us )
{
  // make sure we are not getting garbage cast as an ON::unit_system
  if ( us == ON::UnitSystem(us) )
  {
    m_us.m_unit_system = us;
    if ( ON::custom_unit_system != m_us.m_unit_system )
    {
      m_us.m_custom_unit_scale = ( ON::no_unit_system == m_us.m_unit_system )
                               ? 0.0
                               : ON::UnitScale(ON::meters,m_us.m_unit_system);
    }
  }
}

void ON_InstanceDefinition::SetUnitSystem( const ON_UnitSystem& us )
{
  // make sure we are not getting garbage cast as an ON::unit_system
  if ( us.IsValid() )
  {
    m_us = us;
    if ( ON::custom_unit_system != m_us.m_unit_system )
    {
      m_us.m_custom_unit_scale = ( ON::no_unit_system == m_us.m_unit_system )
                               ? 0.0
                               : ON::UnitScale(ON::meters,m_us.m_unit_system);
    }
  }
}

ON_OBJECT_IMPLEMENT( ON_InstanceRef, ON_Geometry, "F9CFB638-B9D4-4340-87E3-C56E7865D96A" );

const double ON_InstanceRef::m_singular_xform_tol = 1.0e-6;

ON_InstanceRef::ON_InstanceRef()
{
  m_instance_definition_uuid = ON_nil_uuid;
  m_xform.Identity();
}

ON_BOOL32 ON_InstanceRef::IsValid( ON_TextLog* text_log ) const
{
  if ( 0 == ON_UuidCompare( m_instance_definition_uuid, ON_nil_uuid) )
  {
    if ( text_log )
      text_log->Print("ON_InstanceRef has nil m_instance_definition_uuid.\n");
    return false;
  }

  ON_Xform tmp = m_xform.Inverse()*m_xform;
  if ( !tmp.IsIdentity( ON_InstanceRef::m_singular_xform_tol ) )
  {
    if ( text_log )
      text_log->Print("ON_InstanceRef has singular m_xform.\n");
    return false;
  }
  return true;
}

ON_BOOL32 ON_InstanceRef::Write(
       ON_BinaryArchive& binary_archive
     ) const
{
  bool rc = binary_archive.Write3dmChunkVersion(1,0);
  if ( rc )
    rc = binary_archive.WriteUuid( m_instance_definition_uuid );
  if ( rc )
    rc = binary_archive.WriteXform( m_xform );
  if ( rc )
    rc = binary_archive.WriteBoundingBox( m_bbox );
  return rc;
}

ON_BOOL32 ON_InstanceRef::Read(
       ON_BinaryArchive& binary_archive
     )
{
  int major_version = 0;
  int minor_version = 0;
  bool rc = binary_archive.Read3dmChunkVersion(&major_version,&minor_version);
  if ( rc )
  {
    if ( major_version != 1 )
      rc = false;
    if (rc )
      rc = binary_archive.ReadUuid( m_instance_definition_uuid );
    if ( rc )
      rc = binary_archive.ReadXform( m_xform );
    if ( rc )
      rc = binary_archive.ReadBoundingBox( m_bbox );
  }
  return rc;
}

ON::object_type ON_InstanceRef::ObjectType() const
{
  return ON::instance_reference;
}


// virtual ON_Geometry overrides
int ON_InstanceRef::Dimension() const
{
  return 3;
}

ON_BOOL32 ON_InstanceRef::GetBBox(
       double* boxmin,
       double* boxmax,
       ON_BOOL32 bGrowBox
       ) const
{
  if ( !boxmin || !boxmax )
  {
    bGrowBox = false;
  }
  else if ( bGrowBox )
  {
    bGrowBox = ON_BoundingBox(ON_3dPoint(boxmin),ON_3dPoint(boxmax)).IsValid();
  }

  if( m_bbox.IsValid() )
  {
    if( bGrowBox )
    {
      if( boxmin[0] > m_bbox.m_min.x ) boxmin[0] = m_bbox.m_min.x;
      if( boxmin[1] > m_bbox.m_min.y ) boxmin[1] = m_bbox.m_min.y;
      if( boxmin[2] > m_bbox.m_min.z ) boxmin[2] = m_bbox.m_min.z;

      if( boxmax[0] < m_bbox.m_max.x ) boxmax[0] = m_bbox.m_max.x;
      if( boxmax[1] < m_bbox.m_max.y ) boxmax[1] = m_bbox.m_max.y;
      if( boxmax[2] < m_bbox.m_max.z ) boxmax[2] = m_bbox.m_max.z;
    }
    else
    {
      if( boxmin )
      {
        boxmin[0] = m_bbox.m_min.x;
        boxmin[1] = m_bbox.m_min.y;
        boxmin[2] = m_bbox.m_min.z;
      }
      if( boxmax )
      {
        boxmax[0] = m_bbox.m_max.x;
        boxmax[1] = m_bbox.m_max.y;
        boxmax[2] = m_bbox.m_max.z;
      }
      bGrowBox = true;
    }
  }

  return bGrowBox;
}

ON_BOOL32 ON_InstanceRef::Transform( 
       const ON_Xform& xform
       )
{
  ON_Geometry::Transform(xform);
  m_xform = xform*m_xform;
  m_bbox.Transform(xform);
  return true;
}

bool ON_InstanceRef::IsDeformable() const
{
  // 25 Feb 2006 Dale Lear - this seems wrong to me.
  return true;
}

bool ON_InstanceRef::MakeDeformable()
{
  // 25 Feb 2006 Dale Lear - this seems wrong to me.
  return true;
}


class /*NEVER EXPORT THIS CLASS DEFINITION*/ ON__IDefLayerSettingsUserData : public ON_UserData
{
#if !defined(ON_BOZO_VACCINE_11EE2C1FF90D4C6AA7CDEC8532E1E32D)
#error Never copy this class definition or put this definition in a header file!
#endif
  ON_OBJECT_DECLARE(ON__IDefLayerSettingsUserData);

public:
  ON__IDefLayerSettingsUserData();
  ~ON__IDefLayerSettingsUserData();
  // default copy constructor and operator= work fine.

  ON__IDefLayerSettingsUserData(const ON__IDefLayerSettingsUserData& src);
  ON__IDefLayerSettingsUserData& operator=(const ON__IDefLayerSettingsUserData& src);


  static ON__IDefLayerSettingsUserData* FindOrCreate(const ON_InstanceDefinition& idef,bool bCreate);
  
private:
  void CreateHelper()
  {
    m_layers.Destroy();
    m_idef_layer_table_parent_layer = 0;
  }

  void CopyHelper(const ON__IDefLayerSettingsUserData& src)
  {
    m_layers.Reserve(src.m_layers.Count());
    for ( int i = 0; i < src.m_layers.Count(); i++ )
    {
      const ON_Layer* src_layer = src.m_layers[i];
      if ( 0 != src_layer )
      {
        m_layers.Append( new ON_Layer( *src_layer ) );
      }
    }
    
    if ( 0 != src.m_idef_layer_table_parent_layer )
    {
      m_idef_layer_table_parent_layer = new ON_Layer( *src.m_idef_layer_table_parent_layer );
    }

    m_runtime_layer_id_map = src.m_runtime_layer_id_map;
    m_runtime_layer_id_map.ImproveSearchSpeed();
  }

  void DestroyHelper()
  {
    for ( int i = 0; i < m_layers.Count(); i++ )
    {
      delete m_layers[i];
      m_layers[i] = 0;
    }
    m_layers.Destroy();
    if ( 0 != m_idef_layer_table_parent_layer )
    {
      delete m_idef_layer_table_parent_layer;
      m_idef_layer_table_parent_layer = 0;
    }
    m_runtime_layer_id_map.Empty();
  }

public:
  // virtual ON_Object override
  ON_BOOL32 IsValid( ON_TextLog* text_log = NULL ) const;
  // virtual ON_Object override
  unsigned int SizeOf() const;
  // virtual ON_Object override
  ON__UINT32 DataCRC(ON__UINT32 current_remainder) const;
  // virtual ON_Object override
  ON_BOOL32 Write(ON_BinaryArchive& binary_archive) const;
  // virtual ON_Object override
  ON_BOOL32 Read(ON_BinaryArchive& binary_archive);
  // virtual ON_UserData override
  ON_BOOL32 Archive() const;
  // virtual ON_UserData override
  ON_BOOL32 GetDescription( ON_wString& description );

public:
  // m_layers[] satisfies
  //  * has no null members
  //  * is always sorted by layer id
  //  * has no duplicate layer ids
  //  * has no nil layer ids
  //  * the m_layer_id and m_parent_layer_id
  //    values are from the linked file.
  ON_SimpleArray<ON_Layer*> m_layers; 

  // Settings for the layer that is the
  // parent of the layers in the linked
  // files layer table.  This layer is
  // not in the linked file and is not
  // saved in the layer table of file containing
  // the idef.  If null, it is created
  ON_Layer* m_idef_layer_table_parent_layer;

  // When a linked idef is inserted and a layer id 
  // collision occures, the runtime id of the layer
  // has to be changed. This list keeps track of the
  // changes so we can determine which runtime layer
  // correspondes to a layer in m_layers[].
  // The first index id in the pair is the runtime
  // id and the second id in the pair is the m_layer[]
  // id.
  ON_UuidPairList m_runtime_layer_id_map;
};

#undef ON_BOZO_VACCINE_11EE2C1FF90D4C6AA7CDEC8532E1E32D

ON_OBJECT_IMPLEMENT(ON__IDefLayerSettingsUserData,ON_UserData,"11EE2C1F-F90D-4C6A-A7CD-EC8532E1E32D");

ON__IDefLayerSettingsUserData* ON__IDefLayerSettingsUserData::FindOrCreate(const ON_InstanceDefinition& idef,bool bCreate)
{
  ON__IDefLayerSettingsUserData* ud = ON__IDefLayerSettingsUserData::Cast(idef.GetUserData(ON__IDefLayerSettingsUserData::m_ON__IDefLayerSettingsUserData_class_id.Uuid()));
  if ( !ud && bCreate )
  {
    ud = new ON__IDefLayerSettingsUserData();
    const_cast<ON_InstanceDefinition&>(idef).AttachUserData(ud);
  }
  return ud;
}

ON__IDefLayerSettingsUserData::ON__IDefLayerSettingsUserData()
{
  m_userdata_uuid = ON__IDefLayerSettingsUserData::m_ON__IDefLayerSettingsUserData_class_id.Uuid();
  m_application_uuid = ON_opennurbs_id;
  m_userdata_copycount = 1;
  CreateHelper();
}

ON__IDefLayerSettingsUserData::~ON__IDefLayerSettingsUserData()
{
  DestroyHelper();
}

ON__IDefLayerSettingsUserData::ON__IDefLayerSettingsUserData(const ON__IDefLayerSettingsUserData& src)
: ON_UserData(src)
{
  m_userdata_uuid = ON__IDefLayerSettingsUserData::m_ON__IDefLayerSettingsUserData_class_id.Uuid();
  m_application_uuid = ON_opennurbs_id;
  CreateHelper();
  CopyHelper(src);
}

ON__IDefLayerSettingsUserData& ON__IDefLayerSettingsUserData::operator=(const ON__IDefLayerSettingsUserData& src)
{
  if ( this != &src )
  {
    DestroyHelper();
    ON_UserData::operator=(src);
    CopyHelper(src);
  }
  return *this;
}

// virtual ON_Object override
ON_BOOL32 ON__IDefLayerSettingsUserData::IsValid( ON_TextLog* text_log ) const
{
  return true;
}

// virtual ON_Object override
unsigned int ON__IDefLayerSettingsUserData::SizeOf() const
{
  return (unsigned int)(sizeof(*this));
}

// virtual ON_Object override
ON__UINT32 ON__IDefLayerSettingsUserData::DataCRC(ON__UINT32 current_remainder) const
{
  ON__UINT32 crc = current_remainder;
  for ( int i = 0; i < m_layers.Count(); i++ )
    crc = m_layers.DataCRC(crc);
  return crc;
}

// virtual ON_Object override
ON_BOOL32 ON__IDefLayerSettingsUserData::Write(ON_BinaryArchive& binary_archive) const
{
  bool rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1);
  if ( !rc )
    return false;

  rc = false;
  for(;;)
  {
    if ( !binary_archive.WriteArray(m_layers.Count(),m_layers.Array()) )
      break;

    // added in version 1.1 chunks
    bool bHaveParentLayer = ( 0 != m_idef_layer_table_parent_layer );
    if ( !binary_archive.WriteBool(bHaveParentLayer) )
      break;

    if ( bHaveParentLayer )
    {
      if ( !binary_archive.WriteObject(m_idef_layer_table_parent_layer) )
        break;
    }

    rc = true;
    break;
  }

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

  return rc;
}

// virtual ON_Object override
ON_BOOL32 ON__IDefLayerSettingsUserData::Read(ON_BinaryArchive& binary_archive)
{
  DestroyHelper();

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

  rc = false;
  while ( 1 == major_version )
  {
    if ( !binary_archive.ReadArray(m_layers) )
      break;

    if ( minor_version <= 0 )
    {
      rc = true;
      break;
    }

    // added in version 1.1 chunks
    bool bHaveParentLayer = false;
    if ( !binary_archive.ReadBool(&bHaveParentLayer) )
      break;

    if ( bHaveParentLayer )
    {
      ON_Object* p = 0;
      if ( !binary_archive.ReadObject(&p) || 0 == p )
      {
        if (p)
        {
          delete p;
          break;
        }
      }

      m_idef_layer_table_parent_layer = ON_Layer::Cast(p);
      if ( 0 == m_idef_layer_table_parent_layer )
      {
        delete p;
        break;
      }
    }

    rc = true;
    break;
  }

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

  return rc;
}

// virtual ON_UserData override
ON_BOOL32 ON__IDefLayerSettingsUserData::Archive() const
{
  // don't save empty settings
  return m_layers.Count() > 0;
}

// virtual ON_UserData override
ON_BOOL32 ON__IDefLayerSettingsUserData::GetDescription( ON_wString& description )
{
  description = L"Linked Instance Definition Layer Settings";
  return true;
}



bool ON_InstanceDefinition::HasLinkedIdefLayerSettings() const
{
  const ON__IDefLayerSettingsUserData* ud = ON__IDefLayerSettingsUserData::FindOrCreate(*this,false);
  return ud ? (ud->m_layers.Count() > 0) : false;
}

///////*
//////Description:
//////  Get layer settings for the context where the idef
//////  is being used.
//////Parameters:
//////  layer_settings - [out]
//////    Each layer has the settings from the linked reference file
//////    and layer.GetSavedSettings() reports any changes for the
//////    corresponding layers in the current context.
//////*/
//////void GetLinkedIdefLayerSettings( class ON_ObjectArray<ON_Layer>& layer_settings ) const;
//////
///////*
//////Description:
//////  Get the most recently read layer settings from the reference file.
//////Parameters:
//////  layer_settings - [out]
//////    The pointers in this array point memory managed by this
//////    instance definition and may be deleted by other
//////    ON_InstanceDefinition layer settings tools. Use the
//////    returned information immediately or copy it to memory
//////    you are managing.
//////*/
//////void GetLinkedIdefLayerReferenceSettings( class ON_SimpleArray<const ON_Layer*>& layer_settings ) const;
//////
//////
//////void ON_InstanceDefinition::GetLinkedIdefLayerReferenceSettings( class ON_SimpleArray<const ON_Layer*>& layer_settings ) const
//////{
//////  const ON__IDefLayerSettingsUserData* ud = ON__IDefLayerSettingsUserData::LayerSettings(*this,false);
//////  const int count = 0 != ud ? ud->m_layers.Count() : 0;
//////  if ( count > 0 )
//////  {
//////    layer_settings.Reserve(count);
//////    for ( int i = 0; i < count; i++ )
//////    {
//////      if ( 0 != ud->m_layers[i] )
//////        layer_settings.Append(ud->m_layers[i]);
//////    }
//////  }
//////  else
//////  {
//////    layer_settings.SetCount(0);
//////  }
//////}
//////
//////void ON_InstanceDefinition::GetLinkedIdefLayerSettings( class ON_ObjectArray<ON_Layer>& layer_settings ) const
//////{
//////  layer_settings.SetCount(0);
//////
//////  // Get the layers with settings from the referenced file.
//////  ON_SimpleArray<const ON_Layer*> layers;
//////  GetLinkedIdefLayerReferenceSettings(layers);
//////
//////  // Update those settings with any modifications made in the idef's current context.
//////  layer_settings.Reserve(layers.Count());
//////  unsigned int settings;
//////  for ( int i = 0; i < layers.Count(); i++ )
//////  {
//////    if ( 0 != layers[i] )
//////    {
//////      ON_Layer& layer = layer_settings.AppendNew();
//////      layer = *layers[i];
//////      settings = 0;
//////      layer.GetSavedSettings(layer,settings);
//////    }
//////  }
//////}

static int compareLayerPtrId(const void* A, const void*B)
{
  if ( 0 == A )
  {
    return 0 == B ? 0 : -1;
  }
  if ( 0 == B )
  {
    return 1;
  }

  const ON_Layer* a = (0!=A) ? ( *((ON_Layer**)A) ) : 0;
  const ON_Layer* b = (0!=B) ? ( *((ON_Layer**)B) ) : 0;
  if ( 0 == a )
  {
    return (0 == b) ? 0 : -1;
  }
  if ( 0 == b )
  {
    return 1;
  }

  // NOTE WELL:
  //   Compare only m_layer_id.  Other values may differ and
  //   adding compares to them will break the code that uses
  //   this function.
  return ON_UuidCompare(a->m_layer_id,b->m_layer_id);
}

static int compareUuidIndexId(const void* a, const void* b)
{
  return ON_UuidIndex::CompareId((const ON_UuidIndex*)a,(const ON_UuidIndex*)b);
}

void ON_InstanceDefinition::UpdateLinkedIdefReferenceFileLayerRuntimeId( const ON_UuidPairList& id_map )
{
  ON__IDefLayerSettingsUserData* ud = ON__IDefLayerSettingsUserData::FindOrCreate(*this,false);
  if ( 0 == ud || ud->m_layers.Count() <= 0 )
    return;
  ud->m_runtime_layer_id_map = id_map;
  ud->m_runtime_layer_id_map.ImproveSearchSpeed();
}

void ON_InstanceDefinition::UpdateLinkedIdefParentLayerSettings( const ON_Layer* linked_idef_parent_layer )
{
  bool bCreate = ( 0 != linked_idef_parent_layer );
  ON__IDefLayerSettingsUserData* ud = ON__IDefLayerSettingsUserData::FindOrCreate(*this,bCreate);
  if ( 0 != ud && ud->m_idef_layer_table_parent_layer != linked_idef_parent_layer )
  {
    if ( ud->m_idef_layer_table_parent_layer )
    {
      delete ud->m_idef_layer_table_parent_layer;
      ud->m_idef_layer_table_parent_layer = 0;
    }
    if ( 0 != linked_idef_parent_layer )
    {
      ud->m_idef_layer_table_parent_layer = new ON_Layer( *linked_idef_parent_layer );
    }
  }
}

const ON_Layer* ON_InstanceDefinition::LinkedIdefParentLayerSettings() const
{
  ON__IDefLayerSettingsUserData* ud = ON__IDefLayerSettingsUserData::FindOrCreate(*this,false);
  return (0 != ud) ? ud->m_idef_layer_table_parent_layer : 0;
}

void ON_InstanceDefinition::UpdateLinkedIdefReferenceFileLayerSettings( unsigned int layer_count, ON_Layer** layer_settings )
{
  ON__IDefLayerSettingsUserData* ud;

  if ( layer_count <= 0 || 0 == layer_settings )
  {
    // delete linked idef layer settings
    ud = ON__IDefLayerSettingsUserData::FindOrCreate(*this,false);
    if ( 0 != ud )
      delete ud;
    return;
  }

  // Create an index_map[] into the layer_settings[] array that is sorted
  // by layer_settings[]->m_layer_id
  ON_Workspace ws;
  int* index_map = (int*)ws.GetMemory(layer_count*sizeof(index_map[0]));
  ON_Sort(ON::quick_sort,index_map,layer_settings,layer_count,sizeof(layer_settings[0]),compareLayerPtrId);

  // Use index_map[] to get a unique list of layers with valid ids
  ON_UuidIndex* iddex = (ON_UuidIndex*)ws.GetMemory(layer_count*sizeof(iddex[0]));
  unsigned int iddex_count = 0;
  unsigned int i;
  ON_Layer* layer;
  for ( i = 0; i < layer_count; i++ )
  {
    layer = layer_settings[index_map[i]];
    if ( 0 == layer )
      continue;
    layer->SaveSettings(0,false); // remove any saved settings on input layers
    if ( ON_UuidIsNil(layer->m_layer_id) )
      continue;
    if ( iddex_count > 0 && iddex[iddex_count-1].m_id == layer->m_layer_id )
      continue;
    iddex[iddex_count].m_i = index_map[i];
    iddex[iddex_count].m_id = layer->m_layer_id;
    iddex_count++;
  }

  if ( iddex_count <= 0 )
  {
    // delete settings
    UpdateLinkedIdefReferenceFileLayerSettings(0,0);
    return;
  }

  // Create or get user data where the saved layer settings
  // are stored.
  ud = ON__IDefLayerSettingsUserData::FindOrCreate(*this,true);
  if ( 0 == ud )
    return;
    
  // Go through the saved settings that were previously
  // on this idef apply those settings to the layer_settings[]
  // list. Then delete the information from ud->m_layers[].
  ON_UuidIndex idx;
  idx.m_i = 0;
  unsigned int settings;
  for ( i = 0; i < ud->m_layers.UnsignedCount(); i++ )
  {
    if ( 0 == ud->m_layers[i] )
      continue;
    layer = ud->m_layers[i];
    ud->m_layers[i] = 0;
    for(;;)
    {
      settings = layer->SavedSettings();
      if ( 0 == settings )
        break; // no settings were modified
      idx.m_id = layer->m_layer_id;
      const ON_UuidIndex* idx0 = (const ON_UuidIndex*)bsearch(&idx,iddex,iddex_count,sizeof(iddex[0]),compareUuidIndexId);
      if ( 0 == idx0)
        break; // this layer is not in the current layer_settings[] list
      layer_settings[idx0->m_i]->SaveSettings(settings,false); // saves the layer settings found in linked file
      layer_settings[idx0->m_i]->Set(settings,*layer);   // applies modifications found on idef
      break;
    }
    delete layer;
  }

  // Save a copy of this information on the user data
  // so it will persist in the file containing the idef.
  ud->m_layers.SetCount(0);
  ud->m_layers.Reserve(iddex_count);
  for ( i = 0; i < iddex_count; i++ )
  {
    layer = new ON_Layer( *layer_settings[iddex[i].m_i] );
    ud->m_layers.Append(layer);
  }
}



void ON_InstanceDefinition::UpdateLinkedIdefLayerSettings( unsigned int layer_count, const ON_Layer*const* layer_settings )
{
  if ( layer_count <= 0 || 0 == layer_settings )
  {
    // delete linked idef layer settings
    UpdateLinkedIdefReferenceFileLayerSettings(0,0);
    return;
  }

  // Get layer information (saved on this idef) from the linked file 
  ON__IDefLayerSettingsUserData* ud = ON__IDefLayerSettingsUserData::FindOrCreate(*this,false);
  if ( 0 == ud )
    return;
  if ( ud->m_layers.Count() <= 0 )
  {
    delete ud;
    return;
  }

  // Apply any saved settings
  ON_Layer** ud_layers = ud->m_layers.Array();
  size_t ud_layers_count = ud->m_layers.Count();
  ON_Layer layerId;
  const ON_Layer* layerPtrId = &layerId;
  for ( unsigned int i = 0; i < layer_count; i++ )
  {
    const ON_Layer* layer1 = layer_settings[i];
    if ( !ud->m_runtime_layer_id_map.FindId1(layer1->m_layer_id,&layerId.m_layer_id) )
      layerId.m_layer_id = layer1->m_layer_id;
    ON_Layer** pp = (ON_Layer**)bsearch(&layerPtrId,ud_layers,ud_layers_count,sizeof(ud_layers[0]),compareLayerPtrId);
    ON_Layer* layer0 = (0 != pp) ? *pp : 0;
    if ( 0 == layer0 )
      continue;
    unsigned int settings0 = layer0->SavedSettings();
    unsigned int settings1 = ON_Layer::Differences(*layer0,*layer1);
    // settings0 = settings changes
    // new_settings = settings changed since we opened the model
    unsigned int new_settings = (settings1 ^ (settings0 & settings1));
    if ( 0 != new_settings )
      layer0->SaveSettings( new_settings, true );
    layer0->Set((settings0|settings1),*layer1);
  }
}



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


class /*NEVER EXPORT THIS CLASS DEFINITION*/ ON__IDefAlternativePathUserData : public ON_UserData
{
#if !defined(ON_BOZO_VACCINE_F42D967121EB46929B9ABC3507FF28F5)
#error Never copy this class definition or put this definition in a header file!
#endif
  ON_OBJECT_DECLARE(ON__IDefAlternativePathUserData);

public:
  ON__IDefAlternativePathUserData();
  ~ON__IDefAlternativePathUserData();
  // default copy constructor and operator= work fine.

  ON__IDefAlternativePathUserData(const ON__IDefAlternativePathUserData& src);
  ON__IDefAlternativePathUserData& operator=(const ON__IDefAlternativePathUserData& src);


  static ON__IDefAlternativePathUserData* FindOrCreate(const ON_InstanceDefinition& idef,bool bCreate);
  
private:
  void CreateHelper()
  {
    m_alternate_path.Destroy();
    m_bRelativePath = false;
  }

  void CopyHelper(const ON__IDefAlternativePathUserData& src)
  {
    m_alternate_path = src.m_alternate_path;
    m_bRelativePath = src.m_bRelativePath;
  }

  void DestroyHelper()
  {
    m_alternate_path.Destroy();
    m_bRelativePath = false;
  }

public:
  // virtual ON_Object override
  ON_BOOL32 IsValid( ON_TextLog* text_log = NULL ) const;
  // virtual ON_Object override
  unsigned int SizeOf() const;
  // virtual ON_Object override
  ON__UINT32 DataCRC(ON__UINT32 current_remainder) const;
  // virtual ON_Object override
  ON_BOOL32 Write(ON_BinaryArchive& binary_archive) const;
  // virtual ON_Object override
  ON_BOOL32 Read(ON_BinaryArchive& binary_archive);
  // virtual ON_UserData override
  ON_BOOL32 Archive() const;
  // virtual ON_UserData override
  ON_BOOL32 GetDescription( ON_wString& description );

public:
  ON_wString m_alternate_path;
  bool m_bRelativePath;
};

#undef ON_BOZO_VACCINE_F42D967121EB46929B9ABC3507FF28F5

ON_OBJECT_IMPLEMENT(ON__IDefAlternativePathUserData,ON_UserData,"F42D9671-21EB-4692-9B9A-BC3507FF28F5");

ON__IDefAlternativePathUserData* ON__IDefAlternativePathUserData::FindOrCreate(const ON_InstanceDefinition& idef,bool bCreate)
{
  ON__IDefAlternativePathUserData* ud = ON__IDefAlternativePathUserData::Cast(idef.GetUserData(ON__IDefAlternativePathUserData::m_ON__IDefAlternativePathUserData_class_id.Uuid()));
  if ( !ud && bCreate )
  {
    ud = new ON__IDefAlternativePathUserData();
    const_cast<ON_InstanceDefinition&>(idef).AttachUserData(ud);
  }
  return ud;
}

ON__IDefAlternativePathUserData::ON__IDefAlternativePathUserData()
{
  m_userdata_uuid = ON__IDefAlternativePathUserData::m_ON__IDefAlternativePathUserData_class_id.Uuid();
  m_application_uuid = ON_opennurbs_id;
  m_userdata_copycount = 1;
  CreateHelper();
}

ON__IDefAlternativePathUserData::~ON__IDefAlternativePathUserData()
{
  DestroyHelper();
}

ON__IDefAlternativePathUserData::ON__IDefAlternativePathUserData(const ON__IDefAlternativePathUserData& src)
: ON_UserData(src)
{
  m_userdata_uuid = ON__IDefAlternativePathUserData::m_ON__IDefAlternativePathUserData_class_id.Uuid();
  m_application_uuid = ON_opennurbs_id;
  CreateHelper();
  CopyHelper(src);
}

ON__IDefAlternativePathUserData& ON__IDefAlternativePathUserData::operator=(const ON__IDefAlternativePathUserData& src)
{
  if ( this != &src )
  {
    DestroyHelper();
    ON_UserData::operator=(src);
    CopyHelper(src);
  }
  return *this;
}

// virtual ON_Object override
ON_BOOL32 ON__IDefAlternativePathUserData::IsValid( ON_TextLog* text_log ) const
{
  return !m_alternate_path.IsEmpty();
}

// virtual ON_Object override
unsigned int ON__IDefAlternativePathUserData::SizeOf() const
{
  return (unsigned int)(sizeof(*this) + m_alternate_path.SizeOf());
}

// virtual ON_Object override
ON__UINT32 ON__IDefAlternativePathUserData::DataCRC(ON__UINT32 current_remainder) const
{
  ON__UINT32 crc = ON_CRC32(current_remainder,sizeof(m_bRelativePath),&m_bRelativePath);
  crc = m_alternate_path.DataCRC(crc);
  return crc;
}

// virtual ON_Object override
ON_BOOL32 ON__IDefAlternativePathUserData::Write(ON_BinaryArchive& binary_archive) const
{
  bool rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
  if ( !rc )
    return false;

  rc = false;
  for(;;)
  {
    if ( !binary_archive.WriteString(m_alternate_path) )
      break;
    if ( !binary_archive.WriteBool(m_bRelativePath) )
      break;
    rc = true;
    break;
  }

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

  return rc;
}

// virtual ON_Object override
ON_BOOL32 ON__IDefAlternativePathUserData::Read(ON_BinaryArchive& binary_archive)
{
  DestroyHelper();

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

  rc = false;
  while ( 1 == major_version )
  {
    if ( !binary_archive.ReadString(m_alternate_path) )
      break;
    if ( !binary_archive.ReadBool(&m_bRelativePath) )
      break;
    rc = true;
    break;
  }

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

  return rc;
}

// virtual ON_UserData override
ON_BOOL32 ON__IDefAlternativePathUserData::Archive() const
{
  // don't save empty settings
  return !m_alternate_path.IsEmpty();
}

// virtual ON_UserData override
ON_BOOL32 ON__IDefAlternativePathUserData::GetDescription( ON_wString& description )
{
  description = L"Linked Instance Definition Alternate Path";
  return true;
}


void ON_InstanceDefinition::SetAlternateSourceArchivePath( 
      const wchar_t* alternate_source_archive_path,
      bool bRelativePath
      )
{
  if ( 0 != alternate_source_archive_path && 0 == alternate_source_archive_path[0] )
    alternate_source_archive_path = 0;
  ON__IDefAlternativePathUserData* ud = ON__IDefAlternativePathUserData::FindOrCreate(*this,0!=alternate_source_archive_path);
  if ( 0 != ud )
  {
    if ( 0 == alternate_source_archive_path )
      delete ud;
    else
    {
      ud->m_alternate_path = alternate_source_archive_path;
      ud->m_bRelativePath = bRelativePath;
    }
  }
}

bool ON_InstanceDefinition::GetAlternateSourceArchivePath( 
      ON_wString& alternate_source_archive_path,
      bool& bRelativePath
      ) const
{
  const ON__IDefAlternativePathUserData* ud = ON__IDefAlternativePathUserData::FindOrCreate(*this,false);
  const wchar_t* s = (0 != ud) ? ((const wchar_t*)ud->m_alternate_path) : 0;
  if ( 0 != s && 0 != s[0] )
  {
    alternate_source_archive_path = s;
    bRelativePath = ud->m_bRelativePath;
  }
  else
  {
    alternate_source_archive_path.Destroy();
    bRelativePath = false;
  }
  return !alternate_source_archive_path.IsEmpty();
}
