void RpcGenerator::generateImplementsMethod(const google::protobuf::ServiceDescriptor *descriptor,
    google::protobuf::io::Printer &printer) const
{
  printer.PrintRaw(implementsReturnType);


  printer.Print(" $serviceName$_Dispatcher::$implementsSignature$\n{\n",
      "serviceName", descriptor->name(),
      "implementsSignature",  implementsSignature);
  printer.Indent();
  printer.Print("// This is NOT thread safe ...\n");
  printer.Print("static $implementsReturnType$ methodIds;\n",
      "implementsReturnType", implementsReturnType);
  printer.Print("static bool init = true;\n");
  printer.Print("if (init) {\n");
  printer.Indent();
  for(int j=0; j<descriptor->method_count(); j++) {
    const MethodDescriptor *method = descriptor->method(j);
    string methodName = method->full_name();
    int methodId = RpcPlugin::generateExtensionNumber(methodName);
    ostringstream methodIdStr;
    methodIdStr << methodId;

    printer.Print("methodIds.push_back($methodId$);\n", "methodId",
        methodIdStr.str());
  }
  printer.Print("init = false;\n");
  printer.Outdent();
  printer.Print("}\n");
  printer.Print("return methodIds;\n");
  printer.Outdent();
  printer.Print("}\n");
}
void RpcGenerator::generateSentExternalBlock(const string &variableName,
    const string &channelName, const google::protobuf::Descriptor *descriptor,
    google::protobuf::io::Printer &printer) const
{
  for(int i=0; i< descriptor->field_count(); i++) {
    const FieldDescriptor *field = descriptor->field(i);

    if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
      string messageTypeName = field->message_type()->full_name();

      if (messageTypeName.find("external.") == 0) {
        string externalClass = externalTypeToClass(messageTypeName);

        std::ostringstream stream;
        stream << "obj" << i;
        string var = stream.str();
        printer.Print("if ($variableName$->has_$fieldName$()) {\n",
          "variableName", variableName, "fieldName", field->lowercase_name());
        printer.Indent();
        printer.Print("$externalClass$ *$var$ = ", "externalClass", externalClass, "var", var);
        printer.Print("$variableName$->$fieldName$().get();\n", "variableName",
            variableName, "fieldName", field->lowercase_name());

        printer.Print("if ($var$) {\n", "var", var);
        printer.Indent();

        printer.Print("$externalClass$Serializer serializer($var$);\n",
            "externalClass", externalClass, "var", var);
        printer.Print("size_t size = serializer.size();\n");
        std::map<string, string> variables;
        variables["channelName"] = channelName;
        printer.Print("std::vector<unsigned char> data(size);\n");
        this->generateErrorCheck("serializer.serialize(&data[0], size)", variables,
            printer, "$channelName$->setErrorString(\"Serialization failed\");return;\n");

        variables.clear();
        variables["channelName"] = channelName;

        // Send the size first
        this->generateErrorCheck("$channelName$->send(size)", variables,
                    printer);

        this->generateErrorCheck("$channelName$->send(&data[0], size)", variables,
            printer);
        printer.Outdent();
        printer.Print("}\n");
        printer.Outdent();
        printer.Print("}\n");
      }
    }
  }
}
void RpcGenerator::generateSentVtkBlock(const string &variableName,
    const string &channelName, const google::protobuf::Descriptor *descriptor,
    google::protobuf::io::Printer &printer) const
{
  for(int i=0; i< descriptor->field_count(); i++) {
    const FieldDescriptor *field = descriptor->field(i);

    if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
      string messageTypeName = field->message_type()->full_name();

      if (messageTypeName.find("vtk.") == 0) {
        string vtkType = messageTypeName.substr(4);

        std::ostringstream stream;
        stream << "obj" << i;
        string var = stream.str();
        printer.Print("if ($variableName$->has_$fieldName$()) {\n",
          "variableName", variableName, "fieldName", field->lowercase_name());
        printer.Indent();
        printer.Print("$vtkType$ *$var$ = ", "vtkType", vtkType, "var", var);
        printer.Print("$variableName$->$fieldName$().get();\n", "variableName",
            variableName, "fieldName", field->lowercase_name());

        printer.Print("if ($var$) {\n", "var", var);
        printer.Indent();

        std::map<string, string> variables;
        variables["var"] = var;
        variables["channelName"] = channelName;

        this->generateErrorCheck("$channelName$->send($var$)", variables,
            printer);
        printer.Outdent();
        printer.Print("}\n");
        printer.Outdent();
        printer.Print("}\n");
      }
    }
  }
}
void RpcGenerator::generateRelyMethod(
    const google::protobuf::ServiceDescriptor *descriptor,
    google::protobuf::io::Printer &printer) const
{
  printer.Print("void $service$_Dispatcher::$replySignature$\n{\n",
      "service", descriptor->full_name(), "replySignature",
      replySignature);

  printer.Indent();

  printer.Print("switch(info.methodId) {\n");
  printer.Indent();

  for(int j=0; j<descriptor->method_count(); j++) {
    const MethodDescriptor *method = descriptor->method(j);
    string methodName = method->full_name();
    int methodId = RpcPlugin::generateExtensionNumber(methodName);
    string inputTypeName = method->input_type()->name();
    string outputTypeName = method->output_type()->name();

    if (isVoidType(outputTypeName))
      continue;

    ostringstream methodIdStr;
    methodIdStr << methodId;
    printer.Print("case $methodId$ : {\n", "methodId", methodIdStr.str());
    printer.Indent();
    printer.Print("// $methodName$\n", "methodName",  methodName);

    printer.Print("// Prepare the response\n");
    printer.Print("rpc::Message msg;\n");
    printer.Print("rpc::Response *response = msg.mutable_response();\n");
    printer.Print("uint64 requestId = info.requestMessageEnvelope->request().id();\n");
    printer.Print("response->set_id(requestId);\n");
    string extensionName = toExtensionName(method->full_name());
    std::ostringstream var;
    var << "tmp" << j;

    printer.Print("$responseType$ *$var$ = static_cast<$responseType$ *>(info.response);\n",
        "responseType", outputTypeName, "var", var.str());

    // Add error info
    printer.Print("if ($var$->hasError()) {\n", "var", var.str());
    printer.Indent();
    printer.Print("response->set_errorstring($var$->errorString());\n", "var", var.str());
    printer.Print("response->set_errorcode($var$->errorCode());\n", "var", var.str());
    printer.Outdent();
    printer.Print("}\n");
    printer.Print("response->SetAllocatedExtension($extensionName$, $var$);\n",
        "extensionName", extensionName + "_response", "var", var.str());
    printer.Print("// Make sure request is cleaned up\n");
    printer.Print("std::auto_ptr<rpc::Message> request;\n");
    printer.Print("request.reset(info.requestMessageEnvelope);\n");
    this->generateErrorCheck("info.replyChannel->send(&msg)", printer);

    // send any VTK object we need to.
    this->generateSentVtkBlock(var.str(), "info.replyChannel",
        method->output_type(), printer);

    // send any external objects we need to.
    this->generateSentExternalBlock(var.str(), "info.replyChannel",
        method->output_type(), printer);

    printer.Outdent();
    printer.Print("break;\n");
    printer.Print("}\n");
  }

  printer.Print("default:\n");
  printer.Indent();
  printer.Print("// TODO error case\n");
  printer.Print("info.replyChannel->setErrorString(\"No reply case for messageId: \" + info.methodId);\n");
  printer.Outdent();
  printer.Outdent();
  printer.Print("}\n");
  printer.Outdent();
  printer.Print("}\n");
}
void RpcGenerator::generateDispatchMethodBody(
    const google::protobuf::ServiceDescriptor *descriptor,
    google::protobuf::io::Printer &printer) const
{
  printer.Indent();

  printer.Print("switch(methodId) { \n");
  printer.Indent();

  for(int j=0; j<descriptor->method_count(); j++) {
    const MethodDescriptor *method = descriptor->method(j);
    string methodName = method->full_name();
    int methodId = RpcPlugin::generateExtensionNumber(methodName);
    string inputTypeName = method->input_type()->name();
    string outputTypeName = method->output_type()->name();
    ostringstream methodIdStr;
    methodIdStr << methodId;
    printer.Print("case $methodId$ : {\n", "methodId", methodIdStr.str());
    printer.Indent();
    printer.Print("// $methodName$\n", "methodName",  methodName);

    if (!isVoidType(inputTypeName)) {
      printer.Print("const $inputType$ *in = static_cast<const $inputType$ *>(request);\n",
          "inputType", inputTypeName);

      // Receive any VTK types that might be on the wire ...
      generateReceiveVtkBlock("request", "replyChannel",
          method->input_type(),printer);

      // Receive any external type that might be on the wire ...
      generateReceiveExternalBlock("request", "replyChannel",
          method->input_type(),printer);
    }

    if (!isVoidType(outputTypeName)) {
      printer.Print("$outputType$ *out = new $outputType$();\n",
          "outputType", outputTypeName);
      printer.Print("ProtoCall::Runtime::ReplyInfo info;\n");
      printer.Print("info.methodId = $methodId$;\n", "methodId", methodIdStr.str());
      printer.Print("info.requestMessageEnvelope = requestMessageEnvelope;\n");
      printer.Print("info.response = out;\n");
      printer.Print("info.replyChannel = replyChannel;\n");
      string dispatcherName = descriptor->name() + "_Dispatcher";
      printer.Print("::google::protobuf::Closure *done = "
          "::google::protobuf::NewCallback(this, &$dispatcherName$::reply, info);\n",
          "dispatcherName", dispatcherName);
    }
    else {
      printer.Print("::google::protobuf::Closure *done = google::protobuf::NewCallback(&google::protobuf::DoNothing);\n");
    }

    printer.Print("static_cast<$service$ *>(m_service)->$methodName$(", "service",
        descriptor->name(), "methodName", method->name());

    if (!isVoidType(inputTypeName))
      printer.Print("in");

    if (!isVoidType(inputTypeName) && !isVoidType(outputTypeName))
      printer.Print(", ");

    if (!isVoidType(outputTypeName))
      printer.Print("out");

    if (!isVoidType(inputTypeName) || !isVoidType(outputTypeName))
      printer.Print(", ");

    printer.Print("done);\n");


    printer.Outdent();

    printer.Print("break;\n");
    printer.Print("}\n");
  }

  printer.Print("default:\n");
  printer.Indent();
  printer.Print("replyChannel->setErrorString(\"No case for messageId: \" +  methodId);");
  printer.Outdent();
  printer.Outdent();
  printer.Print("}\n");


  printer.Outdent();

}