/*
 * Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com)
 *
 *    This source code is free software; you can redistribute it
 *    and/or modify it in source code form 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

# include "config.h"
# include "compiler.h"

# include  "netlist.h"
# include  "netclass.h"
# include  "netenum.h"
# include  <cstring>
# include  <cstdlib>
# include  <sstream>
# include  "ivl_assert.h"

class PExpr;

/*
 * The NetScope class keeps a scope tree organized. Each node of the
 * scope tree points to its parent, its right sibling and its leftmost
 * child. The root node has no parent or siblings. The node stores the
 * name of the scope. The complete hierarchical name of the scope is
 * formed by appending the path of scopes from the root to the scope
 * in question.
 */

NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t, bool nest, bool prog)
: type_(t), name_(n), nested_module_(nest), program_block_(prog), up_(up)
{
      events_ = 0;
      lcounter_ = 0;
      is_auto_ = false;
      is_cell_ = false;
      calls_stask_ = false;
      in_final_ = false;

      if (up) {
	    assert(t!=CLASS);
	    need_const_func_ = up->need_const_func_;
	    is_const_func_ = up->is_const_func_;
	    time_unit_ = up->time_unit();
	    time_prec_ = up->time_precision();
	    time_from_timescale_ = up->time_from_timescale();
	      // Need to check for duplicate names?
	    up_->children_[name_] = this;
      } else {
	    need_const_func_ = false;
	    is_const_func_ = false;
	    time_unit_ = 0;
	    time_prec_ = 0;
	    time_from_timescale_ = false;
	    assert(t==MODULE || t==PACKAGE || t==CLASS);
      }

      switch (t) {
	  case NetScope::TASK:
	    task_ = 0;
	    break;
	  case NetScope::FUNC:
	    func_ = 0;
	    break;
	  case NetScope::MODULE:
	  case NetScope::PACKAGE:
	    module_name_ = perm_string();
	    break;
	  case NetScope::CLASS:
	    class_def_ = 0;
	    break;
	  default:  /* BEGIN_END and FORK_JOIN, do nothing */
	    break;
      }
      func_pform_ = 0;
      elab_stage_ = 1;
      lineno_ = 0;
      def_lineno_ = 0;
      genvar_tmp_val = 0;
}

NetScope::~NetScope()
{
      lcounter_ = 0;

	/* name_ and module_name_ are perm-allocated. */
}

void NetScope::set_line(const LineInfo*info)
{
      file_ = info->get_file();
      def_file_ = file_;
      lineno_ = info->get_lineno();
      def_lineno_ = lineno_;
}

void NetScope::set_line(perm_string file, unsigned lineno)
{
      file_ = file;
      def_file_ = file;
      lineno_ = lineno;
      def_lineno_ = lineno;
}

void NetScope::set_line(perm_string file, perm_string def_file,
                        unsigned lineno, unsigned def_lineno)
{
      file_ = file;
      def_file_ = def_file;
      lineno_ = lineno;
      def_lineno_ = def_lineno;
}

void NetScope::set_parameter(perm_string key, bool is_annotatable,
			     PExpr*val, ivl_variable_type_t type__,
			     PExpr*msb, PExpr*lsb, bool signed_flag,
			     bool local_flag,
			     NetScope::range_t*range_list,
			     const LineInfo&file_line)
{
      param_expr_t&ref = parameters[key];
      ref.is_annotatable = is_annotatable;
      ref.msb_expr = msb;
      ref.lsb_expr = lsb;
      ref.val_expr = val;
      ref.val_scope = this;
      ref.type = type__;
      ref.msb = 0;
      ref.lsb = 0;
      ref.signed_flag = signed_flag;
      ref.local_flag = local_flag;
      ivl_assert(file_line, ref.range == 0);
      ref.range = range_list;
      ref.val = 0;
      ref.set_line(file_line);
}

/*
 * This is a simplified version of set_parameter, for use when the
 * parameter value is already known. It is currently only used to
 * add a genvar to the parameter list.
 */
