#ifndef SYMBOL_H
#define SYMBOL_H
#ifdef __GNUG__
#pragma interface
#endif
#include <types.h>
#include <hashtab.h>

// class Symbol is defined in types.h.

struct SymbolTable : public HashArray {
  public:
    // A HashTable for Symbols. element_len == 4 (== sizeof(Symbol*))
    void initialize(int init_size);
    SymbolTable(int init_size_log);
    SymbolTable() { }
};

class Environment : public GenMap {
  public:
    // Find value of binding with given name; or NULL if no binding.
    Root *lookup_at(const StringC *name)
	{ return lookup_at(name->chars(), name->leng()); }
    virtual Root *lookup_at(const char *name, int nlen = -1) = 0;
    // Set value of binding with given name; new_val==NULL to remove.
    virtual void set_at(Root *new_val, const char *name, int nlen = -1);
    void set_at(Root *new_val, const StringC *name)
	{ set_at(new_val, name->chars(), name->leng()); }
    // Find binding with given name; or NULL if no binding.
    virtual Assignable *binding_at(const StringC *name);

    virtual void xapply(void* dst, Type* dstType, ArgDesc& args);

    // Also need iteration/print methods.
};

class Package : public Environment {
    DECLARE_MEMBERS(Package)
  private:
    Symbol *prim_intern(const char *chars, int len, const StringC *str, int *code);
  public:
    const StringC *_pname; // Primary package name. NULL if deleted.
    const StringC *package_name() { return _pname; }
    inline Root *name() const { return _pname ? _pname : &NilSymbol; }
    int is_deleted() const { return _pname == NULL; }
    Root *nick_names;
    SymbolTable ext_hash;
    SymbolTable int_hash;
    Root* use_list; // An AList terminated by Nil.
    Root* used_by_list; // An AList terminated by Nil.
    Package *next; // In PackageList.
    Package() : ext_hash(), int_hash() { }
    void initialize_only(const StringC* pname);
    void initialize(const StringC* pname, int size);
    Package(const StringC* pname, int size);
    Package(const String* pname, int size);
//    Package(char* pname, int size);
    virtual void printon(ostream&) const;
    Symbol * find_exported(const char *name, int nlen = -1);
    Symbol * find_interned(const char *name, int nlen = -1, int *code = 0)
	{ return prim_intern(name, nlen, (const StringC*)(-1), code); }
    Symbol * intern(char *name, int nlen, int *code=NULL)
	{ return prim_intern(name, nlen, NULL, code); }
    Symbol * intern(const StringC *name, int *code=NULL)
	{ return prim_intern(name->chars(), name->leng(), name, code); }
//    void raw_intern(Symbol *sym);
    int accessible(const Symbol*) const;
    void export(Symbol*);
    void set_nicknames(Root*);
    void set_name(const StringC*);
    void import(Symbol*);
    int uses(Package*p); // Return 1 if p is on use_list.
    int use(Package*p); // Add p to use_list. Result same as uses().
    int unuse(Package*); // Remove p from use_list. Result same as uses().
    int is_shadowed(Symbol*) { return 0; } // For now.
    void make_shadowed(Symbol*) { } // For now.
    int unmake_shadowed(Symbol*) { return 0; } // For now.
    virtual Assignable *binding_at(const StringC *name)
	{ return find_exported(name->chars(), name->leng()); }
    virtual Root *lookup_at(const char *name, int nlen);
    virtual size_t length() { return (size_t)(-1); } // ???
};

extern Package *PackageList; // List of all (registered) Packages in system.
extern Package KeywordPackage asm("__pkKEYWORD");
extern Package BuiltinPackage asm("__pkBUILTIN");
extern Package CLispPackage asm("__pkCOMMON___LISP");
extern Package SchemePackage asm("__pkSCHEME");
extern Package UserPackage asm("__pkUSER");
extern Package *CurrentPackage;
extern Package *LookupPackage (char *str, int len);

/* result codes from Package::intern. See CLtL:2,p.266 */
#define InternNew 0
#define InternInternal 1
#define InternExternal 2
#define InternInherited 3

class Record : public Environment {
    DECLARE_MEMBERS(Record)
  public:
    // ... data fields follow ...
    Record() { }
    virtual size_t length();
    virtual Root * prefix(Root *);
    virtual void printon(ostream&) const;
    inline const RecordType *rtype() const { return (const RecordType*)isA(); }
    virtual Root *lookup_at(const char *name, int nlen = -1);
};

#if C_SYM > 1
struct SymbolInitElement {
    struct Symbol *symbol;
    unsigned short flags;
    unsigned short length;
};
// SymbolInitElement::flags values:
#define KeywordPackageCode 1
#define BuiltinPackageCode 2
#define CLispPackageCode 3
#define SchemePackageCode 4
#define UserPackageCode 5

extern int InitSymbols(SymbolInitElement *, char*);
#endif

typedef struct Type *SymbolHdr;
/*#define DclSymbol(name, len) extern Symbol name;*/
extern Symbol *True, *EOFmark;
#define NoValue True
#define EOF_mark EOFmark

/*#define StringLen(s) (FileHdrOf(s)->end*/
#define KEYWORD_ASM(name) asm("_$KW" name)
#define STRING_ASM(name) asm("_$ST" name)
#define SYM2(x,s) extern struct Symbol x KEYWORD_ASM(#s);
#define DCL_KEYWORD(x,s) extern struct Symbol x KEYWORD_ASM(#s);
#define DCL_KEYWORD3(x,s,len) static /*const*/ Symbol *x = EnterSymbol(s,len);
#define DCL_KEYWORD2(x,s) DCL_KEYWORD3(x,s,-1)

#define SymbolLength(s) (((Symbol*)(s))->length())
#define SymbolHash(s) (((Symbol*)(s))->Hash())
#define SymbolString(s) (((Symbol*)(s))->string())
#define SymbolUnsignedString(s) (((Symbol*)(s))->ustring())

extern Record *AllocRecord(const Type *cl, size_t size);
extern void StringInsert(HashTable *tab, const StringC* str, void *value);
extern StringC **StringLookup(HashTable *tab, char *s, int len);

inline int IsCharacter(Root *v) { return v->isMemberOf(*Character::desc()); }
#endif /!*SYMBOL_H*/
