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

#include "opennurbs.h"

#if defined(ON_UUID_DECLARED_AS_CLASS)

// When ON_UUID is a typdef for Microsoft 's UUID,
// the Microsoft compiler handles == and !=.
// When a ON_UUID is not a typedef for a Microsoft UUID,
// it is declared as a class and operator== and operator!= 
// need to be explicitly defined.

bool ON_UUID::operator==(const ON_UUID& other) const
{
  return (0==memcmp(this,&other,sizeof(*this)));
}

bool ON_UUID::operator!=(const ON_UUID& other) const
{
  return (0!=memcmp(this,&other,sizeof(*this)));
}

#endif

// used to map the correspondence between uuid strings and
// ON_UUIDs as an array of 16 bytes.

// for little endian CPUs (Intel, etc)
static const int little_endian_rho[16] = {3,2,1,0, 5,4, 7,6, 8,9, 10,11,12,13,14,15};

// for big endian CPUs (Motorola, MIPS, Sparc, etc.)
static const int big_endian_rho[16] = {0,1,2,3, 4,5, 6,7, 8,9, 10,11,12,13,14,15};

 
bool ON_CreateUuid( ON_UUID& new_uuid )
{
  // See http://www.faqs.org/rfcs/rfc4122.html for uuid details.

#if 0
  {
    // Use this code when testing reqires "repeatable uniqueness".
    // NEVER check in this code.
    static ON_UUID blank = {
      0,                                        // unsigned long  Data1;
      0,                                        // unsigned short Data2;
      0x11dc,                                   // unsigned short Data3;
      {0x98,0x85,0x00,0x13,0x72,0xc3,0x38,0x78} // unsigned char  Data4[8];
    };
    if ( 0 == ++blank.Data1)
      blank.Data2++;
    new_uuid = blank;
  }
  return true;

#else

#if defined(ON_OS_WINDOWS)
  // Header: Declared in Rpcdce.h.
  // Library: Use Rpcrt4.lib  
  ::UuidCreate(&new_uuid);
  //::UuidCreateSequential(&new_uuid); // faster but computer MAC address
                                       // identifies the user and some
                                       // customers may object.
  return true;
#elif defined(ON_COMPILER_XCODE)
  // Header: #include <uuid/uuid.h>
  if ( ON::little_endian == ON::Endian() )
  {
    // Intel cpu mac
    // The uuid_generate() function returns a UUID in network or 
    // big-endian order.  The rest of OpenNURBS assumes that a UUID
    // is stored in native byte order, so we switch the byte order
    // of the UUID.
    uuid_t apple_osx_uuid;
    uuid_generate(apple_osx_uuid);
    unsigned char* dst = (unsigned char*)&new_uuid;
    const unsigned char* src = (const unsigned char*)&apple_osx_uuid;
    *dst++ = src[little_endian_rho[ 0]]; 
    *dst++ = src[little_endian_rho[ 1]]; 
    *dst++ = src[little_endian_rho[ 2]]; 
    *dst++ = src[little_endian_rho[ 3]]; 
    *dst++ = src[little_endian_rho[ 4]]; 
    *dst++ = src[little_endian_rho[ 5]]; 
    *dst++ = src[little_endian_rho[ 6]]; 
    *dst++ = src[little_endian_rho[ 7]]; 
    *dst++ = src[little_endian_rho[ 8]]; 
    *dst++ = src[little_endian_rho[ 9]]; 
    *dst++ = src[little_endian_rho[10]]; 
    *dst++ = src[little_endian_rho[11]]; 
    *dst++ = src[little_endian_rho[12]]; 
    *dst++ = src[little_endian_rho[13]]; 
    *dst++ = src[little_endian_rho[14]]; 
    *dst   = src[little_endian_rho[15]]; 
  }
  else
  {
    // Motorola cpu mac
    uuid_generate((unsigned char*)&new_uuid);
  }

  //#if defined (ON_DEBUG)
  //  // OS X generates version 4 UUIDs.  Check that this is still true after mangling.
  //  if ((new_uuid.Data3 & 0xF000) != 0x4000)
  //    ON_ERROR("ON_CreateUuid() failure 1");
  //  if (new_uuid.Data4[0] < 0x80 || new_uuid.Data4[0] >= 0xC0)
  //    ON_ERROR("ON_CreateUuid() failure 2");
  //#endif
  return true;
#else
  // You must supply a way to create unique ids or you 
  // will not be able to write 3dm files.
  memset(&new_uuid,0,sizeof(ON_UUID));
  return false;
#endif

#endif
}

 
ON_UUID ON_UuidFromString( const char* sUUID )
{
  // NOTE WELL: This code has to work on non-Windows OSs and on
  //            both big and little endian CPUs.  On Windows OSs
  //            is must return the same result as 
  //            Windows's UuidFromString().
  //

  // string has format like "85A08515-F383-11d3-BFE7-0010830122F0"
  // or like "{85A08515-F383-11d3-BFE7-0010830122F0}".  Brackets
  // and hyphens are optional and ignored.
  //
  // Windows users can use "guidgen" to create UUID strings.

  /*
#if defined(ON_DEBUG) && defined(ON_OS_WINDOWS)
  RPC_STATUS st;
  union 
  {
    ON_UUID uuid;
    unsigned char b[16];
  } u1;
  st = UuidFromString( (unsigned char*)sUUID, &u1.uuid );
#endif
*/

  static const int* rho = ( ON::big_endian == ON::Endian() ) 
                        ? big_endian_rho 
                        : little_endian_rho;

  union 
  {
    ON_UUID uuid;
    unsigned char b[16];
  } u;
  ON_BOOL32 bFailed;
  int bi, ci;
  unsigned char c;
  unsigned char byte_value[2];

  memset(&u,0,sizeof(u));
  //for ( bi = 0; bi < 16; bi++ ) 
  //  u.b[bi] = 0;

  bFailed = sUUID ? false : true;

  if ( !bFailed ) {
    while ( *sUUID && *sUUID <= ' ' ) // skip leading white space
      sUUID++;
    if ( *sUUID == '{' )
      sUUID++;
    for ( bi = 0; bi < 16; bi++ ) {
      ci = 0;
      byte_value[0] = 0;
      byte_value[1] = 0;
      while ( ci < 2 ) {
        c = *sUUID++;
        if ( !c ) {
          bFailed = true;
          break;
        }
        if ( c >= 'A' && c <= 'F' ) {
          byte_value[ci++] = (c-'A'+10);
        }
        else if ( c >= '0' && c <='9' ) {
          byte_value[ci++] = (c-'0');
        }
        else if ( c >= 'a' && c <= 'f' ) {
          byte_value[ci++] = (c-'a'+10);
        }
        else if ( c != '-' ) {
          bFailed = true;
          break;
        }
      }
      if ( bFailed )
        break;
      u.b[rho[bi]] = 16*byte_value[0] + byte_value[1];
    }
  }

  if ( bFailed ) {
    // 09 August 2006 John Morse
    // There are times when Rhino is looking for a plug-in but the SDK or command
    // allows the plug-in to be specified by name or UUID.  Rhino calls ON_UuidFromString()
    // to see if the string is a plug-in UUID so it knows if it should be comparing the string
    // or plug-in name when looking for a plug-in.  The ON_ERROR line makes the Rhino commands
    // generate an OpenNURBS message box (in DEBUG builds) when the command completes and is
    // a pain so I commented it out per Dale Lear.
    //ON_ERROR("ON_UuidFromString(): bad string passed in");
    u.uuid = ON_nil_uuid;
  }

/*
#if defined(ON_DEBUG) && defined(ON_OS_WINDOWS)
  if ( memcmp( &u.uuid, &u1.uuid, 16 ) ) {
    ON_ERROR("ON_UuidFromString() failed");
  }
  if ( UuidCompare( &u.uuid, &u1.uuid, &st ) ) {
    ON_ERROR("ON_UuidFromString() failed");
  }
  if ( ON_UuidCompare( &u.uuid, &u1.uuid ) ) {
    ON_ERROR("ON_UuidCompare() failed");
  }
#endif
*/
  return u.uuid;
}