void NetScope::set_parameter(perm_string key, NetExpr*val,
			     const LineInfo&file_line)
{
      param_expr_t&ref = parameters[key];
      ref.is_annotatable = false;
      ref.msb_expr = 0;
      ref.lsb_expr = 0;
      ref.val_expr = 0;
      ref.val_scope = this;
      ref.type = IVL_VT_BOOL;
      ref.msb = 0;
      ref.lsb = 0;
      ref.signed_flag = false;
      ref.val = val;
      ref.set_line(file_line);
}

bool NetScope::auto_name(const char*prefix, char pad, const char* suffix)
{
	// Find the current reference to myself in the parent scope.
      map<hname_t,NetScope*>::iterator self = up_->children_.find(name_);
      assert(self != up_->children_.end());
      assert(self->second == this);

      char tmp[32];
      int pad_pos = strlen(prefix);
      int max_pos = sizeof(tmp) - strlen(suffix) - 1;
      strncpy(tmp, prefix, sizeof(tmp));
      tmp[31] = 0;

	// Try a variety of potential new names. Make sure the new
	// name is not in the parent scope. Keep looking until we are
	// sure we have a unique name, or we run out of names to try.
      while (pad_pos <= max_pos) {
	      // Try this name...
	    strcat(tmp + pad_pos, suffix);
	    hname_t new_name(lex_strings.make(tmp));
	    if (!up_->child(new_name)) {
		    // Ah, this name is unique. Rename myself, and
		    // change my name in the parent scope.
		  name_ = new_name;
		  up_->children_.erase(self);
		  up_->children_[name_] = this;
		  return true;
	    }
	    tmp[pad_pos++] = pad;
      }
      return false;
}

/*
 * Return false if the parameter does not already exist.
 * A parameter is not automatically created.
 */
bool NetScope::replace_parameter(perm_string key, PExpr*val, NetScope*scope)
{
      bool flag = false;

      if (parameters.find(key) != parameters.end()) {
	    param_expr_t&ref = parameters[key];
	    ref.val_expr = val;
            ref.val_scope = scope;
	    flag = true;
      }

      return flag;
}

bool NetScope::make_parameter_unannotatable(perm_string key)
{
      bool flag = false;

      if (parameters.find(key) != parameters.end()) {
	    param_expr_t&ref = parameters[key];
	    flag = ref.is_annotatable;
	    ref.is_annotatable = false;
      }

      return flag;
}

/*
 * NOTE: This method takes a const char* as a key to lookup a
 * parameter, because we don't save that pointer. However, due to the
 * way the map<> template works, we need to *cheat* and use the
 * perm_string::literal method to fake the compiler into doing the
 * compare without actually creating a perm_string.
 */
const NetExpr* NetScope::get_parameter(Design*des,
				       const char* key,
				       const NetExpr*&msb,
				       const NetExpr*&lsb)
{
      return get_parameter(des, perm_string::literal(key), msb, lsb);
}

const NetExpr* NetScope::get_parameter(Design*des,
				       perm_string key,
				       const NetExpr*&msb,
				       const NetExpr*&lsb)
{
      map<perm_string,param_expr_t>::iterator idx;

      idx = parameters.find(key);
      if (idx != parameters.end()) {
            if (idx->second.val_expr)
                  evaluate_parameter_(des, idx);

	    msb = idx->second.msb;
	    lsb = idx->second.lsb;
	    return idx->second.val;
      }

      map<perm_string,NetEConstEnum*>::const_iterator eidx;

      eidx = enum_names_.find(key);
      if (eidx != enum_names_.end()) {
	    msb = 0;
	    lsb = 0;
	    return eidx->second;
      }

      return 0;
}

NetScope::param_ref_t NetScope::find_parameter(perm_string key)
{
      map<perm_string,param_expr_t>::iterator idx;

      idx = parameters.find(key);
      if (idx != parameters.end()) return idx;

	// To get here the parameter must already exist, so we should
	// never get here.
      assert(0);
}

