/*-------------------------------------------------------------------------
 * C-Pluff, a plug-in framework for C
 * Copyright 2007 Johannes Lehtinen
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *-----------------------------------------------------------------------*/

/** @file
 * Internal utility functions
 */ 

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#include "../kazlib/list.h"
#include "cpluff.h"
#include "defines.h"
#include "util.h"


/* ------------------------------------------------------------------------
 * Function definitions
 * ----------------------------------------------------------------------*/

CP_HIDDEN int cpi_comp_ptr(const void *ptr1, const void *ptr2) {
	return !(ptr1 == ptr2);
}

CP_HIDDEN hash_val_t cpi_hashfunc_ptr(const void *ptr) {
	return (hash_val_t) ptr;
}

CP_HIDDEN int cpi_ptrset_add(list_t *set, void *ptr) {
	

	// Only add the pointer if it is not already included 
	if (cpi_ptrset_contains(set, ptr)) {
		return 1;
	} else {
		lnode_t *node;

		/* Add the pointer to the list */		
		node = lnode_create(ptr);
		if (node == NULL) {
			return 0;
		}
		list_append(set, node);
		return 1;
	}
	
}

CP_HIDDEN int cpi_ptrset_remove(list_t *set, const void *ptr) {
	lnode_t *node;
	
	// Find the pointer if it is in the set 
	node = list_find(set, ptr, cpi_comp_ptr);
	if (node != NULL) {
		list_delete(set, node);
		lnode_destroy(node);
		return 1;
	} else {
		return 0;
	}
}

CP_HIDDEN int cpi_ptrset_contains(list_t *set, const void *ptr) {
	return list_find(set, ptr, cpi_comp_ptr) != NULL;
}

CP_HIDDEN void cpi_process_free_ptr(list_t *list, lnode_t *node, void *dummy) {
	void *ptr = lnode_get(node);
	list_delete(list, node);
	lnode_destroy(node);
	free(ptr);
}

static const char *vercmp_nondigit_end(const char *v) {
	while (*v != '\0' && (*v < '0' || *v > '9')) {
		v++;
	}
	return v;
}

static const char *vercmp_digit_end(const char *v) {
	while (*v >= '0' && *v <= '9') {
		v++;
	}
	return v;
}

static int vercmp_char_value(char c) {
	if (c == '\0') {
		return 0;
	} else if (c >= 'A' && c <= 'Z') {
		return 1 + (c - 'A');
	} else if (c >= 'a' && c <= 'z') {
		return 1 + ('Z' - 'A' + 1) + (c - 'a');
	} else {
		int i = 1 + ('Z' - 'A' + 1) + ('z' - 'a' + 1) + ((int) c - CHAR_MIN);
		if (c > 'z') {
			i -= 'z' - 'a' + 1;
		}
		if (c > 'Z') {
			i -= 'Z' - 'A' + 1;
		}
		if (c > '\0') {
			i--;
		}
		return i;
	}
}

static int vercmp_num_value(const char *v, const char *vn) {
	
	// Skip leading zeros
	while (v < vn && *v == '0') {
		v++;
	}
	
	// Empty string equals to zero
	if (v == vn) {
		return 0;
	}
	
	// Otherwise return the integer value
	else {
		char str[16];
		strncpy(str, v, vn - v < 16 ? vn - v : 16);
		str[vn - v < 16 ? vn - v : 15] = '\0';
		return atoi(str);
	}
}

CP_HIDDEN int cpi_vercmp(const char *v1, const char *v2) {
	const char *v1n;
	const char *v2n;
	int diff;
	
	// Check for NULL versions
	if (v1 == NULL && v2 != NULL) {
		return -1;
	} else if (v1 == NULL && v2 == NULL) {
		return 0;
	} else if (v1 != NULL && v2 == NULL) {
		return 1;
	}
	assert(v1 != NULL && v2 != NULL);
	
	// Component comparison loop
	while (*v1 != '\0' || *v2 != '\0') {
		
		// Determine longest non-digit prefix
		v1n = vercmp_nondigit_end(v1);
		v2n = vercmp_nondigit_end(v2);
		
		// Compare the non-digit strings
		while (v1 < v1n || v2 < v2n) {
			char c1 = '\0';
			char c2 = '\0';
			
			if (v1 < v1n) {
				c1 = *v1++;
			}
			if (v2 < v2n) {
				c2 = *v2++;
			}
			diff = vercmp_char_value(c1) - vercmp_char_value(c2);
			if (diff != 0) {
				return diff;
			}
			assert(v1 <= v1n && v2 <= v2n);
		}
		assert(v1 == v1n && v2 == v2n);
		
		// Determine the longest digit prefix
		v1n = vercmp_digit_end(v1);
		v2n = vercmp_digit_end(v2);
		
		// Compare the digit strings
		{
			int i1 = vercmp_num_value(v1, v1n);
			int i2 = vercmp_num_value(v2, v2n);
			int diff = i1 - i2;
			if (diff != 0) {
				return diff;
			}
		}
		v1 = v1n;
		v2 = v2n;
		
	}
	return 0;
}
