//
//  chnlib06.c
//  chn
//
//  Created by 西田　耀 on 13/06/30.
//  Copyright (c) 2013年 Hikaru Nishida. All rights reserved.
//

// UUIDを格納する構造体

//
//Include headers
//
#include <stdio.h>
#include "chnlib.h"

//
//Define values
//

//
//Declare internal functions
//
CHNLIB_UUID *CHNLIB_UUID_Internal_Allocate(void);
void CHNLIB_UUID_Internal_Destruct(void **structure);
uint CHNLIB_UUID_Internal_GetHash(const void *structure);

//
//Define types
//
struct CHNLIB_UniversallyUniqueIDentifier {
    //UUID based on RFC4122
    //各フィールドはビッグエンディアンとして扱われるべき。
    CHNLIB_StructureHeader header;
    uchar time_low[4];
    uchar time_mid[2];
    uchar time_hi_and_version[2];
    uchar clock_seq_hi_and_reserved[1];
    uchar clock_seq_low[1];
    uchar node[6];
};

//
//Functions
//

CHNLIB_UUID *CHNLIB_UUID_Initialise(void)
{
    CHNLIB_UUID *uuid;
    
    uuid = CHNLIB_UUID_Internal_Allocate();
    uuid->header.destructor = &CHNLIB_UUID_Internal_Destruct;
    uuid->header.getHash = &CHNLIB_UUID_Internal_GetHash;
    
    CHNLIB_UUID_SetNullUUID(uuid);
    
    return uuid;
}

void CHNLIB_UUID_Free(CHNLIB_UUID *uuid)
{
    //uuidを解放する。
    if(CHNLIB_StructureHeader_GetTypeID(uuid) != CHNLIB_STRUCT_ID_UUID){
        return;
    }
    
    CHNLIB_UUID_SetNullUUID(uuid);
    
    uuid->header.typeid = CHNLIB_STRUCT_ID_Null;
    uuid->header.signature = 0;
    CHNLIB_System_FreeMemory(uuid, CHNLIB_DEBUG_ARGUMENTS);
    
    return;
}

void CHNLIB_UUID_Print(const CHNLIB_UUID *uuid)
{
    if(CHNLIB_StructureHeader_GetTypeID(uuid) != CHNLIB_STRUCT_ID_UUID){
        return;
    }

    printf("%02x%02x%02x%02x-", uuid->time_low[0], uuid->time_low[1], uuid->time_low[2], uuid->time_low[3]);
    printf("%02x%02x-", uuid->time_mid[0], uuid->time_mid[1]);
    printf("%02x%02x-", uuid->time_hi_and_version[0], uuid->time_hi_and_version[1]);
    printf("%02x%02x-", uuid->clock_seq_hi_and_reserved[0], uuid->clock_seq_low[0]);
    printf("%02x%02x%02x%02x%02x%02x", uuid->node[0], uuid->node[1], uuid->node[2], uuid->node[3], uuid->node[4], uuid->node[5]);
}

int CHNLIB_UUID_IsEqualToUUID(const CHNLIB_UUID *uuid1, const CHNLIB_UUID *uuid2)
{
    int i;
    
    if(CHNLIB_StructureHeader_GetTypeID(uuid1) != CHNLIB_STRUCT_ID_UUID){
        return False;
    }
    if(CHNLIB_StructureHeader_GetTypeID(uuid2) != CHNLIB_STRUCT_ID_UUID){
        return False;
    }
    
    for(i = 0; i < 4; i++){
        if(uuid1->time_low[i] != uuid2->time_low[i]){
            return False;
        }
    }
    for(i = 0; i < 2; i++){
        if(uuid1->time_mid[i] != uuid2->time_mid[i]){
            return False;
        }
    }
    for(i = 0; i < 2; i++){
        if(uuid1->time_hi_and_version[i] != uuid2->time_hi_and_version[i]){
            return False;
        }
    }
    if(uuid1->clock_seq_hi_and_reserved[i] != uuid2->clock_seq_hi_and_reserved[i]){
        return False;
    }
    if(uuid1->clock_seq_low[i] != uuid2->clock_seq_low[i]){
        return False;
    }
    for(i = 0; i < 6; i++){
        if(uuid1->node[i] != uuid2->node[i]){
            return False;
        }
    }
    return True;
}

