/*****
 * entry.cc
 * Andy Hammerlindl 2002/08/29
 *
 * All variables, built-in functions and user-defined functions reside
 * within the same namespace.  To keep track of all these, table of
 * "entries" is used.
 *****/

#include <iostream>

#include <cmath>
#include <utility>
#include "entry.h"
#include "coder.h"

using types::signature;

namespace trans {

bool entry::pr::check(action act, coder &c) {
  // We assume PUBLIC permissions and one's without an associated record are not
  // stored.
  assert(perm!=PUBLIC && r!=0);
  return c.inTranslation(r->getLevel()) ||
    (perm == RESTRICTED && act != WRITE);
}

void entry::pr::report(action act, position pos, coder &c) {
  if (!c.inTranslation(r->getLevel())) {
    if (perm == PRIVATE) {
      em.error(pos);
      em << "accessing private field outside of structure";
    }
    else if (perm == RESTRICTED && act == WRITE) {
      em.error(pos);
      em << "modifying non-public field outside of structure";
    }
  }
}

entry::entry(entry &e1, entry &e2) : where(e2.where), pos(e2.pos) {
  perms.insert(perms.end(), e1.perms.begin(), e1.perms.end());
  perms.insert(perms.end(), e2.perms.begin(), e2.perms.end());
}

entry::entry(entry &base, permission perm, record *r)
  : where(base.where), pos(base.pos) {
  perms.insert(perms.end(), base.perms.begin(), base.perms.end());
  addPerm(perm, r);
}

bool entry::checkPerm(action act, coder &c) {
  for (mem::list<pr>::iterator p=perms.begin(); p != perms.end(); ++p)
    if (!p->check(act, c))
      return false;
  return true;
}

void entry::reportPerm(action act, position pos, coder &c) {
  for (mem::list<pr>::iterator p=perms.begin(); p != perms.end(); ++p)
    p->report(act, pos, c);
}


varEntry::varEntry(varEntry &qv, varEntry &v)
  : entry(qv,v), t(v.t),
    location(new qualifiedAccess(qv.location, qv.getLevel(), v.location)) {}

frame *varEntry::getLevel() {
  record *r=dynamic_cast<record *>(t);
  assert(r);
  return r->getLevel();
}

void varEntry::encode(action act, position pos, coder &c) {
  reportPerm(act, pos, c);
  getLocation()->encode(act, pos, c);
}

void varEntry::encode(action act, position pos, coder &c, frame *top) {
  reportPerm(act, pos, c);
  getLocation()->encode(act, pos, c, top);
}

varEntry *qualifyVarEntry(varEntry *qv, varEntry *v)
{
  return qv ? (v ? new varEntry(*qv,*v) : qv) : v;
}


bool tenv::add(symbol *dest,
               names_t::value_type &x, varEntry *qualifier, coder &c)
{
  if (!x.second.empty()) {
    tyEntry *ent=x.second.front();
    if (ent->checkPerm(READ, c)) {
      enter(dest, qualifyTyEntry(qualifier, ent));
      return true;
    }
  }
  return false;
}

void tenv::add(tenv& source, varEntry *qualifier, coder &c) {
  // Enter each distinct (unshadowed) name,type pair.
  for(names_t::iterator p = source.names.begin(); p != source.names.end(); ++p)
    add(p->first, *p, qualifier, c);
}

bool tenv::add(symbol *src, symbol *dest,
               tenv& source, varEntry *qualifier, coder &c) {
  names_t::iterator p = source.names.find(src);
  if (p != source.names.end())
    return add(dest, *p, qualifier, c);
  else
    return false;
}

#ifdef NOHASH //{{{
void venv::add(venv& source, varEntry *qualifier, coder &c)
{
  // Enter each distinct (unshadowed) name,type pair.
  for(names_t::iterator p = source.names.begin();
      p != source.names.end();
      ++p)
    add(p->first, p->first, source, qualifier, c);
}

bool venv::add(symbol *src, symbol *dest,
               venv& source, varEntry *qualifier, coder &c)
{
  bool added=false;
  name_t &list=source.names[src];
  types::overloaded set; // To keep track of what is shadowed.

  for(name_iterator p = list.begin();
      p != list.end();
      ++p) {
    varEntry *v=*p;
    if (!equivalent(v->getType(), &set)) {
      set.addDistinct(v->getType(), src->special);
      if (v->checkPerm(READ, c)) {
        enter(dest, qualifyVarEntry(qualifier, v));
        added=true;
      }
    }
  }
  
  return added;
}

varEntry *venv::lookByType(symbol *name, ty *t)
{
  // Find first applicable function.
  name_t &list = names[name];
  for(name_iterator p = list.begin();
      p != list.end();
      ++p) {
    if (equivalent((*p)->getType(), t))
      return *p;
  }
  return 0;
}

void venv::list(record *module)
{
  bool where=settings::getSetting<bool>("where");
  // List all functions and variables.
  for(names_t::iterator N = names.begin(); N != names.end(); ++N) {
    symbol *s=N->first;
    name_t &list=names[s];
    for(name_iterator p = list.begin(); p != list.end(); ++p) {
      if(!module || (*p)->whereDefined() == module) {
        if(where) cout << (*p)->getPos();
        (*p)->getType()->printVar(cout, s);
        cout << ";\n";
      }
    }
  }
  flush(cout);
}

varEntry *venv::lookInTopScope(symbol *name, ty *t)
{
  scope_t &scope = scopes.front();
  for (scope_iterator p = scope.lower_bound(name);
       p != scope.upper_bound(name);
       ++p) {
    if (name == p->first &&
        equivalent(t, p->second->getType(), name->special))
      return p->second;
  }
  return 0;
}

ty *venv::getType(symbol *name)
{
  types::overloaded set;

  // Find all applicable functions in scope.
  name_t &list = names[name];
  
  for(name_iterator p = list.begin();
      p != list.end();
      ++p) {
    set.addDistinct((*p)->getType(), name->special);
  }

  return set.simplify();
}
// }}}
#else // {{{

ostream& operator<< (ostream& out, const venv::key &k) {
  k.t->printVar(out, k.name);
  return out;
}

#if TEST_COLLISION
bool venv::keyeq::operator()(const key k, const key l) const {
  keyhash kh;
  if (kh(k)==kh(l)) {
    if (base(k,l))
      return true;
    else {
      cerr << "collision: " << endl;
      cerr << "  " << k << " -> " << kh(k) << endl;
      cerr << "  " << l << " -> " << kh(l) << endl;
    }
  }
  return false;
}
#else
bool venv::keyeq::operator()(const key k, const key l) const {
#if 0
  cerr << "k.t = " << k.t << " " << k << endl;
  cerr << "l.t = " << l.t << " " << l << endl;
#endif
  return k.name==l.name &&
    (k.name->special ? equivalent(k.t, l.t) :
     equivalent(k.t->getSignature(),
                l.t->getSignature()));
}
#endif

void venv::remove(key k) {
  //cerr << "removing: " << k << endl;
  value *&val=all[k];
  assert(val);
  if (val->next) {
    val->next->shadowed=false;
    value *temp=val->next;
    val->next=0;
    val=temp;
  }
  else
    all.erase(k);

  // Don't erase it from scopes.top() as that will be popped of the stack at
  // the end of endScope anyway.

  names[k.name].pop_front();
}

void venv::enter(symbol *name, varEntry *v) {
  assert(!scopes.empty());
  key k(name, v);
#ifdef DEBUG_ENTRY
  cout << "entering: " << k << " (t=" << k.t << ")" << endl;
#endif
  value *val=new value(v);

#ifdef DEBUG_ENTRY
  keymap::iterator p=all.find(k);
  if (p!=all.end()) {
    cout << "  over: " << p->first << endl;
  }
#endif
  
  val->next=all[k];
  if (val->next)
    val->next->shadowed=true;

  all[k]=val;
  scopes.top().insert(keymultimap::value_type(k,val));
  names[k.name].push_front(val);
}

void venv::add(venv& source, varEntry *qualifier, coder &c)
{
  // Enter each distinct (unshadowed) name,type pair.
  for(keymap::iterator p = source.all.begin(); p != source.all.end(); ++p) {
    varEntry *v=p->second->v;
    if (v->checkPerm(READ, c))
      enter(p->first.name, qualifyVarEntry(qualifier, v));
  }
}

bool venv::add(symbol *src, symbol *dest,
               venv& source, varEntry *qualifier, coder &c)
{
  bool added=false;
  values &list=source.names[src];

  for (values::iterator p=list.begin(); p!=list.end(); ++p)
    if (!(*p)->shadowed) {
      varEntry *v=(*p)->v;
      if (v->checkPerm(READ, c)) {
        enter(dest, qualifyVarEntry(qualifier, v));
        added=true;
      }
    }

  return added;
}


ty *venv::getType(symbol *name)
{
  //cerr << "getType: " << *name << endl;
  types::overloaded set;
  values &list=names[name];

  for (values::iterator p=list.begin(); p!=list.end(); ++p)
    if (!(*p)->shadowed)
      set.add((*p)->v->getType());

  return set.simplify();
}

void venv::listValues(symbol *name, values &vals, record *module)
{
  ostream& out=cout;

  bool where=settings::getSetting<bool>("where");
  for(values::iterator p = vals.begin(); p != vals.end(); ++p) {
    if(!module || (*p)->v->whereDefined() == module) {
      if(where) out << (*p)->v->getPos();
      if ((*p)->shadowed)
        out << "  <shadowed> ";
      (*p)->v->getType()->printVar(out, name);
      out << ";\n";
    }
  }
  flush(out);
}

void venv::list(record *module)
{
  // List all functions and variables.
  for(namemap::iterator N = names.begin(); N != names.end(); ++N)
    listValues(N->first, N->second,module);
}

void venv::completions(mem::list<symbol *>& l, string start)
{
  for(namemap::iterator N = names.begin(); N != names.end(); ++N)
    if (prefix(start, *(N->first)) && !N->second.empty())
      l.push_back(N->first);
}

#endif // }}}

} // namespace trans