ON_UUID ON_UuidFromString( const wchar_t* sUUID )
{
  wchar_t w;
  char s[64];
  int i;
  if( NULL == sUUID )
    return ON_nil_uuid;
  while ( *sUUID && *sUUID <= ' ' ) // skip leading white space
    sUUID++;
  if ( *sUUID == '{' )
    sUUID++;
  i = 0;
  while (i < 63 )
  {
    w = *sUUID++;
    if ( w >= 'A' && w <= 'F' )
      s[i++] = (char)w;
    else if ( w >= '0' && w <='9' )
      s[i++] = (char)w;
    else if ( w >= 'a' && w <= 'f' )
      s[i++] = (char)w;
    else if ( w != '-' ) 
      break;
  }
  s[i] = 0;

  return ON_UuidFromString(s);

}
 
ON_UuidIndex::ON_UuidIndex()
{
  memset(this,0,sizeof(*this));
}

int ON_UuidIndex::CompareIdAndIndex( const ON_UuidIndex* a, const ON_UuidIndex* b )
{
  int i;
  if ( !a )
    return (b ? -1 : 0 );
  if ( !b )
    return 1;

  // compare id first
  if ( 0 == (i = ON_UuidCompare(&a->m_id,&b->m_id)) )
    i = a->m_i - b->m_i;

  return i;
}

