
// This file is part of the AspectC++ compiler 'ac++'.
// Copyright (C) 1999-2003  The 'ac++' developers (see aspectc.org)
//
// 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., 59 Temple Place, Suite 330, Boston,
// MA  02111-1307  USA

#include "ACModel.h"
#include "Utils.h"

ACModel::ACModel() {
  set_root(0);
  register_namespace1(0, "::");
}

ACM_TUnit *ACModel::setup_tunit(std::string &model_filename, int len, int modi_time) {
  _tunit_file = newTUnit();
  _tunit_file->set_filename(model_filename);
  _tunit_file->set_len(len);
  _tunit_file->set_time(modi_time);
  get_files().insert(_tunit_file);
  file_map().insert(FileMapPair(model_filename, _tunit_file));
  return _tunit_file;
}

ACM_Class * ACModel::register_class1 (ACM_Name *scope, string name, bool in_project) {
  ACM_Name *found = map_lookup(*scope, name);
  if (found && found->type_val() == JPT_Class)
    return (ACM_Class*)found;
  ACM_Class *new_elem = newClass();
  new_elem->set_name(name);
  new_elem->set_builtin(false);
  if (!in_project) new_elem->get_tunits().insert (_tunit_file);
  new_elem->set_intro_target(in_project);
  scope->get_children().insert(new_elem);
  map_insert(*scope, *new_elem, name);
  return new_elem;
}

ACM_Aspect * ACModel::register_aspect1 (ACM_Name *scope, string name, bool in_project) {
  ACM_Name *found = map_lookup(*scope, name);
  if (found && found->type_val() == JPT_Aspect)
    return (ACM_Aspect*)found;
  ACM_Aspect *new_elem = newAspect();
  new_elem->set_name(name);
  new_elem->set_builtin(false);
  if (!in_project) new_elem->get_tunits().insert (_tunit_file);
  new_elem->set_intro_target(in_project);
  scope->get_children().insert(new_elem);
  map_insert(*scope, *new_elem, name);
  return new_elem;
}

ACM_Attribute *ACModel::register_attrdecl1 (ACM_Namespace *parent, const std::string &name)
{
  ACM_Attribute *elem = 0;
  if (parent)
    elem = (ACM_Attribute*)map_lookup(*parent, name);
  if (!elem || elem->type_val() != JPT_Attribute) {
    elem = newAttribute();
    elem->set_name(name);
    // set the 'builtin' flag in phase 2
    if (parent) {
      map_insert(*parent, *elem, name);
      parent->get_children().insert(elem);
    }
  }
  else {
    //for already registered attributes?
    elem = 0;
  }

  return elem;
}

// find an attribute declaration
ACM_Attribute *ACModel::find_attrdecl (const std::vector<std::string> &qual_name) {
  ACM_Name *scope = get_root();
  ACM_Name *found = nullptr;
  for (string name : qual_name) {
    found = map_lookup(*scope, name);
    if (!found)
      return nullptr;
    scope = found;
  }
  if (found->type_val() != JPT_Attribute)
    return nullptr;
  return (ACM_Attribute*)found;
}

ACM_Pointcut *ACModel::register_pointcut1 (ACM_Name *parent, const string &name,
    bool is_virtual, const string& expr) {

  ACM_Pointcut *elem = 0;
  if (parent)
    elem = (ACM_Pointcut*)map_lookup(*parent, name);
  if (!elem || elem->type_val() != JPT_Pointcut) {
    elem = newPointcut();
    elem->set_name(name);
    elem->set_expr (expr);
    elem->set_builtin(false);
    elem->set_kind(PT_NORMAL);
    if (parent) {
      if (is_virtual && expr == "0")
        elem->set_kind(PT_PURE_VIRTUAL);
      else if (is_virtual || overrides_virtual_pointcut(parent, name))
        elem->set_kind(PT_VIRTUAL);
      map_insert(*parent, *elem, name);
      parent->get_children().insert(elem);
    }
  }
  else {
    elem = 0;
  }

  return elem;
}

bool ACModel::overrides_virtual_pointcut (ACM_Name *parent, const string &name) {
  if (parent->type_val() != JPT_Class && parent->type_val() != JPT_Aspect)
    return false;
  ACM_Class *cls = (ACM_Class*)parent;
  typedef ACM_Container<ACM_Class, false> BList;
  const BList &blist = cls->get_bases();
  for (BList::const_iterator i = blist.begin (); i != blist.end(); ++i) {
    ACM_Class *base = (ACM_Class*)*i;
    ACM_Name *elem = map_lookup(*base, name);
    if (!elem || elem->type_val() != JPT_Pointcut)
      continue;
    ACM_Pointcut *base_pct = (ACM_Pointcut*)elem;
    if (base_pct->get_kind() == PT_PURE_VIRTUAL ||
        base_pct->get_kind() == PT_VIRTUAL ||
        overrides_virtual_pointcut (base, name))
      return true;
  }
  return false;
}

ACM_Namespace * ACModel::register_namespace1 (ACM_Name *scope, string name, bool in_project) {
  ACM_Namespace *result = 0;
  assert(scope || name == "::");
  ACM_Name *found = (scope ? map_lookup(*scope, name) : get_root());
  if (!found || found->type_val() != JPT_Namespace) {
    result = newNamespace();
    result->set_name(name);
    result->set_builtin(false);
    if (!in_project) result->get_tunits().insert (_tunit_file);
    if (scope) {
      scope->get_children().insert(result);
      map_insert(*scope, *result, name);
    }
    else {
      set_root(result);
    }
  }
  else
    result = (ACM_Namespace*)found;
  return result;
}

ACM_ClassSlice *ACModel::register_class_slice (ACM_Name *scope, string name, bool is_struct) {
  ACM_Name *found = map_lookup(*scope, name);
  if (found && found->type_val() == JPT_ClassSlice)
    return (ACM_ClassSlice*)found;

  ACM_ClassSlice *new_elem = (ACM_ClassSlice*)newClassSlice();
  new_elem->set_name(name);
  new_elem->set_builtin(false);
  new_elem->set_is_struct(is_struct);
  map_insert(*scope, *new_elem, name);
  scope->get_children().insert(new_elem);
  return new_elem;
}