NetScope::TYPE NetScope::type() const
{
      return type_;
}

void NetScope::print_type(ostream&stream) const
{
      switch (type_) {
	case BEGIN_END:
	    stream << "sequential block";
	    break;
	case FORK_JOIN:
	    stream << "parallel block";
	    break;
	case FUNC:
	    stream << "function";
	    break;
	case MODULE:
	    stream << "module <" << module_name_ << "> instance";
	    break;
	case TASK:
	    stream << "task";
	    break;
	case GENBLOCK:
	    stream << "generate block";
	    break;
	case PACKAGE:
	    stream << "package " << module_name_;
	    break;
	case CLASS:
	    stream << "class";
	    break;
      }
}

void NetScope::set_task_def(NetTaskDef*def)
{
      assert( type_ == TASK );
      assert( task_ == 0 );
      task_ = def;
}

NetTaskDef* NetScope::task_def()
{
      assert( type_ == TASK );
      return task_;
}

const NetTaskDef* NetScope::task_def() const
{
      assert( type_ == TASK );
      return task_;
}

void NetScope::set_func_def(NetFuncDef*def)
{
      assert( type_ == FUNC );
      assert( func_ == 0 );
      func_ = def;
}

NetFuncDef* NetScope::func_def()
{
      assert( type_ == FUNC );
      return func_;
}

bool NetScope::in_func() const
{
      return (type_ == FUNC) ? true : false;
}

const NetFuncDef* NetScope::func_def() const
{
      assert( type_ == FUNC );
      return func_;
}

void NetScope::set_class_def(netclass_t*def)
{
      assert( type_ == CLASS );
      assert( class_def_==0  );
      class_def_ = def;
}

const netclass_t* NetScope::class_def(void) const
{
      if (type_==CLASS)
	    return class_def_;
      else
	    return 0;
}

void NetScope::set_module_name(perm_string n)
{
      assert(type_==MODULE || type_==PACKAGE);
      module_name_ = n;
}

perm_string NetScope::module_name() const
{
      assert(type_==MODULE || type_==PACKAGE);
      return module_name_;
}

void NetScope::set_num_ports(unsigned int num_ports)
{
    assert(type_ == MODULE);
    assert(ports_.empty());
    ports_.resize( num_ports );
}

void NetScope::add_module_port_net(NetNet*subport)
{
      assert(type_ == MODULE);
      port_nets.push_back(subport);
}


void NetScope::add_module_port_info( unsigned idx, perm_string name, PortType::Enum ptype,
                                unsigned long width )
{
      assert(type_ == MODULE);
      PortInfo &info = ports_[idx];
      info.name = name;
      info.type = ptype;
      info.width = width;
}


unsigned NetScope::module_port_nets() const
{
      assert(type_ == MODULE);
      return port_nets.size();
}


const std::vector<PortInfo> & NetScope::module_port_info() const
{
      assert(type_ == MODULE);
      return ports_;
}



NetNet* NetScope::module_port_net(unsigned idx) const
{
      assert(type_ == MODULE);
      assert(idx < port_nets.size());
      return port_nets[idx];
}

void NetScope::time_unit(int val)
{
      time_unit_ = val;
}

void NetScope::time_precision(int val)
{
      time_prec_ = val;
}

void NetScope::time_from_timescale(bool val)
{
      time_from_timescale_ = val;
}

int NetScope::time_unit() const
{
      return time_unit_;
}

int NetScope::time_precision() const
{
      return time_prec_;
}

bool NetScope::time_from_timescale() const
{
      return time_from_timescale_;
}

perm_string NetScope::basename() const
{
      return name_.peek_name();
}

void NetScope::add_event(NetEvent*ev)
{
      assert(ev->scope_ == 0);
      ev->scope_ = this;
      ev->snext_ = events_;
      events_ = ev;
}