void CHNLIB_UUID_SetValueAsUUIDVersion4CompatibleWithRFC4122(CHNLIB_UUID *uuid, uint random1, uint random2, uint random3, uint random4)
{
    int i;
    
    if(CHNLIB_StructureHeader_GetTypeID(uuid) != CHNLIB_STRUCT_ID_UUID){
        return;
    }
    
    //random1
    for(i = 0; i < 4; i++){
        uuid->time_low[i] = ((random1 >> (i * 8)) & 0xff);
    }
    //random2
    for(i = 0; i < 2; i++){
        uuid->time_mid[i] = ((random2 >> (i * 8)) & 0xff);
    }
    for(i = 0; i < 2; i++){
        uuid->time_hi_and_version[i] = ((random2 >> ((i + 2) * 8)) & 0xff);
    }
    //random3
    uuid->clock_seq_hi_and_reserved[0] = ((random3 >> (1 * 8)) & 0xff);
    uuid->clock_seq_low[0] = ((random3 >> (2 * 8)) & 0xff);
    for(i = 0; i < 2; i++){
        uuid->node[i] = ((random3 >> ((i + 2) * 8)) & 0xff);
    }
    //random4
    for(i = 2; i < 6; i++){
        uuid->node[i] = ((random4 >> ((i - 2) * 8)) & 0xff);
    }
    //Fixed field
    uuid->clock_seq_hi_and_reserved[0] &= 0x3f;
    uuid->clock_seq_hi_and_reserved[0] |= 0x80;
    
    uuid->time_hi_and_version[0] &= 0x0f;
    uuid->time_hi_and_version[0] |= 0x40;
    
    return;
}

void CHNLIB_UUID_SetNullUUID(CHNLIB_UUID *uuid)
{
    int i;
    
    if(CHNLIB_StructureHeader_GetTypeID(uuid) != CHNLIB_STRUCT_ID_UUID){
        return;
    }

    for(i = 0; i < 4; i++){
        uuid->time_low[i] = 0x00;
    }
    for(i = 0; i < 2; i++){
        uuid->time_mid[i] = 0x00;
    }
    for(i = 0; i < 2; i++){
        uuid->time_hi_and_version[i] = 0x00;
    }
    uuid->clock_seq_hi_and_reserved[0] = 0x00;
    uuid->clock_seq_low[0] = 0x00;
    for(i = 0; i < 6; i++){
        uuid->node[i] = 0x00;
    }
    return;
}

CHNLIB_UUID *CHNLIB_UUID_Copy(const CHNLIB_UUID *uuidBase)
{
    CHNLIB_UUID *uuidCopy;
    
    uuidCopy = CHNLIB_UUID_Initialise();
    
    int i;
    
    if(CHNLIB_StructureHeader_GetTypeID(uuidBase) == CHNLIB_STRUCT_ID_UUID){
        for(i = 0; i < 4; i++){
            uuidCopy->time_low[i] = uuidBase->time_low[i];
        }
        for(i = 0; i < 2; i++){
            uuidCopy->time_mid[i] = uuidBase->time_mid[i];
        }
        for(i = 0; i < 2; i++){
            uuidCopy->time_hi_and_version[i] = uuidBase->time_hi_and_version[i];
        }
        uuidCopy->clock_seq_hi_and_reserved[0] = uuidBase->clock_seq_hi_and_reserved[0];
        uuidCopy->clock_seq_low[0] = uuidBase->clock_seq_low[0];
        for(i = 0; i < 6; i++){
            uuidCopy->node[i] = uuidBase->node[i];
        }
    }
    return uuidCopy;

}

//
//Internal functions
//
CHNLIB_UUID *CHNLIB_UUID_Internal_Allocate(void)
{
    CHNLIB_UUID *tag;
    
    tag = (CHNLIB_UUID *)CHNLIB_System_AllocateMemory_Strict(sizeof(CHNLIB_UUID), CHNLIB_DEBUG_ARGUMENTS);
    
    CHNLIB_StructureHeader_Initialize(&tag->header, CHNLIB_STRUCT_ID_UUID);
    
    return tag;
}

void CHNLIB_UUID_Internal_Destruct(void **structure)
{
    //デストラクタ（実際にRelease->freeされる時に呼ばれる）
    if(structure == NULL){
        return;
    }
    
#ifdef DEBUG_MEMORY_REFERENCE_COUNT
    CHNLIB_Debug("Release(with free)[%p].", CHNLIB_DEBUG_ARGUMENTS, *structure);
#endif
    
    CHNLIB_UUID_Free(*structure);
    
    *structure = NULL;
    
    return;
}

uint CHNLIB_UUID_Internal_GetHash(const void *structure)
{
    uint hash;
    CHNLIB_UUID *uuid;
    
    hash = 0;
    
    if(CHNLIB_StructureHeader_GetTypeID(structure) == CHNLIB_STRUCT_ID_UUID){
        uuid = (CHNLIB_UUID *)structure;
        hash = ((uuid->time_low[3] << 24) | (uuid->time_mid[1] << 16) | (uuid->clock_seq_low[0] << 8) | (uuid->node[5]));
    }
    
    return hash;
}
