#ifndef __SYMBOL_H__
#define __SYMBOL_H__

#include "Common.h"

#define AScript_Symbol(name) (SymbolPool::GetInstance()->_pSymbol_##name)

#define AScript_DeclareSymbol(name) \
const Symbol *_pSymbol_##name

#define AScript_RealizeSymbol(name) \
_pSymbol_##name = Symbol::Add(#name);

#define AScript_RealizeSymbolEx(name, str) \
_pSymbol_##name = Symbol::Add(str);

#define AScript_PrivSymbol(name) (_pPrivSymbol_##name)

#define AScript_DeclarePrivSymbol(name) \
static const Symbol *_pPrivSymbol_##name = NULL;

#define AScript_RealizePrivSymbol(name) \
_pPrivSymbol_##name = Symbol::Add(#name);

#define AScript_RealizePrivSymbolEx(name, str) \
_pPrivSymbol_##name = Symbol::Add(str);

namespace AScript {

//-----------------------------------------------------------------------------
// Symbol
//-----------------------------------------------------------------------------
class DLLDECLARE Symbol {
public:
	typedef unsigned int UniqNumber;
	struct KeyCompare_UniqNumber {
		inline bool operator()(const Symbol *pSymbol1, const Symbol *pSymbol2) const {
			return pSymbol1->GetUniqNum() < pSymbol2->GetUniqNum();
		}
	};
	struct KeyCompare_Name {
		inline bool operator()(const Symbol *pSymbol1, const Symbol *pSymbol2) const {
			return ::strcmp(pSymbol1->GetName(), pSymbol2->GetName()) < 0;
		}
	};
private:
	UniqNumber _uniqNum;
	char *_name;
public:
	Symbol(UniqNumber uniqNum, const char *name);
	~Symbol();
	bool IsFlowControlSymbol() const;
	inline static void Delete(Symbol *pSymbol) { delete pSymbol; }
	inline UniqNumber GetUniqNum() const { return _uniqNum; }
	inline const char *GetName() const { return _name; }
	inline bool IsIdentical(const Symbol *pSymbol) const {
		return GetUniqNum() == pSymbol->GetUniqNum();
	}
	inline bool IsNone() const { return _name[0] == '\0'; }
	inline bool IsPrivateName() const { return _name[0] == '_' && _name[1] == '_'; }
	static const Symbol *Add(const char *name);
};

//-----------------------------------------------------------------------------
// SymbolList
//-----------------------------------------------------------------------------
class DLLDECLARE SymbolList : public std::vector<const Symbol *> {
public:
	String Join(const char *sep) const;
	String Join(const char sepChar) const;
};

//-----------------------------------------------------------------------------
// SymbolSet
//-----------------------------------------------------------------------------
class DLLDECLARE SymbolSet : public std::set<const Symbol *, Symbol::KeyCompare_UniqNumber> {
public:
	static const SymbolSet Null;
public:
	inline SymbolSet() {}
	SymbolSet(const SymbolSet &symbolSet);
	~SymbolSet();
	void operator=(const SymbolSet &symbolSet);
	inline bool IsSet(const Symbol *pSymbol) const {
		return find(pSymbol) != const_cast<SymbolSet *>(this)->end();
	}
	inline void Insert(const Symbol *pSymbol) {
		insert(pSymbol);
	}
};

//-----------------------------------------------------------------------------
// SymbolPool
//-----------------------------------------------------------------------------
class DLLDECLARE SymbolPool {
public:
	AScript_DeclareSymbol(Str_Empty);
	AScript_DeclareSymbol(Char_Plus);
	AScript_DeclareSymbol(Char_Multiply);
	AScript_DeclareSymbol(Char_Question);
	AScript_DeclareSymbol(Char_Modulo);
	AScript_DeclareSymbol(Char_And);
	AScript_DeclareSymbol(boolean);
	AScript_DeclareSymbol(number);
	AScript_DeclareSymbol(string);
	AScript_DeclareSymbol(ps1);
	AScript_DeclareSymbol(ps2);
	AScript_DeclareSymbol(stdin);
	AScript_DeclareSymbol(stdout);
	AScript_DeclareSymbol(stderr);
	AScript_DeclareSymbol(e);
	AScript_DeclareSymbol(pi);
	AScript_DeclareSymbol(nil);
	AScript_DeclareSymbol(zero);
	AScript_DeclareSymbol(raise);
	AScript_DeclareSymbol(true_);
	AScript_DeclareSymbol(false_);
	AScript_DeclareSymbol(j);
	AScript_DeclareSymbol(if_);
	AScript_DeclareSymbol(elsif);
	AScript_DeclareSymbol(else_);
	AScript_DeclareSymbol(repeat);
	AScript_DeclareSymbol(while_);
	AScript_DeclareSymbol(for_);
	AScript_DeclareSymbol(cross);
	AScript_DeclareSymbol(break_);
	AScript_DeclareSymbol(continue_);
	AScript_DeclareSymbol(except_);
	AScript_DeclareSymbol(text);
	AScript_DeclareSymbol(message);
	AScript_DeclareSymbol(value);
	AScript_DeclareSymbol(__init__);
	AScript_DeclareSymbol(__del__);
	AScript_DeclareSymbol(__str__);
	AScript_DeclareSymbol(__setprop__);
	AScript_DeclareSymbol(__getprop__);
	AScript_DeclareSymbol(__setitem__);
	AScript_DeclareSymbol(__getitem__);
	AScript_DeclareSymbol(__name__);
	AScript_DeclareSymbol(__doc__);
	AScript_DeclareSymbol(super);
	AScript_DeclareSymbol(self);
	AScript_DeclareSymbol(static_);
	AScript_DeclareSymbol(const_);
	AScript_DeclareSymbol(_anonymous_);
	AScript_DeclareSymbol(public_);
	AScript_DeclareSymbol(private_);
	AScript_DeclareSymbol(protected_);
	AScript_DeclareSymbol(dynamic_scope);
	AScript_DeclareSymbol(inside_scope);
	AScript_DeclareSymbol(symbol_func);
	AScript_DeclareSymbol(force);
	AScript_DeclareSymbol(overwrite);
	AScript_DeclareSymbol(raw);
	AScript_DeclareSymbol(stat);
	AScript_DeclareSymbol(map);
	AScript_DeclareSymbol(nomap);
	AScript_DeclareSymbol(fork);
	AScript_DeclareSymbol(strict);
	AScript_DeclareSymbol(loose);
	AScript_DeclareSymbol(block);
	AScript_DeclareSymbol(list);
	AScript_DeclareSymbol(xlist);
	AScript_DeclareSymbol(set);
	AScript_DeclareSymbol(xset);
	AScript_DeclareSymbol(iter);
	AScript_DeclareSymbol(xiter);
	AScript_DeclareSymbol(iteritem);
	AScript_DeclareSymbol(xiteritem);
	AScript_DeclareSymbol(listitem);
	AScript_DeclareSymbol(xlistitem);
	AScript_DeclareSymbol(rev);
	AScript_DeclareSymbol(and);
	AScript_DeclareSymbol(or);
	AScript_DeclareSymbol(xor);
	AScript_DeclareSymbol(root);
	AScript_DeclareSymbol(void_);
	AScript_DeclareSymbol(extern_);
	AScript_DeclareSymbol(reduce);
	AScript_DeclareSymbol(xreduce);
	AScript_DeclareSymbol(local);
	AScript_DeclareSymbol(flat);
	AScript_DeclareSymbol(noflat);
	AScript_DeclareSymbol(index);
	AScript_DeclareSymbol(icase);
	AScript_DeclareSymbol(chop);
	AScript_DeclareSymbol(upper);
	AScript_DeclareSymbol(lower);
	AScript_DeclareSymbol(last_index);
	AScript_DeclareSymbol(indices);
	AScript_DeclareSymbol(transpose);
	AScript_DeclareSymbol(ascend);
	AScript_DeclareSymbol(descend);
	AScript_DeclareSymbol(stable);
	AScript_DeclareSymbol(up);
	AScript_DeclareSymbol(down);
	AScript_DeclareSymbol(center);
	AScript_DeclareSymbol(both);
	AScript_DeclareSymbol(left);
	AScript_DeclareSymbol(right);
	AScript_DeclareSymbol(prev);
	AScript_DeclareSymbol(next);
	AScript_DeclareSymbol(stay);
	AScript_DeclareSymbol(forward);
	AScript_DeclareSymbol(backward);
	AScript_DeclareSymbol(help);
	AScript_DeclareSymbol(symbol);
	AScript_DeclareSymbol(name);
	AScript_DeclareSymbol(fullname);
	AScript_DeclareSymbol(readable);
	AScript_DeclareSymbol(writable);
	AScript_DeclareSymbol(argsymbols);
	AScript_DeclareSymbol(expr);
	AScript_DeclareSymbol(parent);
	AScript_DeclareSymbol(parents);
	AScript_DeclareSymbol(sibling);
	AScript_DeclareSymbol(siblings);
	AScript_DeclareSymbol(child);
	AScript_DeclareSymbol(children);
	AScript_DeclareSymbol(path);
	AScript_DeclareSymbol(shrink);
	AScript_DeclareSymbol(shrinkable);
	AScript_DeclareSymbol(x);
	AScript_DeclareSymbol(y);
	AScript_DeclareSymbol(z);
	AScript_DeclareSymbol(box);
	AScript_DeclareSymbol(imgtype);
	AScript_DeclareSymbol(format);
	AScript_DeclareSymbol(stream);
	AScript_DeclareSymbol(color);
	AScript_DeclareSymbol(width);
	AScript_DeclareSymbol(height);
	AScript_DeclareSymbol(palette);
	AScript_DeclareSymbol(gray);
	AScript_DeclareSymbol(rgb);
	AScript_DeclareSymbol(rgba);
	AScript_DeclareSymbol(rgbq);
	AScript_DeclareSymbol(bgr);
	AScript_DeclareSymbol(bgra);
	AScript_DeclareSymbol(bitmap);
	AScript_DeclareSymbol(red);
	AScript_DeclareSymbol(green);
	AScript_DeclareSymbol(blue);
	AScript_DeclareSymbol(alpha);
	AScript_DeclareSymbol(u8);
	AScript_DeclareSymbol(s8);
	AScript_DeclareSymbol(u16le);
	AScript_DeclareSymbol(s16le);
	AScript_DeclareSymbol(u16be);
	AScript_DeclareSymbol(s16be);
	AScript_DeclareSymbol(channels);
	AScript_DeclareSymbol(len);
	AScript_DeclareSymbol(open);
	AScript_DeclareSymbol(open_l);
	AScript_DeclareSymbol(open_r);
	AScript_DeclareSymbol(sticky);
	AScript_DeclareSymbol(sticky_l);
	AScript_DeclareSymbol(sticky_r);
	AScript_DeclareSymbol(horz);
	AScript_DeclareSymbol(vert);
	AScript_DeclareSymbol(mono);
	AScript_DeclareSymbol(basic);
	AScript_DeclareSymbol(win256);
	AScript_DeclareSymbol(websafe);
	AScript_DeclareSymbol(blank2);
	AScript_DeclareSymbol(blank4);
	AScript_DeclareSymbol(blank8);
	AScript_DeclareSymbol(blank16);
	AScript_DeclareSymbol(blank32);
	AScript_DeclareSymbol(blank64);
	AScript_DeclareSymbol(blank128);
	AScript_DeclareSymbol(blank256);
	AScript_DeclareSymbol(http);
	AScript_DeclareSymbol(ftp);
	AScript_DeclareSymbol(year);
	AScript_DeclareSymbol(month);
	AScript_DeclareSymbol(day);
	AScript_DeclareSymbol(hour);
	AScript_DeclareSymbol(min);
	AScript_DeclareSymbol(sec);
	AScript_DeclareSymbol(usec);
	AScript_DeclareSymbol(wday);
	AScript_DeclareSymbol(yday);
	AScript_DeclareSymbol(days);
	AScript_DeclareSymbol(secs);
	AScript_DeclareSymbol(usecs);
	AScript_DeclareSymbol(single);
	AScript_DeclareSymbol(multi);
	AScript_DeclareSymbol(w3c);
	AScript_DeclareSymbol(asctime);
	AScript_DeclareSymbol(unixtime);
	AScript_DeclareSymbol(utf8);
	AScript_DeclareSymbol(utf16);
	AScript_DeclareSymbol(utf32);
	AScript_DeclareSymbol(msc);
	AScript_DeclareSymbol(bcc);
	AScript_DeclareSymbol(gnuc);
	AScript_DeclareSymbol(file);
	AScript_DeclareSymbol(dir);
private:
	typedef std::set<Symbol *, Symbol::KeyCompare_Name> Content;
private:
	static SymbolPool *_pInst;
	Symbol::UniqNumber _uniqNum;
	Content _content;
private:
	inline SymbolPool() {}
public:
	~SymbolPool();
	static void Initialize();
	static SymbolPool *GetInstance();
	const Symbol *Add(const char *name);
private:
	void _Initialize();
private:
	inline SymbolPool(const SymbolPool &symbolPool) {}
	inline void operator=(const SymbolPool &symbolPool) {}
};

//-----------------------------------------------------------------------------
// utilities
//-----------------------------------------------------------------------------
const Symbol *GetOccurPatternSymbol(OccurPattern occurPattern);

}

#endif