int ON_UuidIndex::CompareIndexAndId( const ON_UuidIndex* a, const ON_UuidIndex* b )
{
  int i;
  if ( !a )
    return (b ? -1 : 0 );
  if ( !b )
    return 1;

  // compare index first
  if ( 0 == (i = a->m_i - b->m_i) )
    i = ON_UuidCompare(&a->m_id,&b->m_id);

  return i;
}

int ON_UuidIndex::CompareId( const ON_UuidIndex* a, const ON_UuidIndex* b )
{
  if ( !a )
    return (b ? -1 : 0 );
  if ( !b )
    return 1;
  return ON_UuidCompare(&a->m_id,&b->m_id);
}

int ON_UuidIndex::CompareIndex( const ON_UuidIndex* a, const ON_UuidIndex* b )
{
  if ( !a )
    return (b ? -1 : 0 );
  if ( !b )
    return 1;
  return a->m_i - b->m_i;
}

// Test code for ON_UuidCompare
////{
////  RPC_STATUS rpc_status = 0;
////  ON_UUID a,b;
////  size_t sz = sizeof(a);
////  unsigned char* pa = (unsigned char*)&a;
////  unsigned char* pb = (unsigned char*)&b;
////  unsigned char u[10] = {0,1,3,63,64,65,127,128,129,255};
////  int x,y,z;
////  for ( int aa = 0; aa < 10; aa++ ) for ( int bb = 0; bb < 10; bb++ )
////  {
////    for ( size_t i = 0; i < sz; i++ )
////    {
////      memset(pa,0,sz);
////      pa[i] = u[aa];
////      for ( size_t j = 0; j < sz; j++ )
////      {
////        memset(pb,0,sz);
////        pb[j] = u[bb];
////        rpc_status = 0;
////        y = ON_UuidCompare(&a,&b);
////        z = ::UuidCompare(&a,&b,&rpc_status);
////        if ( y != z )
////        {
////          int mscomparediff = 99; 
////        }
////      }      
////    }
////  }
////}

int ON_UuidCompare( const ON_UUID* a, const ON_UUID* b )
{
  // NOTE WELL: This code has to work the same way
  //            on Windows and non-Windows OSs and on
  //            both big and little endian CPUs
  //            taking into account the way ON_UUIDs
  //            are read/written by ON_BinaryArchive.
  //
  //            On Windows, ::UuidCompare() must agree 
  //            with this function.

  if ( !a ) 
  {
    return b ? -1 : 0;
  }
  if ( !b )
    return 1;

  if ( a->Data1 < b->Data1 ) return -1;
  if ( a->Data1 > b->Data1 ) return  1;

  if ( a->Data2 < b->Data2 ) return -1;
  if ( a->Data2 > b->Data2 ) return  1;

  if ( a->Data3 < b->Data3 ) return -1;
  if ( a->Data3 > b->Data3 ) return  1;
  return memcmp(a->Data4,b->Data4,sizeof(a->Data4));
}
 
