/************************************************************************
 ************************************************************************
    FAUST compiler
	Copyright (C) 2003-2004 GRAME, Centre National de Creation Musicale
    ---------------------------------------------------------------------
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 ************************************************************************
 ************************************************************************/

#include    <stdlib.h>
#include    <stdio.h>
#include    <string.h>
#include    "symbol.hh"
#include    "compatibility.hh"
#include    <iostream>
#include    <cstring>
#include    <assert.h>

using namespace std;

/**
 * Hash table used to store the symbols
 */
 
Symbol*	Symbol::gSymbolTable[kHashTableSize];


/**
 * Search the hash table for the symbol of name \p str or returns a new one.
 * \param str the name of the symbol
 * \return a symbol of name str
 */
  
Symbol* Symbol::get(const string& str)
{
	char 	buf[1024];
	int		i;
	int		n = str.length();
	
	if (n>1023) n = 1023;
	for (i = 0; i < n; i++) { buf[i] = str[i]; }
	buf[i] = 0;
	
	return Symbol::get(buf);
}



/**
 * Search the hash table for the symbol of name \p str or returns a new one.
 * \param str the name of the symbol
 * \return a symbol of name str
 */ 

Symbol* Symbol::get(const char* str)
{
    unsigned int 			hsh  = calcHashKey(str);
    int 			bckt = hsh % kHashTableSize;
	Symbol*			item = gSymbolTable[bckt];

    while ( item && !item->equiv(hsh,str) ) item = item->fNext;
	Symbol* r = item ? item : gSymbolTable[bckt] = new Symbol(str, hsh, gSymbolTable[bckt]);
	return r;
}


/**
 * Static method that searches the symbol table for a string. 
 * \param str string to search 
 * \return true if the string is NOT in the table (it is a new string) 
 */

bool Symbol::isnew(const char* str)
{
    unsigned int 			hsh  = calcHashKey(str);
    int 			bckt = hsh % kHashTableSize;
	Symbol*			item = gSymbolTable[bckt];
	
    while ( item && !item->equiv(hsh,str) ) item = item->fNext;
	return item == 0;
}


/**
 * Creates a new symbol with a name obtained by concatenating the \p str prefix with a number in order to make it unique
 * \param str the prefix of the name
 * \return a symbol of name \p prefix++n 
 */

Symbol* Symbol::prefix (const char* str)
{
	char 	name[256];
    
    static map<const char*, unsigned int> gPrefixCounters;
	
	for (int n = 0; n<10000; n++) {
		snprintf(name, 256, "%s%d", str, gPrefixCounters[str]++);
		if (isnew(name)) return get(name);
	}
	assert(false);
	return get("UNIQUEOVERFLOW");
}	


/**
 * Check if the name of the symbol is equal to string \p str
 * This method is used by isnew() and make() when searching the hashtable 
 * for an existing symbol.
 *
 * \param hash the hash key of the string (used to speedup the comparison)
 * \param str the string to compare
 * \return \p true if the name of the symbol and \p str are the same
 */
 
bool Symbol::equiv (unsigned int hash, const char *str) const
{
	return (fHash == hash) && (strcmp(fName,str) == 0);
}



/**
 * Compute the 32-bits hash key of string \p str
 * \param str the string
 * \return a 32-bits hash key
 */

unsigned int Symbol::calcHashKey (const char* str)
{
    unsigned int h = 0;

    while (*str) h = (h << 1) ^ (h >> 20) ^ (*str++);
    return h;
}



/**
 * Constructs a symbol ready to be placed in the hash table. 
 * It makes a private copy of its name.
 * \param str the name of the symbol
 * \param hsh the hash key of the symbol
 * \param nxt a pointer to the next symbol in the hash table entry
 */

Symbol::Symbol(const char* str, unsigned int hsh, Symbol* nxt)
{
	int len = strlen(str);
	
    fName = new char [len+1];
    memcpy(fName, str, len+1);
    fHash = hsh;
    fNext = nxt;
	fData = 0;
}

Symbol::~Symbol ()
{
	delete [] fName;
}

ostream& Symbol::print (ostream& fout) const 					///< print a symbol on a stream
{
	return fout << fName;
}
