//______________________________________________________________________________
//
// Design and implementation by kms, 2006
// Fast string comparator class (NLS version)
//______________________________________________________________________________
//
#include "stdafx.h"
#include "compare.h"

int CCompare::Compare(LPCSTR key1, LPCSTR key2)
{
	int result;
	UINT n1, n2;

	while (
		// именно в этом порядке
		n2 = m_tbl[static_cast<UINT>(static_cast<BYTE>(*key2++))],
		n1 = m_tbl[static_cast<UINT>(static_cast<BYTE>(*key1++))],
		!( result = n1 - n2) && n2);

	return result;
};
//______________________________________________________________________________
//
// сравнение строк без учета пробелов слева и справа
//______________________________________________________________________________
//
int CCompare::CompareTrimSpc(LPCSTR key1, LPCSTR key2)
{
	int result;
	UINT n1, n2;
	const UINT spc = 0x01;

	// пропуск пробелов слева
	do n2 = m_tbl[static_cast<UINT>(static_cast<BYTE>(*key2++))]; while(n2 & spc);
	do n1 = m_tbl[static_cast<UINT>(static_cast<BYTE>(*key1++))]; while(n1 & spc);

	// сравнение
	while ((n1 == n2) && n2)
	{
		n2 = m_tbl[static_cast<UINT>(static_cast<BYTE>(*key2++))];
		n1 = m_tbl[static_cast<UINT>(static_cast<BYTE>(*key1++))];
	};

	// пропуск пробелов справа
	int n0 = n2;
	while(n2 & spc) n2 = m_tbl[static_cast<UINT>(static_cast<BYTE>(*key2++))];
	if (n2)
		n2 = n0;

	n0 = n1;
	while(n1 & spc) n1 = m_tbl[static_cast<UINT>(static_cast<BYTE>(*key1++))];
	if (n1)
		n1 = n0;
	
	// финал
	result = n1 - n2;
	return result;
};
//______________________________________________________________________________
//
// сравнение без учета пробелов вообще
//______________________________________________________________________________
//
int CCompare::CompareIgnoreSpc(LPCSTR key1, LPCSTR key2)
{
	int result;
	UINT n1, n2;
	const UINT spc = 0x01;

	for(;;) {
		// простое сравнение
		n2 = m_tbl[static_cast<UINT>(static_cast<BYTE>(*key2++))];
		n1 = m_tbl[static_cast<UINT>(static_cast<BYTE>(*key1++))];
		if ((n1 == n2) && n2)
			continue;

		// пропуск пробелов
		while(n2 & spc) n2 = m_tbl[static_cast<UINT>(static_cast<BYTE>(*key2++))];
		while(n1 & spc)	n1 = m_tbl[static_cast<UINT>(static_cast<BYTE>(*key1++))];

		// финал
		if ((result = n1 - n2) || !n2)
			break;
	};

	return result;
};
//______________________________________________________________________________
//
// инит карты весов
// параметры: mask:
//		0xFFFF0000 : unicode sort weights
//		0x0000FF00 : diacritic weigths
//		0x000000FF : case weights
//______________________________________________________________________________
//
BOOL CCompare::Init(LCID lcid, UINT mask)
{
	const int src_len = 256;
	const int dst_len = 256*4+8;

	char src[src_len]; //256 max
	char dst[dst_len]; //256*4+5 max
	char *pc;
	unsigned char c;
	int i;

	struct s_def
	{
		BYTE w_case;
		BYTE w_diacritic;
		BYTE w_u2;
		BYTE w_u1;
	};

	s_def defs[src_len];
	s_def *pdef;

	// source string prep
	for (i =1, pc = src; i < src_len; ++i)
		*pc++ = i;

	*pc =0;

	if (!LCMapString(lcid, LCMAP_SORTKEY|SORT_STRINGSORT, src, src_len, dst, dst_len))
		return FALSE;

	// defs
	for (i =0, pdef = defs; i < src_len; ++i, ++pdef)
	{
		pdef->w_case = 0;
		pdef->w_diacritic = 0;
		pdef->w_u2 = 0;
		pdef->w_u1 = 0;
	}

	// разбор весов
	i = 0; pc = dst;
	while ((c = *pc++) && (c != 0x01))
	{
		defs[i].w_u1 = c;
		defs[i++].w_u2 = *pc++;
	}
	
	if (c) // always valid
	{
		i = 0;
		while ((c = *pc++) && (c != 0x01))
		{
			defs[i++].w_diacritic = c;
		}
	}
	
	if (c) // always valid
	{
		i = 0;
		while ((c = *pc++) && (c != 0x01))
		{
			defs[i++].w_case = c;
		}
	}

	if (!GetStringTypeA(lcid, CT_CTYPE1, src, src_len, (WORD *)dst))
		return FALSE;

	// weight table contructing
	m_tbl[0] = 0;
	for (i = 0, pdef = defs; i < src_len - 1; ++pdef)
	{
		// 1. здесь теряется старший бит регистра
		// 2. порядок сортировки регистра: младшие w_case, старшие w_case (т.е. прописные сначала)
		UINT weight = (pdef->w_u1 << 24) + (pdef->w_u2 << 16) + (pdef->w_diacritic << 8) + (((pdef->w_case ^ 0xFF) << 1) & 0xFF);
//		UINT weight = (pdef->w_u1 << 24) + (pdef->w_u2 << 16) + (pdef->w_diacritic << 8) + (((pdef->w_case << 1) & 0xFF));
		weight &= mask;

		// здесь добавляется младший бит
		DWORD flags = ((WORD *)dst)[i];
		if (flags & (C1_SPACE | C1_CNTRL | C1_BLANK))
			weight |= 1;

		// ++i
		m_tbl[++i] = weight;
	}

	return TRUE;
};
