// Filename: cppInstance.cxx
// Created by:  drose (19Oct99)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////


#include "cppInstance.h"
#include "cppInstanceIdentifier.h"
#include "cppIdentifier.h"
#include "cppTemplateScope.h"
#include "cppFunctionType.h"
#include "cppSimpleType.h"
#include "cppExpression.h"
#include "cppPreprocessor.h"
#include "cppParameterList.h"
#include "cppReferenceType.h"
#include "cppConstType.h"
#include "indent.h"

#include <algorithm>

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
CPPInstance::
CPPInstance(CPPType *type, const string &name, int storage_class) :
  CPPDeclaration(CPPFile()),
  _type(type),
  _ident(new CPPIdentifier(name)),
  _storage_class(storage_class)
{
  _initializer = NULL;
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
CPPInstance::
CPPInstance(CPPType *type, CPPIdentifier *ident, int storage_class) :
  CPPDeclaration(CPPFile()),
  _type(type),
  _ident(ident),
  _storage_class(storage_class)
{
  _initializer = NULL;
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::Constructor
//       Access: Public
//  Description: Constructs a new CPPInstance object that defines a
//               variable of the indicated type according to the type
//               and the InstanceIdentifier.  The InstanceIdentifier
//               pointer is deallocated.
////////////////////////////////////////////////////////////////////
CPPInstance::
CPPInstance(CPPType *type, CPPInstanceIdentifier *ii, int storage_class,
            const CPPFile &file) :
  CPPDeclaration(file)
{
  _type = ii->unroll_type(type);
  _ident = ii->_ident;
  ii->_ident = NULL;
  _storage_class = storage_class;
  _initializer = NULL;

  CPPParameterList *params = ii->get_initializer();
  if (params != (CPPParameterList *)NULL) {
    // In this case, the instance has a parameter-list initializer, e.g.:
    //
    //   int foo(0);
    //
    // We really should save this initializer in the instance object.
    // But we don't for now, since no one really cares about
    // initializers anyway.
  }

  delete ii;
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::Copy Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
CPPInstance::
CPPInstance(const CPPInstance &copy) :
  CPPDeclaration(copy),
  _type(copy._type),
  _ident(copy._ident),
  _initializer(copy._initializer),
  _storage_class(copy._storage_class)
{
  assert(_type != NULL);
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::Destructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
CPPInstance::
~CPPInstance() {
  // Can't delete the identifier.  Don't try.
}


////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::make_typecast_function
//       Access: Public, Static
//  Description: Constructs and returns a new CPPInstance object that
//               corresponds to a function prototype declaration for a
//               typecast method, whose return type is implicit in the
//               identifier type.
////////////////////////////////////////////////////////////////////
CPPInstance *CPPInstance::
make_typecast_function(CPPInstance *inst, CPPIdentifier *ident,
                       CPPParameterList *parameters, int function_flags) {
  CPPType *type = CPPType::new_type(inst->_type);
  delete inst;

  function_flags |= (int)CPPFunctionType::F_operator_typecast;

  CPPType *ft =
    CPPType::new_type(new CPPFunctionType(type, parameters, function_flags));

  return new CPPInstance(ft, ident);
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::Equivalence Operator
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
bool CPPInstance::
operator == (const CPPInstance &other) const {
  if (_type != other._type) {
    return false;
  }
  if (_storage_class != other._storage_class) {
    return false;
  }

  // We *do* care about the identifier.  We need to differentiate
  // types of function variables, among possibly other things, based
  // on the identifier.
  if ((_ident == NULL && other._ident != NULL) ||
      (_ident != NULL && other._ident == NULL) ||
      (_ident != NULL && other._ident != NULL && *_ident != *other._ident))
  {
    return false;
  }

  // We similarly care about the initializer.
  if ((_initializer == NULL && other._initializer != NULL) ||
      (_initializer != NULL && other._initializer == NULL) ||
      (_initializer != NULL && other._initializer != NULL &&
       *_initializer != *other._initializer))
  {
    return false;
  }

  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::Nonequivalence Operator
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
bool CPPInstance::
operator != (const CPPInstance &other) const {
  return !operator == (other);
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::Ordering Operator
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
bool CPPInstance::
operator < (const CPPInstance &other) const {
  if (_type != other._type) {
    return _type < other._type;
  }
  if (_storage_class != other._storage_class) {
    return _storage_class < other._storage_class;
  }

  // We *do* care about the identifier.  We need to differentiate
  // types of function variables, among possibly other things, based
  // on the identifier.
  if ((_ident == NULL && other._ident != NULL) ||
      (_ident != NULL && other._ident == NULL) ||
      (_ident != NULL && other._ident != NULL && *_ident != *other._ident))
  {
    if (_ident == NULL || other._ident == NULL) {
      return _ident < other._ident;
    }
    return *_ident < *other._ident;
  }

  // We similarly care about the initializer.
  if ((_initializer == NULL && other._initializer != NULL) ||
      (_initializer != NULL && other._initializer == NULL) ||
      (_initializer != NULL && other._initializer != NULL &&
       *_initializer != *other._initializer))
  {
    if (_initializer == NULL || other._initializer == NULL) {
      return _initializer < other._initializer;
    }
    return *_initializer < *other._initializer;
  }

  return false;
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::set_initializer
//       Access: Public
//  Description: Sets the value of the expression that is used to
//               initialize the variable, or the default value for a
//               parameter.  If a non-null expression is set on a
//               function declaration, it implies that the function is
//               pure virtual.
////////////////////////////////////////////////////////////////////
void CPPInstance::
set_initializer(CPPExpression *initializer) {
  if (_type->as_function_type() != (CPPFunctionType *)NULL) {
    // This is a function declaration.
    if (initializer == (CPPExpression *)NULL) {
      _storage_class &= ~SC_pure_virtual;
    } else {
      _storage_class |= SC_pure_virtual;
    }
    _initializer = (CPPExpression *)NULL;
  } else {
    _initializer = initializer;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::is_scoped
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
bool CPPInstance::
is_scoped() const {
  if (_ident == NULL) {
    return false;
  } else {
    return _ident->is_scoped();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::get_scope
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
CPPScope *CPPInstance::
get_scope(CPPScope *current_scope, CPPScope *global_scope,
          CPPPreprocessor *error_sink) const {
  if (_ident == NULL) {
    return current_scope;
  } else {
    return _ident->get_scope(current_scope, global_scope, error_sink);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::get_simple_name
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
string CPPInstance::
get_simple_name() const {
  if (_ident == NULL) {
    return "";
  } else {
    return _ident->get_simple_name();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::get_local_name
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
string CPPInstance::
get_local_name(CPPScope *scope) const {
  if (_ident == NULL) {
    return "";
  } else {
    return _ident->get_local_name(scope);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::get_fully_scoped_name
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
string CPPInstance::
get_fully_scoped_name() const {
  if (_ident == NULL) {
    return "";
  } else {
    return _ident->get_fully_scoped_name();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::check_for_constructor
//       Access: Public
//  Description: If this is a function type instance, checks whether
//               the function name matches the class name (or ~name),
//               and if so, flags it as a constructor (or destructor).
////////////////////////////////////////////////////////////////////
void CPPInstance::
check_for_constructor(CPPScope *current_scope, CPPScope *global_scope) {
  CPPScope *scope = get_scope(current_scope, global_scope);
  if (scope == NULL) {
    scope = current_scope;
  }

  CPPFunctionType *func = _type->as_function_type();
  if (func != NULL) {
    string method_name = get_local_name(scope);
    string class_name = scope->get_local_name();

    if (!method_name.empty() && !class_name.empty()) {
      if (method_name == class_name) {
        CPPType *void_type = CPPType::new_type
          (new CPPSimpleType(CPPSimpleType::T_void));

        _type = CPPType::new_type
          (new CPPFunctionType(void_type, func->_parameters,
                               func->_flags | CPPFunctionType::F_constructor));

      } else if (method_name == "~" + class_name) {
        CPPType *void_type = CPPType::new_type
          (new CPPSimpleType(CPPSimpleType::T_void));

        _type = CPPType::new_type
          (new CPPFunctionType(void_type, func->_parameters,
                               func->_flags | CPPFunctionType::F_destructor));
      }
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::instantiate
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
CPPDeclaration *CPPInstance::
instantiate(const CPPTemplateParameterList *actual_params,
            CPPScope *current_scope, CPPScope *global_scope,
            CPPPreprocessor *error_sink) const {
  if (!is_template()) {
    if (error_sink != NULL) {
      error_sink->warning("Ignoring template parameters for instance " +
                          _ident->get_local_name());
    }
    return (CPPInstance *)this;
  }

  Instantiations::const_iterator ii;
  ii = _instantiations.find(actual_params);
  if (ii != _instantiations.end()) {
    // We've already instantiated this instance with these parameters.
    // Return that.
    return (*ii).second;
  }


  CPPTemplateScope *tscope = get_template_scope();

  CPPDeclaration::SubstDecl subst;
  actual_params->build_subst_decl(tscope->_parameters, subst,
                                  current_scope, global_scope);

  CPPInstance *inst =
    ((CPPInstance *)this)->substitute_decl(subst, current_scope, global_scope)
    ->as_instance();
  if (inst == this) {
    // Hmm, nothing to substitute.  Make a new instance anyway, so we
    // can change the name.
    inst = new CPPInstance(*this);
  }
  assert(inst != NULL);
  inst->_ident = inst->_ident->substitute_decl(subst, current_scope, global_scope);
  if (inst->_ident == _ident) {
    inst->_ident = new CPPIdentifier(*inst->_ident);
  }
  inst->_ident->_names.back().set_templ
    (new CPPTemplateParameterList(*actual_params));

  inst->_template_scope = NULL;

  ((CPPInstance *)this)->_instantiations.insert(Instantiations::value_type(actual_params, inst));
  return inst;
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::is_fully_specified
//       Access: Public, Virtual
//  Description: Returns true if this declaration is an actual,
//               factual declaration, or false if some part of the
//               declaration depends on a template parameter which has
//               not yet been instantiated.
////////////////////////////////////////////////////////////////////
bool CPPInstance::
is_fully_specified() const {
  if (_ident != NULL && !_ident->is_fully_specified()) {
    return false;
  }
  if (_initializer != NULL && !_initializer->is_fully_specified()) {
    return false;
  }
  return CPPDeclaration::is_fully_specified() &&
    _type->is_fully_specified();
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::substitute_decl
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
CPPDeclaration *CPPInstance::
substitute_decl(CPPDeclaration::SubstDecl &subst,
                CPPScope *current_scope, CPPScope *global_scope) {
  CPPDeclaration *top =
    CPPDeclaration::substitute_decl(subst, current_scope, global_scope);
  if (top != this) {
    return top;
  }

  CPPInstance *rep = new CPPInstance(*this);
  CPPDeclaration *new_type =
    _type->substitute_decl(subst, current_scope, global_scope);
  rep->_type = new_type->as_type();

  if (rep->_type == NULL) {
    cerr << "Type " << *_type << " became " << *new_type << " which isn't a type\n";
    rep->_type = _type;
  }

  if (_initializer != NULL) {
    rep->_initializer =
      _initializer->substitute_decl(subst, current_scope, global_scope)
      ->as_expression();
  }

  if (rep->_type == _type &&
      rep->_initializer == _initializer) {
    delete rep;
    rep = this;
  }

  subst.insert(SubstDecl::value_type(this, rep));
  return rep;
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::output
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
void CPPInstance::
output(ostream &out, int indent_level, CPPScope *scope, bool complete) const {
  output(out, indent_level, scope, complete, -1);
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::output
//       Access: Public
//  Description: The extra parameter comes into play only when we
//               happen to be outputting a function prototype.  See
//               CPPFunctionType::output().
////////////////////////////////////////////////////////////////////
void CPPInstance::
output(ostream &out, int indent_level, CPPScope *scope, bool complete,
       int num_default_parameters) const {
  assert(_type != NULL);

  if (_type->is_parameter_expr()) {
    // In this case, the whole thing is really an expression, and not
    // an instance at all.  This can only happen if we parsed an
    // instance declaration while we thought we were parsing a
    // function prototype.
    out << *_initializer;
    return;
  }

  if (is_template()) {
    get_template_scope()->_parameters.write_formal(out, scope);
    indent(out, indent_level);
  }
  if (_storage_class & SC_static) {
    out << "static ";
  }
  if (_storage_class & SC_extern) {
    out << "extern ";
  }
  if (_storage_class & SC_c_binding) {
    out << "\"C\" ";
  }
  if (_storage_class & SC_virtual) {
    out << "virtual ";
  }
  if (_storage_class & SC_inline) {
    out << "inline ";
  }
  if (_storage_class & SC_explicit) {
    out << "explicit ";
  }
  if (_storage_class & SC_register) {
    out << "register ";
  }
  if (_storage_class & SC_volatile) {
    out << "volatile ";
  }
  if (_storage_class & SC_mutable) {
    out << "mutable ";
  }

  string name;
  if (_ident != NULL) {
    name = _ident->get_local_name(scope);
  }

  if (_type->as_function_type()) {
    _type->as_function_type()->
      output_instance(out, indent_level, scope, complete, "", name,
                      num_default_parameters);

  } else {
    _type->output_instance(out, indent_level, scope, complete, "", name);
  }

  if (_storage_class & SC_pure_virtual) {
    out << " = 0";
  }
  if (_initializer != NULL) {
    out << " = (" << *_initializer << ")";
  }
}


////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::get_subtype
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
CPPDeclaration::SubType CPPInstance::
get_subtype() const {
  return ST_instance;
}

////////////////////////////////////////////////////////////////////
//     Function: CPPInstance::as_instance
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
CPPInstance *CPPInstance::
as_instance() {
  return this;
}
