void AMXStackFramePrinter::Print(const AMXStackFrame &frame) {
  PrintReturnAddress(frame);
  *stream_ << " in ";

  AMXDebugSymbol caller = GetCallerSymbol(frame);
  if (caller) {
    PrintCallerName(frame, caller);
  } else {
    PrintCallerName(frame);
  }

  *stream_ << " (";
  PrintArgumentList(frame);
  *stream_ << ")";

  if (HaveDebugInfo() && UsesAutomata(frame)) {
    *stream_ << " ";
    PrintState(frame);
  }

  if (HaveDebugInfo() && frame.return_address() != 0) {
    *stream_ << " at ";
    PrintSourceLocation(frame.return_address());
  }
}
void AMXStackFramePrinter::PrintReturnAddress(const AMXStackFrame &frame) {
  if (frame.return_address() == 0) {
    *stream_ << "????????";
  } else {
    char old_fill = stream_->fill('0');
    *stream_ << std::hex << std::setw(kCellWidthChars)
             << frame.return_address()
             << std::dec;
    stream_->fill(old_fill);
  }
}
void AMXStackFramePrinter::Print(const AMXStackFrame &frame) {
  PrintReturnAddress(frame);
  stream_ << " in ";

  PrintCallerNameAndArguments(frame);

  if (debug_info_.IsLoaded() && UsesAutomata(frame)) {
    stream_ << " ";
    PrintState(frame);
  }

  if (debug_info_.IsLoaded() && frame.return_address() != 0) {
    stream_ << " at ";
    PrintSourceLocation(frame.return_address());
  }
}
void AMXStackFramePrinter::PrintArgumentList(const AMXStackFrame &frame) {
  AMXStackFrame prev_frame = frame.GetPrevious();

  if (prev_frame) {
    // Although the symbol's code start address points at the state
    // switch code block, function arguments actually use the real
    // function address for the code start because in different states
    // they may be not the same.
    cell arg_address = frame.caller_address();
    if (UsesAutomata(frame)) {
      arg_address = GetRealFunctionAddress(frame.amx(), frame.caller_address(),
                                                        frame.return_address());
    }

    std::vector<AMXDebugSymbol> args;
    int num_actual_args = 0;

    if (HaveDebugInfo()) {
      std::remove_copy_if(debug_info_->GetSymbols().begin(),
                          debug_info_->GetSymbols().end(),
                          std::back_inserter(args),
                          std::not1(IsArgumentOf(arg_address)));
      std::sort(args.begin(), args.end());
      num_actual_args = static_cast<int>(args.size());
    } else {
      static const int kMaxRawArgs = 10;
      num_actual_args = std::min(kMaxRawArgs,
                                 GetNumArgs(frame.amx(), prev_frame.address()));
    }

    // Print a comma-separated list of arguments and their values.
    // If debug info is not available argument names are omitted,
    // so only the values are printed.
    for (int i = 0; i < num_actual_args; i++) {
      if (i > 0) {
        *stream_ << ", ";
      }
      if (HaveDebugInfo()) {
        PrintArgument(prev_frame, args[i], i);
      } else {
        PrintArgument(prev_frame, i);
      }
    }

    // If the number of actual arguments passed to the function exceeds
    // that obtained via debug info the function may take a variable
    // number of arguments. In this case we don't evaluate them but just
    // just say that they are present as we can't say anything about
    // their names and types.
    int num_var_args = GetNumArgs(frame.amx(), prev_frame.address())
                     - num_actual_args;
    if (num_var_args > 0) {
      if (num_actual_args != 0) {
        *stream_ << ", ";
      }
      PrintVariableArguments(num_var_args);
    }
  }
}
void AMXStackFramePrinter::PrintState(const AMXStackFrame &frame) {
  AMXDebugAutomaton automaton = debug_info_->GetAutomaton(
    GetStateVarAddress(frame.amx(), frame.caller_address()));
  if (automaton) {
    std::vector<cell> states = GetStateIDs(frame.amx(), frame.caller_address(),
                                                        frame.return_address());
    if (!states.empty()) {
      *stream_ << "<" << automaton.GetName() << ":";
      for (std::size_t i = 0; i < states.size(); i++ ) {
        if (i > 0) {
          *stream_ << ", ";
        }
        AMXDebugState state = debug_info_->GetState(automaton.GetID(), states[i]);
        if (state) {
          *stream_ << state.GetName();
        }
      }
      *stream_ << ">";
    }
  }
}
void AMXStackFramePrinter::PrintReturnAddress(const AMXStackFrame &frame) {
  PrintAddress(frame.return_address());
}