// Filename: cppPointerType.cxx
// Created by:  drose (19Oct99)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////


#include "cppPointerType.h"
#include "cppFunctionType.h"
#include "cppIdentifier.h"

////////////////////////////////////////////////////////////////////
//     Function: CPPPointerType::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
CPPPointerType::
CPPPointerType(CPPType *pointing_at) :
  CPPType(CPPFile()),
  _pointing_at(pointing_at)
{
}

////////////////////////////////////////////////////////////////////
//     Function: CPPPointerType::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 CPPPointerType::
is_fully_specified() const {
  return CPPType::is_fully_specified() &&
    _pointing_at->is_fully_specified();
}

////////////////////////////////////////////////////////////////////
//     Function: CPPPointerType::substitute_decl
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
CPPDeclaration *CPPPointerType::
substitute_decl(CPPDeclaration::SubstDecl &subst,
                CPPScope *current_scope, CPPScope *global_scope) {
  SubstDecl::const_iterator si = subst.find(this);
  if (si != subst.end()) {
    return (*si).second;
  }

  CPPPointerType *rep = new CPPPointerType(*this);
  rep->_pointing_at =
    _pointing_at->substitute_decl(subst, current_scope, global_scope)
    ->as_type();

  if (rep->_pointing_at == _pointing_at) {
    delete rep;
    rep = this;
  }
  rep = CPPType::new_type(rep)->as_pointer_type();
  subst.insert(SubstDecl::value_type(this, rep));
  return rep;
}

////////////////////////////////////////////////////////////////////
//     Function: CPPPointerType::resolve_type
//       Access: Public, Virtual
//  Description: If this CPPType object is a forward reference or
//               other nonspecified reference to a type that might now
//               be known a real type, returns the real type.
//               Otherwise returns the type itself.
////////////////////////////////////////////////////////////////////
CPPType *CPPPointerType::
resolve_type(CPPScope *current_scope, CPPScope *global_scope) {
  CPPType *ptype = _pointing_at->resolve_type(current_scope, global_scope);

  if (ptype != _pointing_at) {
    CPPPointerType *rep = new CPPPointerType(*this);
    rep->_pointing_at = ptype;
    return CPPType::new_type(rep);
  }
  return this;
}

////////////////////////////////////////////////////////////////////
//     Function: CPPPointerType::is_tbd
//       Access: Public, Virtual
//  Description: Returns true if the type, or any nested type within
//               the type, is a CPPTBDType and thus isn't fully
//               determined right now.  In this case, calling
//               resolve_type() may or may not resolve the type.
////////////////////////////////////////////////////////////////////
bool CPPPointerType::
is_tbd() const {
  return _pointing_at->is_tbd();
}

////////////////////////////////////////////////////////////////////
//     Function: CPPPointerType::is_equivalent
//       Access: Public, Virtual
//  Description: This is a little more forgiving than is_equal(): it
//               returns true if the types appear to be referring to
//               the same thing, even if they may have different
//               pointers or somewhat different definitions.  It's
//               useful for parameter matching, etc.
////////////////////////////////////////////////////////////////////
bool CPPPointerType::
is_equivalent(const CPPType &other) const {
  const CPPPointerType *ot = ((CPPType *)&other)->as_pointer_type();
  if (ot == (CPPPointerType *)NULL) {
    return CPPType::is_equivalent(other);
  }

  return _pointing_at->is_equivalent(*ot->_pointing_at);
}

////////////////////////////////////////////////////////////////////
//     Function: CPPPointerType::output
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
void CPPPointerType::
output(ostream &out, int indent_level, CPPScope *scope, bool complete) const {
  /*
  CPPFunctionType *ftype = _pointing_at->as_function_type();
  if (ftype != (CPPFunctionType *)NULL) {
    // Pointers to functions are a bit of a special case; we have to
    // be a little more careful about where the '*' goes.

    string star = "*";
    if ((ftype->_flags & CPPFunctionType::F_method_pointer) != 0) {
      // We have to output pointers-to-method with a scoping before the
      // '*'.
      star = ftype->_class_owner->get_fully_scoped_name() + "::*";
    }

    _pointing_at->output_instance(out, indent_level, scope, complete,
                                  star, "");

  } else {
    _pointing_at->output(out, indent_level, scope, complete);
    out << " *";
  }
  */
  output_instance(out, indent_level, scope, complete, "", "");
}

////////////////////////////////////////////////////////////////////
//     Function: CPPPointerType::output_instance
//       Access: Public, Virtual
//  Description: Formats a C++-looking line that defines an instance
//               of the given type, with the indicated name.  In most
//               cases this will be "type name", but some types have
//               special exceptions.
////////////////////////////////////////////////////////////////////
void CPPPointerType::
output_instance(ostream &out, int indent_level, CPPScope *scope,
                bool complete, const string &prename,
                const string &name) const {
  string star = "*";

  CPPFunctionType *ftype = _pointing_at->as_function_type();
  if (ftype != NULL &&
      ((ftype->_flags & CPPFunctionType::F_method_pointer) != 0)) {
    // We have to output pointers-to-method with a scoping before the
    // '*'.
    star = ftype->_class_owner->get_fully_scoped_name() + "::*";
  }

  _pointing_at->output_instance(out, indent_level, scope, complete,
                                star + prename, name);
}

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

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


////////////////////////////////////////////////////////////////////
//     Function: CPPPointerType::is_equal
//       Access: Protected, Virtual
//  Description: Called by CPPDeclaration() to determine whether this type is
//               equivalent to another type of the same type.
////////////////////////////////////////////////////////////////////
bool CPPPointerType::
is_equal(const CPPDeclaration *other) const {
  const CPPPointerType *ot = ((CPPDeclaration *)other)->as_pointer_type();
  assert(ot != NULL);

  return _pointing_at == ot->_pointing_at;
}


////////////////////////////////////////////////////////////////////
//     Function: CPPPointerType::is_less
//       Access: Protected, Virtual
//  Description: Called by CPPDeclaration() to determine whether this type
//               should be ordered before another type of the same
//               type, in an arbitrary but fixed ordering.
////////////////////////////////////////////////////////////////////
bool CPPPointerType::
is_less(const CPPDeclaration *other) const {
  const CPPPointerType *ot = ((CPPDeclaration *)other)->as_pointer_type();
  assert(ot != NULL);

  return _pointing_at < ot->_pointing_at;
}