void NetScope::rem_event(NetEvent*ev)
{
      assert(ev->scope_ == this);
      ev->scope_ = 0;
      if (events_ == ev) {
	    events_ = ev->snext_;

      } else {
	    NetEvent*cur = events_;
	    while (cur->snext_ != ev) {
		  assert(cur->snext_);
		  cur = cur->snext_;
	    }
	    cur->snext_ = ev->snext_;
      }

      ev->snext_ = 0;
}


NetEvent* NetScope::find_event(perm_string name)
{
      for (NetEvent*cur = events_;  cur ;  cur = cur->snext_)
	    if (cur->name() == name)
		  return cur;

      return 0;
}

void NetScope::add_genvar(perm_string name, LineInfo *li)
{
      assert((type_ == MODULE) || (type_ == GENBLOCK));
      genvars_[name] = li;
}

LineInfo* NetScope::find_genvar(perm_string name)
{
      if (genvars_.find(name) != genvars_.end())
	    return genvars_[name];
      else
            return 0;
}

void NetScope::add_signal(NetNet*net)
{
      signals_map_[net->name()]=net;
}

void NetScope::rem_signal(NetNet*net)
{
      assert(net->scope() == this);
      signals_map_.erase(net->name());
}

/*
 * This method looks for a signal within the current scope. The name
 * is assumed to be the base name of the signal, so no sub-scopes are
 * searched.
 */
NetNet* NetScope::find_signal(perm_string key)
{
      if (signals_map_.find(key)!=signals_map_.end())
	    return signals_map_[key];
      else
	    return 0;
}

void NetScope::add_enumeration_set(netenum_t*enum_set)
{
      enum_sets_.push_back(enum_set);
}

bool NetScope::add_enumeration_name(netenum_t*enum_set, perm_string name)
{
      netenum_t::iterator enum_val = enum_set->find_name(name);
      assert(enum_val != enum_set->end_name());

      NetEConstEnum*val = new NetEConstEnum(this, name, enum_set, enum_val->second);

      pair<map<perm_string,NetEConstEnum*>::iterator, bool> cur;
      cur = enum_names_.insert(make_pair(name,val));

	// Return TRUE if the name is added (i.e. is NOT a duplicate.)
      return cur.second;
}

const netenum_t*NetScope::enumeration_for_name(perm_string name)
{
      NetEConstEnum*tmp = enum_names_[name];
      assert(tmp != 0);

      return tmp->enumeration();
}

void NetScope::add_class(netclass_t*net_class)
{
      classes_[net_class->get_name()] = net_class;
}

netclass_t*NetScope::find_class(perm_string name)
{
	// Special class: The scope itself is the class that we are
	// looking for. This may happen for example when elaborating
	// methods within the class.
      if (type_==CLASS && name_==hname_t(name))
	    return class_def_;

	// Look for the class that directly within this scope.
      map<perm_string,netclass_t*>::const_iterator cur = classes_.find(name);
      if (cur != classes_.end())
	    return cur->second;

	// If this is a module scope, then look no further.
      if (type_==MODULE)
	    return 0;

	// If there is no further to look, ...
      if (up_ == 0)
	    return 0;

	// Try looking up for the class.
      return up_->find_class(name);
}

/*
 * This method locates a child scope by name. The name is the simple
 * name of the child, no hierarchy is searched.
 */
NetScope* NetScope::child(const hname_t&name)
{
      map<hname_t,NetScope*>::iterator cur = children_.find(name);
      if (cur == children_.end())
	    return 0;
      else
	    return cur->second;
}

const NetScope* NetScope::child(const hname_t&name) const
{
      map<hname_t,NetScope*>::const_iterator cur = children_.find(name);
      if (cur == children_.end())
	    return 0;
      else
	    return cur->second;
}

perm_string NetScope::local_symbol()
{
      ostringstream res;
      res << "_s" << (lcounter_++);
      return lex_strings.make(res.str());
}