int ON_UuidCompare( const ON_UUID& a, const ON_UUID& b)
{
  return ON_UuidCompare(&a,&b);
}

bool ON_UuidIsNil( 
        const ON_UUID& uuid 
        )
{
  const ON__INT32* p = (const ON__INT32*)&uuid;
  return ( p[0] || p[1] || p[2] || p[3] ) ? false : true;
}


bool ON_UuidIsNotNil( 
        const ON_UUID& uuid 
        )
{
  const ON__INT32* p = (const ON__INT32*)&uuid;
  return ( p[0] || p[1] || p[2] || p[3] ) ? true : false;
}


char* ON_UuidToString( const ON_UUID& uuid, char* s)
{
  // s - [out]  The s[] char array must have length >= 37.  
  //            The returned char array will have a 36 
  //            character uuid in s[0..35] and a null in s[36].

  // NOTE WELL: 
  //   This code has to work on non-Windows OSs and on both big and
  //   little endian CPUs.  The result must satisfy
  //   uuid == ON_UuidFromString(ON_UuidToString(uuid,s))

  // 31 August 2005 Dale Lear
  //     Changed upper case to lower case so result is
  //     identical to the string returned by Windows' ::UuidToString().
  //static const char x[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
  static const char x[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
  static const int addhyphen[16] = {0,0,0,1, 0,1, 0,1, 0,1,  0, 0, 0, 0, 0, 0};
  const unsigned char* b = (const unsigned char*)&uuid;
  char* p;
  int i;
  
  static const int* rho = ( ON::big_endian == ON::Endian() ) 
                        ? big_endian_rho 
                        : little_endian_rho;

  // 5 December 2002 Dale Lear:
  //   There is either a bug in Purify (likely) or perhaps a bug in the 
  //   way Microsoft compiles  c>>4 when c is an unsigned char.  In any
  //   case, changing c to an unsigned int makes purify happy and should
  //   work just as well.
  //
  //unsigned char c;

  unsigned int c;

  if ( !s )
    return 0;
  p = s;
  for ( i = 0; i < 16; i++ ) {
    c = b[rho[i]];
    *p++ = x[c>>4];  // purify gripes here if c is an unsigned char - the code runs fine.
    *p++ = x[c&0x0F];
    if ( addhyphen[i] )
      *p++ = '-';
  }
  *p = 0;

#if defined(ON_DEBUG)
  {
    ON_UUID u = ON_UuidFromString(s);
    if ( ON_UuidCompare(&u,&uuid) ) {
      ON_ERROR("ON_UuidToString() bug"); // <- breakpoint here
    }
  }
#endif

  return s;
}

wchar_t* ON_UuidToString( const ON_UUID& uuid, wchar_t* s)
{
  // s - [out]  The s[] char array must have length >= 37.  
  //            The returned char array will have a 36 
  //            character uuid in s[0..35] and a null in s[36].

  // NOTE WELL: 
  //   This code has to work on non-Windows OSs and on both big and
  //   little endian CPUs.  The result must satisfy
  //   uuid == ON_UuidFromString(ON_UuidToString(uuid,s))
  char x[37];
  if ( s && ON_UuidToString(uuid,x) )
  {
    int i;
    for (i = 0; i < 37; i++ )
    {
      s[i] = (wchar_t)x[i];
    }
  }
  else
  {
    s = 0;
  }
  return s;
}

 
const char* ON_UuidToString( const ON_UUID& uuid, ON_String& s )
{
  char x[37];
  s = ON_UuidToString( uuid, x );
  return s.Array();
}

 
const wchar_t* ON_UuidToString( const ON_UUID& uuid, ON_wString& s )
{
  wchar_t x[37];
  s = ON_UuidToString( uuid, x );
  return s.Array();
}
