/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 * Contains some contributions under the Thrift Software License.
 * Please see doc/old-thrift-license.txt in the Thrift distribution for
 * details.
 */

#include <cassert>

#include <string>
#include <fstream>
#include <iostream>
#include <vector>
#include <list>

#include <stdlib.h>
#include <sys/stat.h>
#include <sstream>
#include <cctype>

#include "thrift/platform.h"
#include "thrift/generate/t_oop_generator.h"

using std::map;
using std::ofstream;
using std::ostream;
using std::ostringstream;
using std::string;
using std::stringstream;
using std::vector;

static const string endl = "\n"; // avoid ostream << std::endl flushes

class t_delphi_generator : public t_oop_generator {
public:
  t_delphi_generator(t_program* program,
                     const std::map<std::string, std::string>& parsed_options,
                     const std::string& option_string)
    : t_oop_generator(program) {
    (void)option_string;
    indent_impl_ = 0;
    has_forward = false;
    has_enum = false;
    has_const = false;
    std::map<std::string, std::string>::const_iterator iter;

    ansistr_binary_ = false;
    register_types_ = false;
    constprefix_ = false;
    events_ = false;
    xmldoc_ = false;
    async_ = false;
    for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
      if( iter->first.compare("ansistr_binary") == 0) {
        ansistr_binary_ = true;
      } else if( iter->first.compare("register_types") == 0) {
        register_types_ = true;
      } else if( iter->first.compare("constprefix") == 0) {
        constprefix_ = true;
      } else if( iter->first.compare("events") == 0) {
        events_ = true;
      } else if( iter->first.compare("xmldoc") == 0) {
        xmldoc_ = true;
      } else if( iter->first.compare("async") == 0) {
        async_ = true;
      } else {
        throw "unknown option delphi:" + iter->first;
      }
    }

    out_dir_base_ = "gen-delphi";
    escape_.clear();
    escape_['\''] = "''";
  }

  void init_generator();
  void close_generator();

  void generate_consts(std::vector<t_const*> consts);

  void generate_typedef(t_typedef* ttypedef);
  void generate_enum(t_enum* tenum);
  void generate_forward_declaration(t_struct* tstruct);
  void generate_struct(t_struct* tstruct);
  void generate_xception(t_struct* txception);
  void generate_service(t_service* tservice);
  void generate_property(ostream& out, t_field* tfield, bool isPublic, bool is_xception);
  void generate_property_writer_(ostream& out, t_field* tfield, bool isPublic);

  void generate_delphi_property(ostream& out,
                                bool struct_is_exception,
                                t_field* tfield,
                                bool isPublic,
                                std::string fieldPrefix = "");
  void generate_delphi_isset_reader_definition(ostream& out, t_field* tfield, bool is_xception);
  void generate_delphi_property_reader_definition(ostream& out,
                                                  t_field* tfield,
                                                  bool is_xception_class);
  void generate_delphi_property_writer_definition(ostream& out,
                                                  t_field* tfield,
                                                  bool is_xception_class);
  void generate_delphi_property_reader_impl(ostream& out,
                                            std::string cls_prefix,
                                            std::string name,
                                            t_type* type,
                                            t_field* tfield,
                                            std::string fieldPrefix,
                                            bool is_xception_class);
  void generate_delphi_property_writer_impl(ostream& out,
                                            std::string cls_prefix,
                                            std::string name,
                                            t_type* type,
                                            t_field* tfield,
                                            std::string fieldPrefix,
                                            bool is_xception_class,
                                            bool is_union,
                                            bool is_xception_factory,
                                            std::string xception_factroy_name);
  void generate_delphi_clear_union_value(ostream& out,
                                         std::string cls_prefix,
                                         std::string name,
                                         t_type* type,
                                         t_field* tfield,
                                         std::string fieldPrefix,
                                         bool is_xception_class,
                                         bool is_union,
                                         bool is_xception_factory,
                                         std::string xception_factroy_name);
  void generate_delphi_isset_reader_impl(ostream& out,
                                         std::string cls_prefix,
                                         std::string name,
                                         t_type* type,
                                         t_field* tfield,
                                         std::string fieldPrefix,
                                         bool is_xception);
  void generate_delphi_struct_writer_impl(ostream& out,
                                          std::string cls_prefix,
                                          t_struct* tstruct,
                                          bool is_exception);
  void generate_delphi_struct_result_writer_impl(ostream& out,
                                                 std::string cls_prefix,
                                                 t_struct* tstruct,
                                                 bool is_exception);

  void generate_delphi_struct_tostring_impl(ostream& out,
                                            std::string cls_prefix,
                                            t_struct* tstruct,
                                            bool is_exception,
                                            bool is_x_factory);

  void add_delphi_uses_list(string unitname);

  void generate_delphi_struct_reader_impl(ostream& out,
                                          std::string cls_prefix,
                                          t_struct* tstruct,
                                          bool is_exception);
  void generate_delphi_create_exception_impl(ostream& out,
                                             string cls_prefix,
                                             t_struct* tstruct,
                                             bool is_exception);

  bool const_needs_var(t_type* type);
  void print_const_prop(std::ostream& out, string name, t_type* type, t_const_value* value);
  void print_private_field(std::ostream& out, string name, t_type* type, t_const_value* value);
  void print_const_value(std::ostream& vars,
                         std::ostream& out,
                         std::string name,
                         t_type* type,
                         t_const_value* value);
  void initialize_field(std::ostream& vars,
                        std::ostream& out,
                        std::string name,
                        t_type* type,
                        t_const_value* value);
  void finalize_field(std::ostream& out,
                      std::string name,
                      t_type* type,
                      t_const_value* value,
                      std::string cls_nm = "");
  std::string render_const_value(std::ostream& local_vars,
                                 std::ostream& out,
                                 std::string name,
                                 t_type* type,
                                 t_const_value* value);
  void print_const_def_value(std::ostream& vars,
                             std::ostream& out,
                             std::string name,
                             t_type* type,
                             t_const_value* value,
                             std::string cls_nm = "");
  std::string make_constants_classname();

  void generate_delphi_struct(t_struct* tstruct, bool is_exception);
  void generate_delphi_struct_impl(ostream& out,
                                   std::string cls_prefix,
                                   t_struct* tstruct,
                                   bool is_exception,
                                   bool is_result = false,
                                   bool is_x_factory = false);
  void print_delphi_struct_type_factory_func(ostream& out, t_struct* tstruct);
  void generate_delphi_struct_type_factory(ostream& out,
                                           std::string cls_prefix,
                                           t_struct* tstruct,
                                           bool is_exception,
                                           bool is_result = false,
                                           bool is_x_factory = false);
  void generate_delphi_struct_type_factory_registration(ostream& out,
                                                        std::string cls_prefix,
                                                        t_struct* tstruct,
                                                        bool is_exception,
                                                        bool is_result = false,
                                                        bool is_x_factory = false);
  void generate_delphi_struct_definition(std::ostream& out,
                                         t_struct* tstruct,
                                         bool is_xception = false,
                                         bool in_class = false,
                                         bool is_result = false,
                                         bool is_x_factory = false);
  void generate_delphi_struct_reader(std::ostream& out, t_struct* tstruct);
  void generate_delphi_struct_result_writer(std::ostream& out, t_struct* tstruct);
  void generate_delphi_struct_writer(std::ostream& out, t_struct* tstruct);
  void generate_delphi_struct_tostring(std::ostream& out, t_struct* tstruct);

  void generate_function_helpers(t_function* tfunction);
  void generate_service_interface(t_service* tservice);
  void generate_service_interface(t_service* tservice, bool for_async);
  void generate_service_helpers(t_service* tservice);
  void generate_service_client(t_service* tservice);
  void generate_service_server(t_service* tservice);
  void generate_process_function(t_service* tservice, t_function* function);

  void generate_deserialize_field(std::ostream& out,
                                  bool is_xception,
                                  t_field* tfield,
                                  std::string prefix,
                                  std::ostream& local_vars);
  void generate_deserialize_struct(std::ostream& out,
                                   t_struct* tstruct,
                                   std::string name,
                                   std::string prefix);
  void generate_deserialize_container(ostream& out,
                                      bool is_xception,
                                      t_type* ttype,
                                      string name,
                                      std::ostream& local_vars);

  void generate_deserialize_set_element(std::ostream& out,
                                        bool is_xception,
                                        t_set* tset,
                                        std::string prefix,
                                        std::ostream& local_vars);
  void generate_deserialize_map_element(std::ostream& out,
                                        bool is_xception,
                                        t_map* tmap,
                                        std::string prefix,
                                        std::ostream& local_vars);
  void generate_deserialize_list_element(std::ostream& out,
                                         bool is_xception,
                                         t_list* list,
                                         std::string prefix,
                                         std::ostream& local_vars);

  void generate_serialize_field(std::ostream& out,
                                bool is_xception,
                                t_field* tfield,
                                std::string prefix,
                                std::ostream& local_vars);
  void generate_serialize_struct(std::ostream& out,
                                 t_struct* tstruct,
                                 std::string prefix,
                                 std::ostream& local_vars);
  void generate_serialize_container(std::ostream& out,
                                    bool is_xception,
                                    t_type* ttype,
                                    std::string prefix,
                                    std::ostream& local_vars);
  void generate_serialize_map_element(std::ostream& out,
                                      bool is_xception,
                                      t_map* tmap,
                                      std::string iter,
                                      std::string map,
                                      std::ostream& local_vars);
  void generate_serialize_set_element(std::ostream& out,
                                      bool is_xception,
                                      t_set* tmap,
                                      std::string iter,
                                      std::ostream& local_vars);
  void generate_serialize_list_element(std::ostream& out,
                                       bool is_xception,
                                       t_list* tlist,
                                       std::string iter,
                                       std::ostream& local_vars);

  void delphi_type_usings(std::ostream& out);
  std::string delphi_thrift_usings();

  std::string type_name(t_type* ttype,
                        bool b_cls = false,
                        bool b_no_postfix = false,
                        bool b_exception_factory = false,
                        bool b_full_exception_factory = false);
  std::string normalize_clsnm(std::string name,
                              std::string prefix,
                              bool b_no_check_keyword = false);
  std::string make_valid_delphi_identifier(std::string const& fromName);
  std::string input_arg_prefix(t_type* ttype);

  std::string base_type_name(t_base_type* tbase);
  std::string declare_field(t_field* tfield,
                            bool init = false,
                            std::string prefix = "",
                            bool is_xception_class = false);
  std::string function_signature(t_function* tfunction,
                                 bool for_async,
                                 std::string full_cls = "",
                                 bool is_xception = false);
  std::string argument_list(t_struct* tstruct);
  std::string constructor_argument_list(t_struct* tstruct, std::string current_indent);
  std::string type_to_enum(t_type* ttype);
  std::string prop_name(t_field* tfield, bool is_xception = false);
  std::string prop_name(std::string name, bool is_xception = false);
  std::string constructor_param_name(string name);

  void write_enum(std::string line);
  void write_forward_decr(std::string line);
  void write_const(std::string line);
  void write_struct(std::string line);
  void write_service(std::string line);

  virtual std::string autogen_comment() {
    return std::string("(**\n") + " * Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
           + " *\n" + " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
           + " *)\n";
  }

  string replace_all(string contents, string search, string replace);
  string xml_encode(string contents);
  string xmldoc_encode(string contents);
  string xmlattrib_encode(string contents);
  void generate_delphi_doc(std::ostream& out, t_field* field);
  void generate_delphi_doc(std::ostream& out, t_doc* tdoc);
  void generate_delphi_doc(std::ostream& out, t_function* tdoc);
  void generate_delphi_docstring_comment(std::ostream& out, string contents);

  bool type_can_be_null(t_type* ttype) {
    while (ttype->is_typedef()) {
      ttype = ((t_typedef*)ttype)->get_type();
    }

    return ttype->is_container() || ttype->is_struct() || ttype->is_xception();
  }

private:
  std::string namespace_name_;
  std::ostringstream s_forward_decr;
  std::ostringstream s_enum;
  std::ostringstream s_const;
  std::ostringstream s_struct;
  std::ostringstream s_service;
  std::ostringstream s_const_impl;
  std::ostringstream s_struct_impl;
  std::ostringstream s_service_impl;
  std::ostringstream s_type_factory_registration;
  std::ostringstream s_type_factory_funcs;
  bool has_forward;
  bool has_enum;
  bool has_const;
  std::string namespace_dir_;
  std::map<std::string, int> delphi_keywords;
  std::map<std::string, int> delphi_reserved_method;
  std::map<std::string, int> delphi_reserved_method_exception;
  std::map<std::string, int> types_known;
  std::list<t_typedef*> typedefs_pending;
  std::vector<std::string> uses_list;
  void create_keywords();
  bool find_keyword(std::map<std::string, int>& keyword_map, std::string name);
  std::string normalize_name(std::string name,
                             bool b_method = false,
                             bool b_exception_method = false);
  std::string empty_value(t_type* type);
  bool is_fully_defined_type(t_type* ttype);
  void add_defined_type(t_type* ttype);
  void init_known_types_list();
  bool is_void(t_type* type);
  int indent_impl_;
  bool ansistr_binary_;
  bool register_types_;
  bool constprefix_;
  bool events_;
  bool xmldoc_;
  bool async_;
  void indent_up_impl() { ++indent_impl_; };
  void indent_down_impl() { --indent_impl_; };
  std::string indent_impl() {
    std::string ind = "";
    int i;
    for (i = 0; i < indent_impl_; ++i) {
      ind += "  ";
    }
    return ind;
  };
  std::ostream& indent_impl(std::ostream& os) { return os << indent_impl(); };
};

string t_delphi_generator::replace_all(string contents, string search, string repl) {
  string str(contents);

  size_t slen = search.length();
  size_t rlen = repl.length();
  size_t incr = (rlen > 0) ? rlen : 1;

  if (slen > 0) {
    size_t found = str.find(search);
    while ((found != string::npos) && (found < str.length())) {
      str.replace(found, slen, repl);
      found = str.find(search, found + incr);
    }
  }

  return str;
}

// XML encoding
string t_delphi_generator::xml_encode(string contents) {
  string str(contents);

  // escape the escape
  str = replace_all(str, "&", "&amp;");

  // other standard XML entities
  str = replace_all(str, "<", "&lt;");
  str = replace_all(str, ">", "&gt;");

  return str;
}

// XML attribute encoding
string t_delphi_generator::xmlattrib_encode(string contents) {
  string str(xml_encode(contents));

  // our attribs are enclosed in "
  str = replace_all(str, "\"", "\\\"");

  return str;
}

// XML encoding for doc comments
string t_delphi_generator::xmldoc_encode(string contents) {
  string str(xml_encode(contents));

  // XMLDoc specific: convert linebreaks into <para>graphs</para>
  str = replace_all(str, "\r\n", "\r");
  str = replace_all(str, "\n", "\r");
  str = replace_all(str, "\r", "</para>\n<para>");

  return str;
}

void t_delphi_generator::generate_delphi_docstring_comment(ostream& out, string contents) {
  if (xmldoc_) {
    generate_docstring_comment(out,
                               "{$REGION 'XMLDoc'}/// <summary>\n",
                               "/// ",
                               "<para>" + contents + "</para>",
                               "/// </summary>\n{$ENDREGION}\n");
  }
}

void t_delphi_generator::generate_delphi_doc(ostream& out, t_field* field) {
  if (xmldoc_) {
    if (field->get_type()->is_enum()) {
      string combined_message = xmldoc_encode(field->get_doc()) + "\n<seealso cref=\""
                                + xmldoc_encode(type_name(field->get_type())) + "\"/>";
      generate_delphi_docstring_comment(out, combined_message);
    } else {
      generate_delphi_doc(out, (t_doc*)field);
    }
  }
}

void t_delphi_generator::generate_delphi_doc(ostream& out, t_doc* tdoc) {
  if (tdoc->has_doc() && xmldoc_) {
    generate_delphi_docstring_comment(out, xmldoc_encode(tdoc->get_doc()));
  }
}

void t_delphi_generator::generate_delphi_doc(ostream& out, t_function* tfunction) {
  if (tfunction->has_doc() && xmldoc_) {
    stringstream ps;
    const vector<t_field*>& fields = tfunction->get_arglist()->get_members();
    vector<t_field*>::const_iterator p_iter;
    for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) {
      t_field* p = *p_iter;
      ps << "\n<param name=\"" << xmlattrib_encode(p->get_name()) << "\">";
      if (p->has_doc()) {
        std::string str = p->get_doc();
        str.erase(std::remove(str.begin(), str.end(), '\n'),
                  str.end()); // remove the newlines that appear from the parser
        ps << xmldoc_encode(str);
      }
      ps << "</param>";
    }
    generate_docstring_comment(out,
                               "{$REGION 'XMLDoc'}",
                               "/// ",
                               "<summary><para>" + xmldoc_encode(tfunction->get_doc())
                               + "</para></summary>" + ps.str(),
                               "{$ENDREGION}\n");
  }
}

bool t_delphi_generator::find_keyword(std::map<std::string, int>& keyword_map, std::string name) {
  std::string::size_type len = name.length();

  if (len <= 0) {
    return false;
  }

  std::string::size_type nlast = name.find_last_of('_');

  if (nlast >= 1) {
    if (nlast == (len - 1)) {
      string new_name(name, 0, nlast);
      return find_keyword(keyword_map, new_name);
    }
  }
  return (keyword_map[name] == 1);
}

std::string t_delphi_generator::normalize_name(std::string name,
                                               bool b_method,
                                               bool b_exception_method) {
  string tmp(name);
  std::transform(tmp.begin(), tmp.end(), tmp.begin(), static_cast<int (*)(int)>(std::tolower));

  bool b_found = false;

  if (find_keyword(delphi_keywords, tmp)) {
    b_found = true;
  } else if (b_method && find_keyword(delphi_reserved_method, tmp)) {
    b_found = true;
  } else if (b_exception_method && find_keyword(delphi_reserved_method_exception, tmp)) {
    b_found = true;
  }

  if (b_found) {
    return name + "_";
  } else {
    return name;
  }
}

void t_delphi_generator::create_keywords() {
  delphi_keywords["and"] = 1;
  delphi_keywords["end"] = 1;
  delphi_keywords["interface"] = 1;
  delphi_keywords["raise"] = 1;
  delphi_keywords["uses"] = 1;
  delphi_keywords["array"] = 1;
  delphi_keywords["except"] = 1;
  delphi_keywords["is"] = 1;
  delphi_keywords["record"] = 1;
  delphi_keywords["var"] = 1;
  delphi_keywords["as"] = 1;
  delphi_keywords["exports"] = 1;
  delphi_keywords["label"] = 1;
  delphi_keywords["repeat"] = 1;
  delphi_keywords["while"] = 1;
  delphi_keywords["asm"] = 1;
  delphi_keywords["file"] = 1;
  delphi_keywords["library"] = 1;
  delphi_keywords["resourcestring"] = 1;
  delphi_keywords["with"] = 1;
  delphi_keywords["begin"] = 1;
  delphi_keywords["finalization"] = 1;
  delphi_keywords["mod"] = 1;
  delphi_keywords["set"] = 1;
  delphi_keywords["xor"] = 1;
  delphi_keywords["case"] = 1;
  delphi_keywords["finally"] = 1;
  delphi_keywords["nil"] = 1;
  delphi_keywords["shl"] = 1;
  delphi_keywords["class"] = 1;
  delphi_keywords["for"] = 1;
  delphi_keywords["not"] = 1;
  delphi_keywords["shr"] = 1;
  delphi_keywords["const"] = 1;
  delphi_keywords["function"] = 1;
  delphi_keywords["object"] = 1;
  delphi_keywords["string"] = 1;
  delphi_keywords["constructor"] = 1;
  delphi_keywords["goto"] = 1;
  delphi_keywords["of"] = 1;
  delphi_keywords["then"] = 1;
  delphi_keywords["destructor"] = 1;
  delphi_keywords["if"] = 1;
  delphi_keywords["or"] = 1;
  delphi_keywords["threadvar"] = 1;
  delphi_keywords["dispinterface"] = 1;
  delphi_keywords["implementation"] = 1;
  delphi_keywords["out"] = 1;
  delphi_keywords["to"] = 1;
  delphi_keywords["div"] = 1;
  delphi_keywords["in"] = 1;
  delphi_keywords["packed"] = 1;
  delphi_keywords["try"] = 1;
  delphi_keywords["do"] = 1;
  delphi_keywords["inherited"] = 1;
  delphi_keywords["procedure"] = 1;
  delphi_keywords["type"] = 1;
  delphi_keywords["downto"] = 1;
  delphi_keywords["initialization"] = 1;
  delphi_keywords["program"] = 1;
  delphi_keywords["unit"] = 1;
  delphi_keywords["else"] = 1;
  delphi_keywords["inline"] = 1;
  delphi_keywords["property"] = 1;
  delphi_keywords["until"] = 1;
  delphi_keywords["private"] = 1;
  delphi_keywords["protected"] = 1;
  delphi_keywords["public"] = 1;
  delphi_keywords["published"] = 1;
  delphi_keywords["automated"] = 1;
  delphi_keywords["at"] = 1;
  delphi_keywords["on"] = 1;

  // reserved/predefined variables and types (lowercase!)
  delphi_keywords["result"] = 1;
  delphi_keywords["system"] = 1;
  delphi_keywords["sysutils"] = 1;
  delphi_keywords["thrift"] = 1;
  delphi_keywords["tbytes"] = 1;
  delphi_keywords["tobject"] = 1;
  delphi_keywords["tclass"] = 1;
  delphi_keywords["tinterfacedobject"] = 1;
  delphi_keywords["ansistring"] = 1;
  delphi_keywords["string"] = 1;
  delphi_keywords["boolean"] = 1;
  delphi_keywords["shortint"] = 1;
  delphi_keywords["smallint"] = 1;
  delphi_keywords["integer"] = 1;
  delphi_keywords["int64"] = 1;
  delphi_keywords["double"] = 1;

  delphi_reserved_method["create"] = 1;
  delphi_reserved_method["free"] = 1;
  delphi_reserved_method["initinstance"] = 1;
  delphi_reserved_method["cleanupinstance"] = 1;
  delphi_reserved_method["classtype"] = 1;
  delphi_reserved_method["classname"] = 1;
  delphi_reserved_method["classnameis"] = 1;
  delphi_reserved_method["classparent"] = 1;
  delphi_reserved_method["classinfo"] = 1;
  delphi_reserved_method["instancesize"] = 1;
  delphi_reserved_method["inheritsfrom"] = 1;
  delphi_reserved_method["methodaddress"] = 1;
  delphi_reserved_method["methodaddress"] = 1;
  delphi_reserved_method["methodname"] = 1;
  delphi_reserved_method["fieldaddress"] = 1;
  delphi_reserved_method["fieldaddress"] = 1;
  delphi_reserved_method["getinterface"] = 1;
  delphi_reserved_method["getinterfaceentry"] = 1;
  delphi_reserved_method["getinterfacetable"] = 1;
  delphi_reserved_method["unitname"] = 1;
  delphi_reserved_method["equals"] = 1;
  delphi_reserved_method["gethashcode"] = 1;
  delphi_reserved_method["tostring"] = 1;
  delphi_reserved_method["safecallexception"] = 1;
  delphi_reserved_method["afterconstruction"] = 1;
  delphi_reserved_method["beforedestruction"] = 1;
  delphi_reserved_method["dispatch"] = 1;
  delphi_reserved_method["defaulthandler"] = 1;
  delphi_reserved_method["newinstance"] = 1;
  delphi_reserved_method["freeinstance"] = 1;
  delphi_reserved_method["destroy"] = 1;
  delphi_reserved_method["read"] = 1;
  delphi_reserved_method["write"] = 1;

  delphi_reserved_method_exception["setinnerexception"] = 1;
  delphi_reserved_method_exception["setstackinfo"] = 1;
  delphi_reserved_method_exception["getstacktrace"] = 1;
  delphi_reserved_method_exception["raisingexception"] = 1;
  delphi_reserved_method_exception["createfmt"] = 1;
  delphi_reserved_method_exception["createres"] = 1;
  delphi_reserved_method_exception["createresfmt"] = 1;
  delphi_reserved_method_exception["createhelp"] = 1;
  delphi_reserved_method_exception["createfmthelp"] = 1;
  delphi_reserved_method_exception["createreshelp"] = 1;
  delphi_reserved_method_exception["createresfmthelp"] = 1;
  delphi_reserved_method_exception["getbaseexception"] = 1;
  delphi_reserved_method_exception["baseexception"] = 1;
  delphi_reserved_method_exception["helpcontext"] = 1;
  delphi_reserved_method_exception["innerexception"] = 1;
  delphi_reserved_method_exception["message"] = 1;
  delphi_reserved_method_exception["stacktrace"] = 1;
  delphi_reserved_method_exception["stackinfo"] = 1;
  delphi_reserved_method_exception["getexceptionstackinfoproc"] = 1;
  delphi_reserved_method_exception["getstackinfostringproc"] = 1;
  delphi_reserved_method_exception["cleanupstackinfoproc"] = 1;
  delphi_reserved_method_exception["raiseouterexception"] = 1;
  delphi_reserved_method_exception["throwouterexception"] = 1;
}

void t_delphi_generator::add_delphi_uses_list(string unitname) {
  vector<std::string>::const_iterator s_iter;
  bool found = false;
  for (s_iter = uses_list.begin(); s_iter != uses_list.end(); ++s_iter) {
    if ((*s_iter) == unitname) {
      found = true;
      break;
    }
  }
  if (!found) {
    uses_list.push_back(unitname);
  }
}

void t_delphi_generator::init_generator() {
  indent_impl_ = 0;
  namespace_name_ = program_->get_namespace("delphi");
  has_forward = false;
  has_enum = false;
  has_const = false;
  create_keywords();

  add_delphi_uses_list("Classes");
  add_delphi_uses_list("SysUtils");
  add_delphi_uses_list("Generics.Collections");
  if(async_) {
    add_delphi_uses_list("System.Threading");
  }

  add_delphi_uses_list("Thrift");
  add_delphi_uses_list("Thrift.Utils");
  add_delphi_uses_list("Thrift.Collections");
  add_delphi_uses_list("Thrift.Protocol");
  add_delphi_uses_list("Thrift.Transport");
  if (register_types_) {
    add_delphi_uses_list("Thrift.TypeRegistry");
  }

  init_known_types_list();

  string unitname, nsname;
  const vector<t_program*>& includes = program_->get_includes();
  for (size_t i = 0; i < includes.size(); ++i) {
    unitname = includes[i]->get_name();
    nsname = includes[i]->get_namespace("delphi");
    if ("" != nsname) {
      unitname = normalize_name(nsname);
    }
    add_delphi_uses_list(unitname);
  }

  MKDIR(get_out_dir().c_str());
}

void t_delphi_generator::close_generator() {
  std::string unitname = program_name_;
  if ("" != namespace_name_) {
    unitname = namespace_name_;
  }

  for (int i = 0; i < (int)unitname.size(); i++) {
    if (unitname[i] == ' ') {
      unitname.replace(i, 1, "_");
    }
  }

  unitname = normalize_name(unitname);
  
  std::string f_name = get_out_dir() + "/" + unitname + ".pas";
  std::ofstream f_all;

  f_all.open(f_name.c_str());

  f_all << autogen_comment() << endl;
  generate_delphi_doc(f_all, program_);
  f_all << "unit " << unitname << ";" << endl << endl;
  f_all << "interface" << endl << endl;
  f_all << "uses" << endl;

  indent_up();

  vector<std::string>::const_iterator s_iter;
  for (s_iter = uses_list.begin(); s_iter != uses_list.end(); ++s_iter) {
    if (s_iter != uses_list.begin()) {
      f_all << ",";
      f_all << endl;
    }
    indent(f_all) << *s_iter;
  }

  f_all << ";" << endl << endl;

  indent_down();

  string tmp_unit(unitname);
  for (int i = 0; i < (int)tmp_unit.size(); i++) {
    if (tmp_unit[i] == '.') {
      tmp_unit.replace(i, 1, "_");
    }
  }

  f_all << "const" << endl;
  indent_up();
  indent(f_all) << "c" << tmp_unit
                << "_Option_AnsiStr_Binary = " << (ansistr_binary_ ? "True" : "False") << ";"
                << endl;
  indent(f_all) << "c" << tmp_unit
                << "_Option_Register_Types = " << (register_types_ ? "True" : "False") << ";"
                << endl;
  indent(f_all) << "c" << tmp_unit
                << "_Option_ConstPrefix    = " << (constprefix_ ? "True" : "False") << ";" << endl;
  indent(f_all) << "c" << tmp_unit << "_Option_Events         = " << (events_ ? "True" : "False")
                << ";" << endl;
  indent(f_all) << "c" << tmp_unit << "_Option_XmlDoc         = " << (xmldoc_ ? "True" : "False")
                << ";" << endl;
  indent_down();

  f_all << endl;
  f_all << "type" << endl;
  if (has_forward) {
    f_all << s_forward_decr.str() << endl;
  }
  if (has_enum) {
    indent(f_all) << endl;
    indent(f_all) << "{$SCOPEDENUMS ON}" << endl << endl;
    f_all << s_enum.str();
    indent(f_all) << "{$SCOPEDENUMS OFF}" << endl << endl;
  }
  f_all << s_struct.str();
  f_all << s_service.str();
  f_all << s_const.str();
  f_all << "implementation" << endl << endl;
  f_all << s_struct_impl.str();
  f_all << s_service_impl.str();
  f_all << s_const_impl.str();

  if (register_types_) {
    f_all << endl;
    f_all << "// Type factory methods and registration" << endl;
    f_all << s_type_factory_funcs.str();
    f_all << "procedure RegisterTypeFactories;" << endl;
    f_all << "begin" << endl;
    f_all << s_type_factory_registration.str();
    f_all << "end;" << endl;
  }
  f_all << endl;

  string constants_class = make_constants_classname();

  f_all << "initialization" << endl;
  if (has_const) {
    f_all << "{$IF CompilerVersion < 21.0}  // D2010" << endl;
    f_all << "  " << constants_class.c_str() << "_Initialize;" << endl;
    f_all << "{$IFEND}" << endl;
  }
  if (register_types_) {
    f_all << "  RegisterTypeFactories;" << endl;
  }
  f_all << endl;

  f_all << "finalization" << endl;
  if (has_const) {
    f_all << "{$IF CompilerVersion < 21.0}  // D2010" << endl;
    f_all << "  " << constants_class.c_str() << "_Finalize;" << endl;
    f_all << "{$IFEND}" << endl;
  }
  f_all << endl << endl;

  f_all << "end." << endl;
  f_all.close();

  if (!typedefs_pending.empty()) {
    pwarning(0, "%d typedefs with unresolved type references left:\n", typedefs_pending.size());
    for (std::list<t_typedef*>::iterator iter = typedefs_pending.begin();
         typedefs_pending.end() != iter;
         ++iter) {
      pwarning(0, "- %s\n", (*iter)->get_symbolic().c_str());
    }
  }
}

void t_delphi_generator::delphi_type_usings(ostream& out) {
  indent_up();
  indent(out) << "Classes, SysUtils, Generics.Collections, Thrift.Collections, Thrift.Protocol,"
              << endl;
  indent(out) << "Thrift.Transport;" << endl << endl;
  indent_down();
}

void t_delphi_generator::generate_forward_declaration(t_struct* tstruct) {
  // Forward declare struct def
  has_forward = true;
  pverbose("forward declaration of %s\n", type_name(tstruct).c_str());

  string what = tstruct->is_xception() ? "class" : "interface";

  indent_up();
  indent(s_forward_decr) << type_name(tstruct, tstruct->is_xception(), true) << " = " << what << ";"
                         << endl;
  indent_down();

  add_defined_type(tstruct);
}

void t_delphi_generator::generate_typedef(t_typedef* ttypedef) {
  t_type* type = ttypedef->get_type();

  // write now or save for later?
  if (!is_fully_defined_type(type)) {
    pverbose("typedef %s: unresolved dependencies found\n", type_name(ttypedef).c_str());
    typedefs_pending.push_back(ttypedef);
    return;
  }

  indent_up();
  generate_delphi_doc(s_struct, ttypedef);
  indent(s_struct) << type_name(ttypedef) << " = ";

  // commented out: the benefit is not big enough to risk breaking existing code
  // bool container = type->is_list() || type->is_map() || type->is_set();
  // if( ! container)
  //  s_struct << "type ";  //the "type A = type B" syntax leads to E2574 with generics

  s_struct << type_name(ttypedef->get_type()) << ";" << endl << endl;
  indent_down();

  add_defined_type(ttypedef);
}

bool t_delphi_generator::is_fully_defined_type(t_type* ttype) {
  if ((NULL != ttype->get_program()) && (ttype->get_program() != program_)) {
    t_scope* scope = ttype->get_program()->scope();
    if (NULL != scope->get_type(ttype->get_name())) {
      // printf("type %s found in included scope %s\n", ttype->get_name().c_str(),
      // ttype->get_program()->get_name().c_str());
      return true;
    }
  }

  if (ttype->is_typedef()) {
    return (1 == types_known[type_name(ttype)]);
  }

  if (ttype->is_base_type()) {
    return (1 == types_known[base_type_name((t_base_type*)ttype)]);
  } else if (ttype->is_enum()) {
    return true; // enums are written first, before all other types
  } else if (ttype->is_map()) {
    t_map* tmap = (t_map*)ttype;
    return is_fully_defined_type(tmap->get_key_type())
           && is_fully_defined_type(tmap->get_val_type());
  } else if (ttype->is_set()) {
    t_set* tset = (t_set*)ttype;
    return is_fully_defined_type(tset->get_elem_type());
  } else if (ttype->is_list()) {
    t_list* tlist = (t_list*)ttype;
    return is_fully_defined_type(tlist->get_elem_type());
  }

  return (1 == types_known[type_name(ttype)]);
}

void t_delphi_generator::add_defined_type(t_type* ttype) {
  // mark as known type
  types_known[type_name(ttype)] = 1;

  // check all pending typedefs
  std::list<t_typedef*>::iterator iter;
  bool more = true;
  while (more && (!typedefs_pending.empty())) {
    more = false;

    for (iter = typedefs_pending.begin(); typedefs_pending.end() != iter; ++iter) {
      t_typedef* ttypedef = (*iter);
      if (is_fully_defined_type(ttypedef->get_type())) {
        pverbose("typedef %s: all pending references are now resolved\n",
                 type_name(ttypedef).c_str());
        typedefs_pending.erase(iter);
        generate_typedef(ttypedef);
        more = true;
        break;
      }
    }
  }
}

void t_delphi_generator::init_known_types_list() {
  // known base types
  types_known[type_name(g_type_string)] = 1;
  types_known[type_name(g_type_binary)] = 1;
  types_known[type_name(g_type_bool)] = 1;
  types_known[type_name(g_type_i8)] = 1;
  types_known[type_name(g_type_i16)] = 1;
  types_known[type_name(g_type_i32)] = 1;
  types_known[type_name(g_type_i64)] = 1;
  types_known[type_name(g_type_double)] = 1;
}

void t_delphi_generator::generate_enum(t_enum* tenum) {
  has_enum = true;
  indent_up();
  generate_delphi_doc(s_enum, tenum);
  indent(s_enum) << type_name(tenum, true, true) << " = "
                 << "(" << endl;
  indent_up();
  vector<t_enum_value*> constants = tenum->get_constants();
  if (constants.empty()) {
    indent(s_enum) << "dummy = 0  // empty enums are not allowed";
  } else {
    vector<t_enum_value*>::iterator c_iter;
    for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
      int value = (*c_iter)->get_value();
      if (c_iter != constants.begin()) {
        s_enum << ",";
        s_enum << endl;
      }
      generate_delphi_doc(s_enum, *c_iter);
      indent(s_enum) << normalize_name((*c_iter)->get_name()) << " = " << value;
    }
  }
  s_enum << endl;
  indent_down();
  indent(s_enum) << ");" << endl << endl;
  indent_down();
}

std::string t_delphi_generator::make_valid_delphi_identifier(std::string const& fromName) {
  std::string str = fromName;
  if (str.empty()) {
    return str;
  }

  // tests rely on this
  assert(('A' < 'Z') && ('a' < 'z') && ('0' < '9'));

  // if the first letter is a number, we add an additional underscore in front of it
  char c = str.at(0);
  if (('0' <= c) && (c <= '9')) {
    str = "_" + str;
  }

  // following chars: letter, number or underscore
  for (size_t i = 0; i < str.size(); ++i) {
    c = str.at(i);
    if ((('A' > c) || (c > 'Z')) && (('a' > c) || (c > 'z')) && (('0' > c) || (c > '9'))
        && ('_' != c)) {
      str.replace(i, 1, "_");
    }
  }

  return str;
}

std::string t_delphi_generator::make_constants_classname() {
  if (constprefix_) {
    return make_valid_delphi_identifier("T" + program_name_ + "Constants");
  } else {
    return "TConstants"; // compatibility
  }
}

void t_delphi_generator::generate_consts(std::vector<t_const*> consts) {
  if (consts.empty()) {
    return;
  }

  has_const = true;
  string constants_class = make_constants_classname();

  indent_up();
  indent(s_const) << constants_class.c_str() << " = class" << endl;
  indent(s_const) << "private" << endl;
  indent_up();
  vector<t_const*>::iterator c_iter;
  for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
    if (const_needs_var((*c_iter)->get_type())) {
      print_private_field(s_const,
                          normalize_name((*c_iter)->get_name()),
                          (*c_iter)->get_type(),
                          (*c_iter)->get_value());
    }
  }
  indent_down();
  indent(s_const) << "public" << endl;
  indent_up();
  for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
    generate_delphi_doc(s_const, *c_iter);
    print_const_prop(s_const,
                     normalize_name((*c_iter)->get_name()),
                     (*c_iter)->get_type(),
                     (*c_iter)->get_value());
  }
  indent(s_const) << "{$IF CompilerVersion >= 21.0}" << endl;
  indent(s_const) << "class constructor Create;" << endl;
  indent(s_const) << "class destructor Destroy;" << endl;
  indent(s_const) << "{$IFEND}" << endl;
  indent_down();
  indent(s_const) << "end;" << endl << endl;
  indent_down();

  std::ostringstream vars, code;

  indent_up_impl();
  for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
    initialize_field(vars,
                     code,
                     "F" + prop_name((*c_iter)->get_name()),
                     (*c_iter)->get_type(),
                     (*c_iter)->get_value());
  }
  indent_down_impl();

  indent_impl(s_const_impl) << "{$IF CompilerVersion >= 21.0}" << endl;
  indent_impl(s_const_impl) << "class constructor " << constants_class.c_str() << ".Create;"
                            << endl;

  if (!vars.str().empty()) {
    indent_impl(s_const_impl) << "var" << endl;
    s_const_impl << vars.str();
  }
  indent_impl(s_const_impl) << "begin" << endl;
  if (!code.str().empty()) {
    s_const_impl << code.str();
  }
  indent_impl(s_const_impl) << "end;" << endl << endl;
  indent_impl(s_const_impl) << "class destructor " << constants_class.c_str() << ".Destroy;"
                            << endl;
  indent_impl(s_const_impl) << "begin" << endl;
  indent_up_impl();
  for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
    if (const_needs_var((*c_iter)->get_type())) {
      finalize_field(s_const_impl,
                     normalize_name((*c_iter)->get_name()),
                     (*c_iter)->get_type(),
                     (*c_iter)->get_value());
    }
  }
  indent_impl(s_const_impl) << "inherited;" << endl;
  indent_down_impl();
  indent_impl(s_const_impl) << "end;" << endl;
  indent_impl(s_const_impl) << "{$ELSE}" << endl;

  vars.str("");
  code.str("");

  indent_up_impl();
  for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
    if (const_needs_var((*c_iter)->get_type())) {
      initialize_field(vars,
                       code,
                       constants_class + ".F" + prop_name((*c_iter)->get_name()),
                       (*c_iter)->get_type(),
                       (*c_iter)->get_value());
    }
  }
  indent_down_impl();

  indent_impl(s_const_impl) << "procedure " << constants_class.c_str() << "_Initialize;" << endl;
  if (!vars.str().empty()) {
    indent_impl(s_const_impl) << "var" << endl;
    s_const_impl << vars.str();
  }
  indent_impl(s_const_impl) << "begin" << endl;
  if (!code.str().empty()) {
    s_const_impl << code.str();
  }
  indent_impl(s_const_impl) << "end;" << endl << endl;

  indent_impl(s_const_impl) << "procedure " << constants_class.c_str() << "_Finalize;" << endl;
  indent_impl(s_const_impl) << "begin" << endl;
  indent_up_impl();
  for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
    finalize_field(s_const_impl,
                   normalize_name((*c_iter)->get_name()),
                   (*c_iter)->get_type(),
                   (*c_iter)->get_value(),
                   constants_class);
  }
  indent_down_impl();
  indent_impl(s_const_impl) << "end;" << endl;
  indent_impl(s_const_impl) << "{$IFEND}" << endl << endl;
}

void t_delphi_generator::print_const_def_value(std::ostream& vars,
                                               std::ostream& out,
                                               string name,
                                               t_type* type,
                                               t_const_value* value,
                                               string cls_nm) {

  string cls_prefix;

  if (cls_nm == "") {
    cls_prefix = "";
  } else {
    cls_prefix = cls_nm + ".";
  }

  if (type->is_struct() || type->is_xception()) {
    const vector<t_field*>& fields = ((t_struct*)type)->get_members();
    vector<t_field*>::const_iterator f_iter;
    const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
    map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
    for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
      t_type* field_type = NULL;
      for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
        if ((*f_iter)->get_name() == v_iter->first->get_string()) {
          field_type = (*f_iter)->get_type();
        }
      }
      if (field_type == NULL) {
        throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
      }
      string val = render_const_value(vars, out, name, field_type, v_iter->second);
      indent_impl(out) << cls_prefix << normalize_name(name) << "."
                       << prop_name(v_iter->first->get_string(), type->is_xception())
                       << " := " << val << ";" << endl;
    }
  } else if (type->is_map()) {
    t_type* ktype = ((t_map*)type)->get_key_type();
    t_type* vtype = ((t_map*)type)->get_val_type();
    const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
    map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
    for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
      string key = render_const_value(vars, out, name, ktype, v_iter->first);
      string val = render_const_value(vars, out, name, vtype, v_iter->second);
      indent_impl(out) << cls_prefix << normalize_name(name) << "[" << key << "]"
                       << " := " << val << ";" << endl;
    }
  } else if (type->is_list() || type->is_set()) {
    t_type* etype;
    if (type->is_list()) {
      etype = ((t_list*)type)->get_elem_type();
    } else {
      etype = ((t_set*)type)->get_elem_type();
    }

    const vector<t_const_value*>& val = value->get_list();
    vector<t_const_value*>::const_iterator v_iter;
    for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
      string val = render_const_value(vars, out, name, etype, *v_iter);
      indent_impl(out) << cls_prefix << normalize_name(name) << ".Add(" << val << ");" << endl;
    }
  }
}

void t_delphi_generator::print_private_field(std::ostream& out,
                                             string name,
                                             t_type* type,
                                             t_const_value* value) {
  (void)value;
  indent(out) << "class var F" << name << ": " << type_name(type) << ";" << endl;
}

bool t_delphi_generator::const_needs_var(t_type* type) {
  t_type* truetype = type;
  while (truetype->is_typedef()) {
    truetype = ((t_typedef*)truetype)->get_type();
  }
  return (!truetype->is_base_type());
}

void t_delphi_generator::print_const_prop(std::ostream& out,
                                          string name,
                                          t_type* type,
                                          t_const_value* value) {
  (void)value;
  if (const_needs_var(type)) {
    indent(out) << "class property " << name << ": " << type_name(type) << " read F" << name << ";"
                << endl;
  } else {
    std::ostringstream vars; // dummy
    string v2 = render_const_value(vars, out, name, type, value);
    indent(out) << "const " << name << " = " << v2 << ";" << endl;
  }
}

void t_delphi_generator::print_const_value(std::ostream& vars,
                                           std::ostream& out,
                                           string name,
                                           t_type* type,
                                           t_const_value* value) {
  t_type* truetype = type;
  while (truetype->is_typedef()) {
    truetype = ((t_typedef*)truetype)->get_type();
  }

  if (truetype->is_base_type()) {
    // already done
    // string v2 = render_const_value( vars, out, name, type, value);
    // indent_impl(out) << name << " := " << v2 << ";" << endl;
  } else if (truetype->is_enum()) {
    indent_impl(out) << name << " := " << type_name(type) << "." << value->get_identifier_name()
                     << ";" << endl;
  } else {
    string typname;
    typname = type_name(truetype, true, false, type->is_xception(), type->is_xception());
    indent_impl(out) << name << " := " << typname << ".Create;" << endl;
    print_const_def_value(vars, out, name, truetype, value);
  }
}

void t_delphi_generator::initialize_field(std::ostream& vars,
                                          std::ostream& out,
                                          string name,
                                          t_type* type,
                                          t_const_value* value) {
  print_const_value(vars, out, name, type, value);
}

void t_delphi_generator::finalize_field(std::ostream& out,
                                        string name,
                                        t_type* type,
                                        t_const_value* value,
                                        string cls_nm) {
  (void)out;
  (void)name;
  (void)type;
  (void)value;
  (void)cls_nm;
}

string t_delphi_generator::render_const_value(ostream& vars,
                                              ostream& out,
                                              string name,
                                              t_type* type,
                                              t_const_value* value) {
  (void)name;

  t_type* truetype = type;
  while (truetype->is_typedef()) {
    truetype = ((t_typedef*)truetype)->get_type();
  }

  std::ostringstream render;

  if (truetype->is_base_type()) {
    t_base_type::t_base tbase = ((t_base_type*)truetype)->get_base();
    switch (tbase) {
    case t_base_type::TYPE_STRING:
      render << "'" << get_escaped_string(value) << "'";
      break;
    case t_base_type::TYPE_BOOL:
      render << ((value->get_integer() > 0) ? "True" : "False");
      break;
    case t_base_type::TYPE_I8:
      render << "ShortInt( " << value->get_integer() << ")";
      break;
    case t_base_type::TYPE_I16:
      render << "SmallInt( " << value->get_integer() << ")";
      break;
    case t_base_type::TYPE_I32:
      render << "LongInt( " << value->get_integer() << ")";
      break;
    case t_base_type::TYPE_I64:
      render << "Int64( " << value->get_integer() << ")";
      break;
    case t_base_type::TYPE_DOUBLE:
      if (value->get_type() == t_const_value::CV_INTEGER) {
        render << value->get_integer() << ".0"; // make it a double constant by adding ".0"
      } else {
        render << value->get_double();
      }
      break;
    default:
      throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
    }
  } else if (truetype->is_enum()) {
    render << type_name(type, false) << "." << value->get_identifier_name();
  } else {
    string t = tmp("tmp");
    vars << "  " << t << " : " << type_name(type) << ";" << endl;
    print_const_value(vars, out, t, type, value);
    render << t;
  }

  return render.str();
}

void t_delphi_generator::generate_struct(t_struct* tstruct) {
  generate_delphi_struct(tstruct, false);
}

void t_delphi_generator::generate_xception(t_struct* txception) {
  generate_delphi_struct(txception, true);
}

void t_delphi_generator::generate_delphi_struct(t_struct* tstruct, bool is_exception) {
  indent_up();
  generate_delphi_struct_definition(s_struct, tstruct, is_exception);
  indent_down();

  add_defined_type(tstruct);

  generate_delphi_struct_impl(s_struct_impl, "", tstruct, is_exception);
  if (register_types_) {
    generate_delphi_struct_type_factory(s_type_factory_funcs, "", tstruct, is_exception);
    generate_delphi_struct_type_factory_registration(s_type_factory_registration,
                                                     "",
                                                     tstruct,
                                                     is_exception);
  }
}

void t_delphi_generator::generate_delphi_struct_impl(ostream& out,
                                                     string cls_prefix,
                                                     t_struct* tstruct,
                                                     bool is_exception,
                                                     bool is_result,
                                                     bool is_x_factory) {

  if (is_exception && (!is_x_factory)) {
    generate_delphi_struct_impl(out, cls_prefix, tstruct, is_exception, is_result, true);
  }

  string cls_nm;

  string exception_factory_name;

  if (is_exception) {
    exception_factory_name = normalize_clsnm(tstruct->get_name(), "", true) + "Factory";
  }

  if (is_exception) {
    cls_nm = type_name(tstruct, true, (!is_x_factory), is_x_factory, true);
  } else {
    cls_nm = type_name(tstruct, true, false);
  }

  std::ostringstream vars, code;

  const vector<t_field*>& members = tstruct->get_members();
  vector<t_field*>::const_iterator m_iter;

  indent_up_impl();
  for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
    t_type* t = (*m_iter)->get_type();
    while (t->is_typedef()) {
      t = ((t_typedef*)t)->get_type();
    }
    if ((*m_iter)->get_value() != NULL) {
      initialize_field(vars,
                       code,
                       "F" + prop_name((*m_iter)->get_name(), is_exception),
                       t,
                       (*m_iter)->get_value());
      if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
        indent_impl(code) << "F__isset_" << prop_name((*m_iter), is_exception) << " := True;"
                          << endl;
      }
    }
  }
  indent_down_impl();

  indent_impl(out) << "constructor " << cls_prefix << cls_nm << "."
                   << "Create;" << endl;

  if (!vars.str().empty()) {
    out << "var" << endl;
    out << vars.str();
  }

  indent_impl(out) << "begin" << endl;
  indent_up_impl();
  if (is_exception && (!is_x_factory)) {
    indent_impl(out) << "inherited Create('');" << endl;
    indent_impl(out) << "F" << exception_factory_name << " := T" << exception_factory_name
                     << "Impl.Create;" << endl;
  } else {
    indent_impl(out) << "inherited;" << endl;
  }

  if (!code.str().empty()) {
    out << code.str();
  }

  indent_down_impl();
  indent_impl(out) << "end;" << endl << endl;

  if ((members.size() > 0) && is_exception && (!is_x_factory)) {
    indent_impl(out) << "constructor " << cls_prefix << cls_nm << "."
                     << "Create(" << constructor_argument_list(tstruct, indent_impl()) << ");"
                     << endl;
    indent_impl(out) << "begin" << endl;
    indent_up_impl();
    indent_impl(out) << "Create;" << endl;
    for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
      string propname = prop_name((*m_iter)->get_name(), is_exception);
      string param_name = constructor_param_name((*m_iter)->get_name());
      indent_impl(out) << propname << " := " << param_name << ";" << endl;
    }
    indent_impl(out) << "UpdateMessageProperty;" << endl;
    indent_down_impl();
    indent_impl(out) << "end;" << endl << endl;
  }

  indent_impl(out) << "destructor " << cls_prefix << cls_nm << "."
                   << "Destroy;" << endl;
  indent_impl(out) << "begin" << endl;
  indent_up_impl();

  for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
    t_type* t = (*m_iter)->get_type();
    while (t->is_typedef()) {
      t = ((t_typedef*)t)->get_type();
    }
    finalize_field(out, prop_name(*m_iter, is_exception), t, (*m_iter)->get_value());
  }

  indent_impl(out) << "inherited;" << endl;
  indent_down_impl();
  indent_impl(out) << "end;" << endl << endl;

  if (tstruct->is_union()) {
    indent_impl(out) << "procedure " << cls_prefix << cls_nm << "."
                     << "ClearUnionValues;" << endl;
    indent_impl(out) << "begin" << endl;
    indent_up_impl();
    for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
      t_type* t = (*m_iter)->get_type();
      while (t->is_typedef()) {
        t = ((t_typedef*)t)->get_type();
      }

      generate_delphi_clear_union_value(out,
                                        cls_prefix,
                                        cls_nm,
                                        t,
                                        *m_iter,
                                        "F",
                                        is_exception,
                                        tstruct->is_union(),
                                        is_x_factory,
                                        exception_factory_name);
    }
    indent_down_impl();
    indent_impl(out) << "end;" << endl << endl;
  }

  for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
    t_type* t = (*m_iter)->get_type();
    while (t->is_typedef()) {
      t = ((t_typedef*)t)->get_type();
    }
    generate_delphi_property_reader_impl(out, cls_prefix, cls_nm, t, *m_iter, "F", is_exception);
    generate_delphi_property_writer_impl(out,
                                         cls_prefix,
                                         cls_nm,
                                         t,
                                         *m_iter,
                                         "F",
                                         is_exception,
                                         tstruct->is_union(),
                                         is_x_factory,
                                         exception_factory_name);
    if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
      generate_delphi_isset_reader_impl(out, cls_prefix, cls_nm, t, *m_iter, "F", is_exception);
    }
  }

  if ((!is_exception) || is_x_factory) {
    generate_delphi_struct_reader_impl(out, cls_prefix, tstruct, is_exception);
    if (is_result) {
      generate_delphi_struct_result_writer_impl(out, cls_prefix, tstruct, is_exception);
    } else {
      generate_delphi_struct_writer_impl(out, cls_prefix, tstruct, is_exception);
    }
  }
  generate_delphi_struct_tostring_impl(out, cls_prefix, tstruct, is_exception, is_x_factory);

  if (is_exception && is_x_factory) {
    generate_delphi_create_exception_impl(out, cls_prefix, tstruct, is_exception);
  }
}

void t_delphi_generator::print_delphi_struct_type_factory_func(ostream& out, t_struct* tstruct) {
  string struct_intf_name = type_name(tstruct);
  out << "Create_";
  out << struct_intf_name;
  out << "_Impl";
}

void t_delphi_generator::generate_delphi_struct_type_factory(ostream& out,
                                                             string cls_prefix,
                                                             t_struct* tstruct,
                                                             bool is_exception,
                                                             bool is_result,
                                                             bool is_x_factory) {
  (void)cls_prefix;
  if (is_exception)
    return;
  if (is_result)
    return;
  if (is_x_factory)
    return;

  string struct_intf_name = type_name(tstruct);
  string cls_nm = type_name(tstruct, true, false);

  out << "function ";
  print_delphi_struct_type_factory_func(out, tstruct);
  out << ": ";
  out << struct_intf_name;
  out << ";" << endl;
  out << "begin" << endl;
  indent_up();
  indent(out) << "Result := " << cls_nm << ".Create;" << endl;
  indent_down();
  out << "end;" << endl << endl;
}

void t_delphi_generator::generate_delphi_struct_type_factory_registration(ostream& out,
                                                                          string cls_prefix,
                                                                          t_struct* tstruct,
                                                                          bool is_exception,
                                                                          bool is_result,
                                                                          bool is_x_factory) {
  (void)cls_prefix;
  if (is_exception)
    return;
  if (is_result)
    return;
  if (is_x_factory)
    return;

  string struct_intf_name = type_name(tstruct);

  indent(out) << "  TypeRegistry.RegisterTypeFactory<" << struct_intf_name << ">(";
  print_delphi_struct_type_factory_func(out, tstruct);
  out << ");";
  out << endl;
}

void t_delphi_generator::generate_delphi_struct_definition(ostream& out,
                                                           t_struct* tstruct,
                                                           bool is_exception,
                                                           bool in_class,
                                                           bool is_result,
                                                           bool is_x_factory) {
  bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end());
  string struct_intf_name;
  string struct_name;
  string isset_name;
  const vector<t_field*>& members = tstruct->get_members();
  vector<t_field*>::const_iterator m_iter;

  string exception_factory_name = normalize_clsnm(tstruct->get_name(), "", true) + "Factory";

  if (is_exception) {
    struct_intf_name = type_name(tstruct, false, false, true);
  } else {
    struct_intf_name = type_name(tstruct);
  }

  if (is_exception) {
    struct_name = type_name(tstruct, true, (!is_x_factory), is_x_factory);
  } else {
    struct_name = type_name(tstruct, true);
  }

  if ((!is_exception) || is_x_factory) {

    generate_delphi_doc(out, tstruct);
    indent(out) << struct_intf_name << " = interface(IBase)" << endl;
    indent_up();

    for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
      generate_delphi_property_reader_definition(out, *m_iter, is_exception);
      generate_delphi_property_writer_definition(out, *m_iter, is_exception);
    }

    if (is_x_factory) {
      out << endl;
      indent(out) << "// Create Exception Object" << endl;
      indent(out) << "function CreateException: " << type_name(tstruct, true, true) << ";" << endl;
    }

    if (members.size() > 0) {
      out << endl;
      for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
        generate_property(out, *m_iter, true, is_exception);
      }
    }

    if (members.size() > 0) {
      out << endl;
      for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
        if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
          generate_delphi_isset_reader_definition(out, *m_iter, is_exception);
        }
      }
    }

    if (members.size() > 0) {
      out << endl;
      for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
        if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
          isset_name = "__isset_" + prop_name(*m_iter, is_exception);
          indent(out) << "property " << isset_name << ": System.Boolean read Get" << isset_name << ";"
                      << endl;
        }
      }
    }

    indent_down();
    indent(out) << "end;" << endl << endl;
  }

  generate_delphi_doc(out, tstruct);
  indent(out) << struct_name << " = ";
  if (is_final) {
    out << "sealed ";
  }
  out << "class(";
  if (is_exception && (!is_x_factory)) {
    out << "TException";
  } else {
    out << "TInterfacedObject, IBase, " << struct_intf_name;
  }
  out << ")" << endl;

  if (is_exception && (!is_x_factory)) {
    indent(out) << "public" << endl;
    indent_up();
    indent(out) << "type" << endl;
    indent_up();
    generate_delphi_struct_definition(out, tstruct, is_exception, in_class, is_result, true);
    indent_down();
    indent_down();
  }

  indent(out) << "private" << endl;
  indent_up();

  if (is_exception && (!is_x_factory)) {
    indent(out) << "F" << exception_factory_name << " :" << struct_intf_name << ";" << endl << endl;
  }

  for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
    indent(out) << declare_field(*m_iter, false, "F", is_exception) << endl;
  }

  if (members.size() > 0) {
    indent(out) << endl;
    for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
      if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
        isset_name = "F__isset_" + prop_name(*m_iter, is_exception);
        indent(out) << isset_name << ": System.Boolean;" << endl;
      }
    }
  }

  indent(out) << endl;

  for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
    generate_delphi_property_reader_definition(out, *m_iter, is_exception);
    generate_delphi_property_writer_definition(out, *m_iter, is_exception);
  }

  if (tstruct->is_union()) {
    out << endl;
    indent(out) << "// Clear values(for union's property setter)" << endl;
    indent(out) << "procedure ClearUnionValues;" << endl;
  }

  if (members.size() > 0) {
    out << endl;
    for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
      if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
        isset_name = "__isset_" + prop_name(*m_iter, is_exception);
        indent(out) << "function Get" << isset_name << ": System.Boolean;" << endl;
      }
    }
  }

  indent_down();

  indent(out) << "public" << endl;
  indent_up();

  if ((members.size() > 0) && is_exception && (!is_x_factory)) {
    indent(out) << "constructor Create; overload;" << endl;
    indent(out) << "constructor Create(" << constructor_argument_list(tstruct, indent())
                << "); overload;" << endl;
  } else {
    indent(out) << "constructor Create;" << endl;
  }

  indent(out) << "destructor Destroy; override;" << endl;

  out << endl;
  indent(out) << "function ToString: string; override;" << endl;

  if (is_exception && (!is_x_factory)) {
    out << endl;
    indent(out) << "// Exception Factory" << endl;
    indent(out) << "property " << exception_factory_name << ": " << struct_intf_name << " read F"
                << exception_factory_name << " write F" << exception_factory_name << ";" << endl;
  }

  if ((!is_exception) || is_x_factory) {
    out << endl;
    indent(out) << "// IBase" << endl;
    indent(out) << "procedure Read( const iprot: IProtocol);" << endl;
    indent(out) << "procedure Write( const oprot: IProtocol);" << endl;
  }

  if (is_exception && is_x_factory) {
    out << endl;
    indent(out) << "// Create Exception Object" << endl;
    indent(out) << "function CreateException: " << type_name(tstruct, true, true) << ";" << endl;
  }

  if (members.size() > 0) {
    out << endl;
    indent(out) << "// Properties" << endl;
    for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
      generate_property(out, *m_iter, true, is_exception);
    }
  }

  if (members.size() > 0) {
    out << endl;
    indent(out) << "// isset" << endl;
    for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
      if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
        isset_name = "__isset_" + prop_name(*m_iter, is_exception);
        indent(out) << "property " << isset_name << ": System.Boolean read Get" << isset_name << ";"
                    << endl;
      }
    }
  }

  indent_down();
  indent(out) << "end;" << endl << endl;
}

void t_delphi_generator::generate_service(t_service* tservice) {
  indent_up();
  generate_delphi_doc(s_service, tservice);
  indent(s_service) << normalize_clsnm(service_name_, "T") << " = class" << endl;
  indent(s_service) << "public" << endl;
  indent_up();
  indent(s_service) << "type" << endl;
  generate_service_interface(tservice);
  generate_service_client(tservice);
  generate_service_server(tservice);
  generate_service_helpers(tservice);
  indent_down();
  indent_down();
  indent(s_service) << "end;" << endl;
  indent(s_service) << endl;
  indent_down();
}

void t_delphi_generator::generate_service_interface(t_service* tservice) {
  generate_service_interface(tservice,false);
  if(async_) {
    generate_service_interface(tservice,true);
  }
}


void t_delphi_generator::generate_service_interface(t_service* tservice, bool for_async) {
  string extends = "";
  string extends_iface = "";
  string iface_name = for_async ? "IAsync" : "Iface";

  indent_up();

  generate_delphi_doc(s_service, tservice);
  if (tservice->get_extends() != NULL) {
    extends = type_name(tservice->get_extends(), true, true);
    extends_iface = extends + "." + iface_name;
    generate_delphi_doc(s_service, tservice);
    indent(s_service) << iface_name << " = interface(" << extends_iface << ")" << endl;
  } else {
    indent(s_service) << iface_name << " = interface" << endl;
  }

  indent_up();
  vector<t_function*> functions = tservice->get_functions();
  vector<t_function*>::iterator f_iter;
  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
    generate_delphi_doc(s_service, *f_iter);
    indent(s_service) << function_signature(*f_iter, for_async) << endl;
  }
  indent_down();
  indent(s_service) << "end;" << endl << endl;

  indent_down();
}

void t_delphi_generator::generate_service_helpers(t_service* tservice) {
  vector<t_function*> functions = tservice->get_functions();
  vector<t_function*>::iterator f_iter;

  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
    t_struct* ts = (*f_iter)->get_arglist();
    generate_delphi_struct_definition(s_service, ts, false, true);
    generate_delphi_struct_impl(s_service_impl,
                                normalize_clsnm(service_name_, "T") + ".",
                                ts,
                                false);
    generate_function_helpers(*f_iter);
  }
}

void t_delphi_generator::generate_service_client(t_service* tservice) {
  indent_up();
  string extends = "";
  string extends_client = "TInterfacedObject";
  string implements = async_ ? "Iface, IAsync" : "Iface";

  generate_delphi_doc(s_service, tservice);
  if (tservice->get_extends() != NULL) {
    extends = type_name(tservice->get_extends(), true, true);
    extends_client = extends + ".TClient";
  }
  indent(s_service) << "TClient = class( " << extends_client << ", " << implements << ")" << endl;

  indent(s_service) << "public" << endl;
  indent_up();

  indent(s_service) << "constructor Create( prot: IProtocol); overload;" << endl;

  indent_impl(s_service_impl) << "constructor " << normalize_clsnm(service_name_, "T")
                              << ".TClient.Create( prot: IProtocol);" << endl;
  indent_impl(s_service_impl) << "begin" << endl;
  indent_up_impl();
  indent_impl(s_service_impl) << "Create( prot, prot );" << endl;
  indent_down_impl();
  indent_impl(s_service_impl) << "end;" << endl << endl;

  indent(s_service)
      << "constructor Create( const iprot: IProtocol; const oprot: IProtocol); overload;" << endl;

  indent_impl(s_service_impl) << "constructor " << normalize_clsnm(service_name_, "T")
                              << ".TClient.Create( const iprot: IProtocol; const oprot: IProtocol);"
                              << endl;
  indent_impl(s_service_impl) << "begin" << endl;
  indent_up_impl();
  indent_impl(s_service_impl) << "inherited Create;" << endl;
  indent_impl(s_service_impl) << "iprot_ := iprot;" << endl;
  indent_impl(s_service_impl) << "oprot_ := oprot;" << endl;
  indent_down_impl();
  indent_impl(s_service_impl) << "end;" << endl << endl;

  indent_down();

  if (extends.empty()) {
    indent(s_service) << "protected" << endl;
    indent_up();
    indent(s_service) << "iprot_: IProtocol;" << endl;
    indent(s_service) << "oprot_: IProtocol;" << endl;
    indent(s_service) << "seqid_: System.Integer;" << endl;
    indent_down();

    indent(s_service) << "public" << endl;
    indent_up();
    indent(s_service) << "property InputProtocol: IProtocol read iprot_;" << endl;
    indent(s_service) << "property OutputProtocol: IProtocol read oprot_;" << endl;
    indent_down();
  }

  vector<t_function*> functions = tservice->get_functions();
  vector<t_function*>::const_iterator f_iter;

  indent(s_service) << "protected" << endl;
  indent_up();

  indent(s_service) << "// Iface" << endl;
  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
    string funname = (*f_iter)->get_name();
    generate_delphi_doc(s_service, *f_iter);
    indent(s_service) << function_signature(*f_iter, false) << endl;
  }

  if( async_) {
    indent(s_service) << endl;
    indent(s_service) << "// IAsync" << endl;
    for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
      string funname = (*f_iter)->get_name();
      generate_delphi_doc(s_service, *f_iter);
      indent(s_service) << function_signature(*f_iter, true) << endl;
    }
  }

  indent_down();

  indent(s_service) << "public" << endl;
  indent_up();

  string full_cls = normalize_clsnm(service_name_, "T") + ".TClient";

  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
    string funname = (*f_iter)->get_name();

    vector<t_field*>::const_iterator fld_iter;
    t_struct* arg_struct = (*f_iter)->get_arglist();
    const vector<t_field*>& fields = arg_struct->get_members();

    // one for sync only, two for async+sync
    int mode = async_ ? 1 : 0;
    while( mode >= 0) {
      bool for_async = (mode != 0);
      mode--;

      indent_impl(s_service_impl) << function_signature(*f_iter, for_async, full_cls) << endl;
      indent_impl(s_service_impl) << "begin" << endl;
      indent_up_impl();

      t_type* ttype = (*f_iter)->get_returntype();
      if( for_async) {
        if (is_void(ttype)) {
           // Delphi forces us to specify a type with IFuture<T>, so we use Integer=0 for void methods
          indent_impl(s_service_impl) << "result := TTask.Future<System.Integer>(function: System.Integer" << endl;
        } else {
          string rettype = type_name(ttype, false, true, false, true);
          indent_impl(s_service_impl) << "result := TTask.Future<" << rettype << ">(function: " << rettype << endl;
        }
        indent_impl(s_service_impl) << "begin" << endl;
        indent_up_impl();
      }

      indent_impl(s_service_impl) << "send_" << funname << "(";

      bool first = true;
      for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
        if (first) {
          first = false;
        } else {
          s_service_impl << ", ";
        }
        s_service_impl << normalize_name((*fld_iter)->get_name());
      }
      s_service_impl << ");" << endl;

      if (!(*f_iter)->is_oneway()) {
        s_service_impl << indent_impl();
        if (!(*f_iter)->get_returntype()->is_void()) {
          s_service_impl << "Result := ";
        }
        s_service_impl << "recv_" << funname << "();" << endl;
      }

      if( for_async) {
        if (is_void(ttype)) {
          indent_impl(s_service_impl) << "Result := 0;" << endl;  // no IFuture<void> in Delphi
        }
        indent_down_impl();
        indent_impl(s_service_impl) << "end);" << endl;
      }

      indent_down_impl();
      indent_impl(s_service_impl) << "end;" << endl << endl;
    }

    t_function send_function(g_type_void,
                             string("send_") + (*f_iter)->get_name(),
                             (*f_iter)->get_arglist());

    string argsname = (*f_iter)->get_name() + "_args";
    string args_clsnm = normalize_clsnm(argsname, "T");
    string args_intfnm = normalize_clsnm(argsname, "I");

    string argsvar = tmp("_args");
    string msgvar = tmp("_msg");

    indent(s_service) << function_signature(&send_function, false) << endl;
    indent_impl(s_service_impl) << function_signature(&send_function, false, full_cls) << endl;
    indent_impl(s_service_impl) << "var" << endl;
    indent_up_impl();
    indent_impl(s_service_impl) << argsvar << " : " << args_intfnm << ";" << endl;
    indent_impl(s_service_impl) << msgvar << " : Thrift.Protocol.TThriftMessage;" << endl;
    indent_down_impl();
    indent_impl(s_service_impl) << "begin" << endl;
    indent_up_impl();

    indent_impl(s_service_impl) << "seqid_ := seqid_ + 1;" << endl;
    indent_impl(s_service_impl) << "Thrift.Protocol.Init( " << msgvar << ", '" << funname
                                << "', " << ((*f_iter)->is_oneway() ? "TMessageType.Oneway"
                                                                    : "TMessageType.Call")
                                << ", seqid_);" << endl;

    indent_impl(s_service_impl) << "oprot_.WriteMessageBegin( " << msgvar << " );" << endl;
    indent_impl(s_service_impl) << argsvar << " := " << args_clsnm << "Impl.Create();" << endl;

    for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
      indent_impl(s_service_impl) << argsvar << "." << prop_name(*fld_iter)
                                  << " := " << normalize_name((*fld_iter)->get_name()) << ";"
                                  << endl;
    }
    indent_impl(s_service_impl) << argsvar << ".Write(oprot_);" << endl;
    for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
      indent_impl(s_service_impl) << argsvar << "." << prop_name(*fld_iter)
                                  << " := " << empty_value((*fld_iter)->get_type()) << ";" << endl;
    }

    indent_impl(s_service_impl) << "oprot_.WriteMessageEnd();" << endl;
    indent_impl(s_service_impl) << "oprot_.Transport.Flush();" << endl;

    indent_down_impl();
    indent_impl(s_service_impl) << "end;" << endl << endl;

    if (!(*f_iter)->is_oneway()) {
      string org_resultname = (*f_iter)->get_name() + "_result";
      string result_clsnm = normalize_clsnm(org_resultname, "T");
      string result_intfnm = normalize_clsnm(org_resultname, "I");

      t_struct noargs(program_);
      t_function recv_function((*f_iter)->get_returntype(),
                               string("recv_") + (*f_iter)->get_name(),
                               &noargs,
                               (*f_iter)->get_xceptions());

      t_struct* xs = (*f_iter)->get_xceptions();
      const std::vector<t_field*>& xceptions = xs->get_members();

      string exceptvar = tmp("_ex");
      string appexvar = tmp("_ax");
      string retvar = tmp("_ret");

      indent(s_service) << function_signature(&recv_function, false) << endl;
      indent_impl(s_service_impl) << function_signature(&recv_function, false, full_cls) << endl;
      indent_impl(s_service_impl) << "var" << endl;
      indent_up_impl();
      indent_impl(s_service_impl) << msgvar << " : Thrift.Protocol.TThriftMessage;" << endl;
      if (xceptions.size() > 0) {
        indent_impl(s_service_impl) << exceptvar << " : Exception;" << endl;
      }
      indent_impl(s_service_impl) << appexvar << " : TApplicationException;" << endl;
      indent_impl(s_service_impl) << retvar << " : " << result_intfnm << ";" << endl;

      indent_down_impl();
      indent_impl(s_service_impl) << "begin" << endl;
      indent_up_impl();
      indent_impl(s_service_impl) << msgvar << " := iprot_.ReadMessageBegin();" << endl;
      indent_impl(s_service_impl) << "if (" << msgvar << ".Type_ = TMessageType.Exception) then"
                                  << endl;
      indent_impl(s_service_impl) << "begin" << endl;
      indent_up_impl();
      indent_impl(s_service_impl) << appexvar << " := TApplicationException.Read(iprot_);" << endl;
      indent_impl(s_service_impl) << "iprot_.ReadMessageEnd();" << endl;
      indent_impl(s_service_impl) << "raise " << appexvar << ";" << endl;
      indent_down_impl();
      indent_impl(s_service_impl) << "end;" << endl;

      indent_impl(s_service_impl) << retvar << " := " << result_clsnm << "Impl.Create();" << endl;
      indent_impl(s_service_impl) << retvar << ".Read(iprot_);" << endl;
      indent_impl(s_service_impl) << "iprot_.ReadMessageEnd();" << endl;

      if (!(*f_iter)->get_returntype()->is_void()) {
        indent_impl(s_service_impl) << "if (" << retvar << ".__isset_success) then" << endl;
        indent_impl(s_service_impl) << "begin" << endl;
        indent_up_impl();
        indent_impl(s_service_impl) << "Result := " << retvar << ".Success;" << endl;
        t_type* type = (*f_iter)->get_returntype();
        if (type->is_struct() || type->is_xception() || type->is_map() || type->is_list()
            || type->is_set()) {
          indent_impl(s_service_impl) << retvar << ".Success := nil;" << endl;
        }
        indent_impl(s_service_impl) << "Exit;" << endl;
        indent_down_impl();
        indent_impl(s_service_impl) << "end;" << endl;
      }

      vector<t_field*>::const_iterator x_iter;
      for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
        indent_impl(s_service_impl) << "if (" << retvar << ".__isset_" << prop_name(*x_iter)
                                    << ") then" << endl;
        indent_impl(s_service_impl) << "begin" << endl;
        indent_up_impl();
        indent_impl(s_service_impl) << exceptvar << " := " << retvar << "." << prop_name(*x_iter)
                                    << ".CreateException;" << endl;
        indent_impl(s_service_impl) << "raise " << exceptvar << ";" << endl;
        indent_down_impl();
        indent_impl(s_service_impl) << "end;" << endl;
      }

      if (!(*f_iter)->get_returntype()->is_void()) {
        indent_impl(s_service_impl)
            << "raise TApplicationExceptionMissingResult.Create('"
            << (*f_iter)->get_name() << " failed: unknown result');" << endl;
      }

      indent_down_impl();
      indent_impl(s_service_impl) << "end;" << endl << endl;
    }
  }

  indent_down();
  indent(s_service) << "end;" << endl << endl;
}

void t_delphi_generator::generate_service_server(t_service* tservice) {
  vector<t_function*> functions = tservice->get_functions();
  vector<t_function*>::iterator f_iter;

  string extends = "";
  string extends_processor = "";

  string full_cls = normalize_clsnm(service_name_, "T") + ".TProcessorImpl";

  if (tservice->get_extends() != NULL) {
    extends = type_name(tservice->get_extends(), true, true);
    extends_processor = extends + ".TProcessorImpl";
    indent(s_service) << "TProcessorImpl = class(" << extends_processor << ", IProcessor)" << endl;
  } else {
    indent(s_service) << "TProcessorImpl = class( TInterfacedObject, IProcessor)" << endl;
  }

  indent(s_service) << "public" << endl;
  indent_up();
  indent(s_service) << "constructor Create( iface_: Iface );" << endl;
  indent(s_service) << "destructor Destroy; override;" << endl;
  indent_down();

  indent_impl(s_service_impl) << "constructor " << full_cls << ".Create( iface_: Iface );" << endl;
  indent_impl(s_service_impl) << "begin" << endl;
  indent_up_impl();
  if (tservice->get_extends() != NULL) {
    indent_impl(s_service_impl) << "inherited Create( iface_);" << endl;
  } else {
    indent_impl(s_service_impl) << "inherited Create;" << endl;
  }
  indent_impl(s_service_impl) << "Self.iface_ := iface_;" << endl;
  if (tservice->get_extends() != NULL) {
    indent_impl(s_service_impl) << "ASSERT( processMap_ <> nil);  // inherited" << endl;
  } else {
    indent_impl(s_service_impl)
        << "processMap_ := TThriftDictionaryImpl<string, TProcessFunction>.Create;" << endl;
  }

  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
    indent_impl(s_service_impl) << "processMap_.AddOrSetValue( '" << (*f_iter)->get_name() << "', "
                                << (*f_iter)->get_name() << "_Process);" << endl;
  }
  indent_down_impl();
  indent_impl(s_service_impl) << "end;" << endl << endl;

  indent_impl(s_service_impl) << "destructor " << full_cls << ".Destroy;" << endl;
  indent_impl(s_service_impl) << "begin" << endl;
  indent_up_impl();
  indent_impl(s_service_impl) << "inherited;" << endl;
  indent_down_impl();
  indent_impl(s_service_impl) << "end;" << endl << endl;

  indent(s_service) << "private" << endl;
  indent_up();
  indent(s_service) << "iface_: Iface;" << endl;
  indent_down();

  if (tservice->get_extends() == NULL) {
    indent(s_service) << "protected" << endl;
    indent_up();
    indent(s_service) << "type" << endl;
    indent_up();
    indent(s_service) << "TProcessFunction = reference to procedure( seqid: System.Integer; const iprot: "
                         "IProtocol; const oprot: IProtocol"
                      << (events_ ? "; const events : IRequestEvents" : "") << ");" << endl;
    indent_down();
    indent_down();
    indent(s_service) << "protected" << endl;
    indent_up();
    indent(s_service) << "processMap_: IThriftDictionary<string, TProcessFunction>;" << endl;
    indent_down();
  }

  indent(s_service) << "public" << endl;
  indent_up();
  if (extends.empty()) {
    indent(s_service) << "function Process( const iprot: IProtocol; const oprot: IProtocol; const "
                         "events : IProcessorEvents): System.Boolean;" << endl;
  } else {
    indent(s_service) << "function Process( const iprot: IProtocol; const oprot: IProtocol; const "
                         "events : IProcessorEvents): System.Boolean; reintroduce;" << endl;
  }

  indent_impl(s_service_impl) << "function " << full_cls << ".Process( const iprot: IProtocol; "
                                                            "const oprot: IProtocol; const events "
                                                            ": IProcessorEvents): System.Boolean;" << endl;
  ;
  indent_impl(s_service_impl) << "var" << endl;
  indent_up_impl();
  indent_impl(s_service_impl) << "msg : Thrift.Protocol.TThriftMessage;" << endl;
  indent_impl(s_service_impl) << "fn : TProcessFunction;" << endl;
  indent_impl(s_service_impl) << "x : TApplicationException;" << endl;
  if (events_) {
    indent_impl(s_service_impl) << "context : IRequestEvents;" << endl;
  }
  indent_down_impl();
  indent_impl(s_service_impl) << "begin" << endl;
  indent_up_impl();
  indent_impl(s_service_impl) << "try" << endl;
  indent_up_impl();
  indent_impl(s_service_impl) << "msg := iprot.ReadMessageBegin();" << endl;
  indent_impl(s_service_impl) << "fn := nil;" << endl;
  indent_impl(s_service_impl) << "if not processMap_.TryGetValue(msg.Name, fn)" << endl;
  indent_impl(s_service_impl) << "or not Assigned(fn) then" << endl;
  indent_impl(s_service_impl) << "begin" << endl;
  indent_up_impl();
  indent_impl(s_service_impl) << "TProtocolUtil.Skip(iprot, TType.Struct);" << endl;
  indent_impl(s_service_impl) << "iprot.ReadMessageEnd();" << endl;
  indent_impl(s_service_impl) << "x := "
                                 "TApplicationExceptionUnknownMethod.Create("
                                 "'Invalid method name: ''' + msg.Name + '''');" << endl;
  indent_impl(s_service_impl)
      << "Thrift.Protocol.Init( msg, msg.Name, TMessageType.Exception, msg.SeqID);"
      << endl;
  indent_impl(s_service_impl) << "oprot.WriteMessageBegin( msg);" << endl;
  indent_impl(s_service_impl) << "x.Write(oprot);" << endl;
  indent_impl(s_service_impl) << "oprot.WriteMessageEnd();" << endl;
  indent_impl(s_service_impl) << "oprot.Transport.Flush();" << endl;
  indent_impl(s_service_impl) << "Result := True;" << endl;
  indent_impl(s_service_impl) << "Exit;" << endl;
  indent_down_impl();
  indent_impl(s_service_impl) << "end;" << endl;
  if (events_) {
    indent_impl(s_service_impl) << "if events <> nil" << endl;
    indent_impl(s_service_impl) << "then context := events.CreateRequestContext(msg.Name)" << endl;
    indent_impl(s_service_impl) << "else context := nil;" << endl;
    indent_impl(s_service_impl) << "try" << endl;
    indent_up_impl();
    indent_impl(s_service_impl) << "fn(msg.SeqID, iprot, oprot, context);" << endl;
    indent_down_impl();
    indent_impl(s_service_impl) << "finally" << endl;
    indent_up_impl();
    indent_impl(s_service_impl) << "if context <> nil then begin" << endl;
    indent_up_impl();
    indent_impl(s_service_impl) << "context.CleanupContext;" << endl;
    indent_impl(s_service_impl) << "context := nil;" << endl;
    indent_down_impl();
    indent_impl(s_service_impl) << "end;" << endl;
    indent_down_impl();
    indent_impl(s_service_impl) << "end;" << endl;
  } else {
    indent_impl(s_service_impl) << "fn(msg.SeqID, iprot, oprot);" << endl;
  }
  indent_down_impl();
  indent_impl(s_service_impl) << "except" << endl;
  indent_up_impl();
  indent_impl(s_service_impl) << "on TTransportExceptionTimedOut do begin" << endl;
  indent_up_impl();
  indent_impl(s_service_impl) << "Result := True;" << endl;
  indent_impl(s_service_impl) << "Exit;" << endl;
  indent_down_impl();
  indent_impl(s_service_impl) << "end;" << endl;
  indent_impl(s_service_impl) << "else begin" << endl;
  indent_up_impl();
  indent_impl(s_service_impl) << "Result := False;" << endl;
  indent_impl(s_service_impl) << "Exit;" << endl;
  indent_down_impl();
  indent_impl(s_service_impl) << "end;" << endl;
  indent_down_impl();
  indent_impl(s_service_impl) << "end;" << endl;
  indent_impl(s_service_impl) << "Result := True;" << endl;
  indent_down_impl();
  indent_impl(s_service_impl) << "end;" << endl << endl;

  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
    generate_process_function(tservice, *f_iter);
  }

  indent_down();
  indent(s_service) << "end;" << endl << endl;
}

void t_delphi_generator::generate_function_helpers(t_function* tfunction) {
  if (tfunction->is_oneway()) {
    return;
  }

  t_struct result(program_, tfunction->get_name() + "_result");
  t_field success(tfunction->get_returntype(), "Success", 0);
  if (!tfunction->get_returntype()->is_void()) {
    result.append(&success);
  }

  t_struct* xs = tfunction->get_xceptions();
  const vector<t_field*>& fields = xs->get_members();
  vector<t_field*>::const_iterator f_iter;
  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
    result.append(*f_iter);
  }

  generate_delphi_struct_definition(s_service, &result, false, true, true);
  generate_delphi_struct_impl(s_service_impl,
                              normalize_clsnm(service_name_, "T") + ".",
                              &result,
                              false);
}

void t_delphi_generator::generate_process_function(t_service* tservice, t_function* tfunction) {
  (void)tservice;
  string funcname = tfunction->get_name();
  string full_cls = normalize_clsnm(service_name_, "T") + ".TProcessorImpl";

  string org_argsname = funcname + "_args";
  string args_clsnm = normalize_clsnm(org_argsname, "T");
  string args_intfnm = normalize_clsnm(org_argsname, "I");

  string org_resultname = funcname + "_result";
  string result_clsnm = normalize_clsnm(org_resultname, "T");
  string result_intfnm = normalize_clsnm(org_resultname, "I");

  indent(s_service) << "procedure " << funcname
                    << "_Process( seqid: System.Integer; const iprot: IProtocol; const oprot: IProtocol"
                    << (events_ ? "; const events : IRequestEvents" : "") << ");" << endl;

  if (tfunction->is_oneway()) {
    indent_impl(s_service_impl) << "// one way processor" << endl;
  } else {
    indent_impl(s_service_impl) << "// both way processor" << endl;
  }

  indent_impl(s_service_impl)
      << "procedure " << full_cls << "." << funcname
      << "_Process( seqid: System.Integer; const iprot: IProtocol; const oprot: IProtocol"
      << (events_ ? "; const events : IRequestEvents" : "") << ");" << endl;
  indent_impl(s_service_impl) << "var" << endl;
  indent_up_impl();
  indent_impl(s_service_impl) << "args: " << args_intfnm << ";" << endl;
  if (!tfunction->is_oneway()) {
    indent_impl(s_service_impl) << "msg: Thrift.Protocol.TThriftMessage;" << endl;
    indent_impl(s_service_impl) << "ret: " << result_intfnm << ";" << endl;
    indent_impl(s_service_impl) << "appx : TApplicationException;" << endl;
  }

  indent_down_impl();
  indent_impl(s_service_impl) << "begin" << endl;
  indent_up_impl();

  if (events_) {
    indent_impl(s_service_impl) << "if events <> nil then events.PreRead;" << endl;
  }
  indent_impl(s_service_impl) << "args := " << args_clsnm << "Impl.Create;" << endl;
  indent_impl(s_service_impl) << "args.Read(iprot);" << endl;
  indent_impl(s_service_impl) << "iprot.ReadMessageEnd();" << endl;
  if (events_) {
    indent_impl(s_service_impl) << "if events <> nil then events.PostRead;" << endl;
  }

  t_struct* xs = tfunction->get_xceptions();
  const std::vector<t_field*>& xceptions = xs->get_members();
  vector<t_field*>::const_iterator x_iter;

  if (!tfunction->is_oneway()) {
    indent_impl(s_service_impl) << "ret := " << result_clsnm << "Impl.Create;" << endl;
  }

  indent_impl(s_service_impl) << "try" << endl;
  indent_up_impl();

  t_struct* arg_struct = tfunction->get_arglist();
  const std::vector<t_field*>& fields = arg_struct->get_members();
  vector<t_field*>::const_iterator f_iter;

  s_service_impl << indent_impl();
  if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
    s_service_impl << "ret.Success := ";
  }
  s_service_impl << "iface_." << normalize_name(tfunction->get_name(), true) << "(";
  bool first = true;
  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
    if (first) {
      first = false;
    } else {
      s_service_impl << ", ";
    }
    s_service_impl << "args." << prop_name(*f_iter);
  }
  s_service_impl << ");" << endl;

  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
    indent_impl(s_service_impl) << "args." << prop_name(*f_iter)
                                << " := " << empty_value((*f_iter)->get_type()) << ";" << endl;
  }

  indent_down_impl();
  indent_impl(s_service_impl) << "except" << endl;
  indent_up_impl();

  for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
    indent_impl(s_service_impl) << "on E: " << type_name((*x_iter)->get_type(), true, true)
                                << " do begin" << endl;
    indent_up_impl();
    if (!tfunction->is_oneway()) {
      string factory_name = normalize_clsnm((*x_iter)->get_type()->get_name(), "", true)
                            + "Factory";
      indent_impl(s_service_impl) << "ret." << prop_name(*x_iter) << " := E." << factory_name << ";"
                                  << endl;
    }
    indent_down_impl();
    indent_impl(s_service_impl) << "end;" << endl;
  }

  indent_impl(s_service_impl) << "on E: Exception do begin" << endl;
  indent_up_impl();
  if(events_) {
    indent_impl(s_service_impl) << "if events <> nil then events.UnhandledError(E);" << endl;
  }
  if (!tfunction->is_oneway()) {
    indent_impl(s_service_impl) << "appx := TApplicationExceptionInternalError.Create(E.Message);"
                                << endl;
    indent_impl(s_service_impl) << "try" << endl;
    indent_up_impl();
    if(events_) {
      indent_impl(s_service_impl) << "if events <> nil then events.PreWrite;" << endl;
    }
    indent_impl(s_service_impl) << "Thrift.Protocol.Init( msg, '"
                                << tfunction->get_name() << "', TMessageType.Exception, seqid);"
                                << endl;
    indent_impl(s_service_impl) << "oprot.WriteMessageBegin( msg);" << endl;
    indent_impl(s_service_impl) << "appx.Write(oprot);" << endl;
    indent_impl(s_service_impl) << "oprot.WriteMessageEnd();" << endl;
    indent_impl(s_service_impl) << "oprot.Transport.Flush();" << endl;
    if(events_) {
      indent_impl(s_service_impl) << "if events <> nil then events.PostWrite;" << endl;
    }
    indent_impl(s_service_impl) << "Exit;" << endl;
    indent_down_impl();
    indent_impl(s_service_impl) << "finally" << endl;
    indent_up_impl();
    indent_impl(s_service_impl) << "appx.Free;" << endl;
    indent_down_impl();
    indent_impl(s_service_impl) << "end;" << endl;
  }
  indent_down_impl();
  indent_impl(s_service_impl) << "end;" << endl;

  indent_down_impl();
  indent_impl(s_service_impl) << "end;" << endl;

  if (!tfunction->is_oneway()) {
    if (events_) {
      indent_impl(s_service_impl) << "if events <> nil then events.PreWrite;" << endl;
    }
    indent_impl(s_service_impl) << "Thrift.Protocol.Init( msg, '"
                                << tfunction->get_name() << "', TMessageType.Reply, seqid); "
                                << endl;
    indent_impl(s_service_impl) << "oprot.WriteMessageBegin( msg); " << endl;
    indent_impl(s_service_impl) << "ret.Write(oprot);" << endl;
    indent_impl(s_service_impl) << "oprot.WriteMessageEnd();" << endl;
    indent_impl(s_service_impl) << "oprot.Transport.Flush();" << endl;
    if (events_) {
      indent_impl(s_service_impl) << "if events <> nil then events.PostWrite;" << endl;
    }
  } else if (events_) {
    indent_impl(s_service_impl) << "if events <> nil then events.OnewayComplete;" << endl;
  }

  indent_down_impl();
  indent_impl(s_service_impl) << "end;" << endl << endl;
}

void t_delphi_generator::generate_deserialize_field(ostream& out,
                                                    bool is_xception,
                                                    t_field* tfield,
                                                    string prefix,
                                                    ostream& local_vars) {
  t_type* type = tfield->get_type();
  while (type->is_typedef()) {
    type = ((t_typedef*)type)->get_type();
  }

  if (type->is_void()) {
    throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name();
  }

  string name = prefix + prop_name(tfield, is_xception);

  if (type->is_struct() || type->is_xception()) {
    generate_deserialize_struct(out, (t_struct*)type, name, "");
  } else if (type->is_container()) {
    generate_deserialize_container(out, is_xception, type, name, local_vars);
  } else if (type->is_base_type() || type->is_enum()) {
    indent_impl(out) << name << " := ";

    if (type->is_enum()) {
      out << type_name(type, false) << "(";
    }

    out << "iprot.";

    if (type->is_base_type()) {
      t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
      switch (tbase) {
      case t_base_type::TYPE_VOID:
        throw "compiler error: cannot serialize void field in a struct: " + name;
        break;
      case t_base_type::TYPE_STRING:
        if (type->is_binary()) {
          if (ansistr_binary_) {
            out << "ReadAnsiString();";
          } else {
            out << "ReadBinary();";
          }
        } else {
          out << "ReadString();";
        }
        break;
      case t_base_type::TYPE_BOOL:
        out << "ReadBool();";
        break;
      case t_base_type::TYPE_I8:
        out << "ReadByte();";
        break;
      case t_base_type::TYPE_I16:
        out << "ReadI16();";
        break;
      case t_base_type::TYPE_I32:
        out << "ReadI32();";
        break;
      case t_base_type::TYPE_I64:
        out << "ReadI64();";
        break;
      case t_base_type::TYPE_DOUBLE:
        out << "ReadDouble();";
        break;
      default:
        throw "compiler error: no Delphi name for base type " + t_base_type::t_base_name(tbase);
      }
    } else if (type->is_enum()) {
      out << "ReadI32()";
      out << ");";
    }
    out << endl;
  } else {
    printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
           tfield->get_name().c_str(),
           type_name(type).c_str());
  }
}

void t_delphi_generator::generate_deserialize_struct(ostream& out,
                                                     t_struct* tstruct,
                                                     string name,
                                                     string prefix) {
  string typ_name;

  if (tstruct->is_xception()) {
    typ_name = type_name(tstruct, true, false, true, true);
  } else {
    typ_name = type_name(tstruct, true, false);
  }

  indent_impl(out) << prefix << name << " := " << typ_name << ".Create;" << endl;
  indent_impl(out) << prefix << name << ".Read(iprot);" << endl;
}

void t_delphi_generator::generate_deserialize_container(ostream& out,
                                                        bool is_xception,
                                                        t_type* ttype,
                                                        string name,
                                                        std::ostream& local_vars) {

  string obj;
  string counter;
  string local_var;

  if (ttype->is_map()) {
    obj = tmp("_map");
  } else if (ttype->is_set()) {
    obj = tmp("_set");
  } else if (ttype->is_list()) {
    obj = tmp("_list");
  }

  if (ttype->is_map()) {
    local_var = obj + ": TThriftMap;";
  } else if (ttype->is_set()) {
    local_var = obj + ": TThriftSet;";
  } else if (ttype->is_list()) {
    local_var = obj + ": TThriftList;";
  }
  local_vars << "  " << local_var << endl;
  counter = tmp("_i");
  local_var = counter + ": System.Integer;";
  local_vars << "  " << local_var << endl;

  indent_impl(out) << name << " := " << type_name(ttype, true) << ".Create;" << endl;

  if (ttype->is_map()) {
    indent_impl(out) << obj << " := iprot.ReadMapBegin();" << endl;
  } else if (ttype->is_set()) {
    indent_impl(out) << obj << " := iprot.ReadSetBegin();" << endl;
  } else if (ttype->is_list()) {
    indent_impl(out) << obj << " := iprot.ReadListBegin();" << endl;
  }

  indent_impl(out) << "for " << counter << " := 0 to " << obj << ".Count - 1 do" << endl;
  indent_impl(out) << "begin" << endl;
  indent_up_impl();
  if (ttype->is_map()) {
    generate_deserialize_map_element(out, is_xception, (t_map*)ttype, name, local_vars);
  } else if (ttype->is_set()) {
    generate_deserialize_set_element(out, is_xception, (t_set*)ttype, name, local_vars);
  } else if (ttype->is_list()) {
    generate_deserialize_list_element(out, is_xception, (t_list*)ttype, name, local_vars);
  }
  indent_down_impl();
  indent_impl(out) << "end;" << endl;

  if (ttype->is_map()) {
    indent_impl(out) << "iprot.ReadMapEnd();" << endl;
  } else if (ttype->is_set()) {
    indent_impl(out) << "iprot.ReadSetEnd();" << endl;
  } else if (ttype->is_list()) {
    indent_impl(out) << "iprot.ReadListEnd();" << endl;
  }
}

void t_delphi_generator::generate_deserialize_map_element(ostream& out,
                                                          bool is_xception,
                                                          t_map* tmap,
                                                          string prefix,
                                                          ostream& local_vars) {

  string key = tmp("_key");
  string val = tmp("_val");
  string local_var;

  t_field fkey(tmap->get_key_type(), key);
  t_field fval(tmap->get_val_type(), val);

  local_vars << "  " << declare_field(&fkey) << endl;
  local_vars << "  " << declare_field(&fval) << endl;

  generate_deserialize_field(out, is_xception, &fkey, "", local_vars);
  generate_deserialize_field(out, is_xception, &fval, "", local_vars);

  indent_impl(out) << prefix << ".AddOrSetValue( " << key << ", " << val << ");" << endl;
}

void t_delphi_generator::generate_deserialize_set_element(ostream& out,
                                                          bool is_xception,
                                                          t_set* tset,
                                                          string prefix,
                                                          ostream& local_vars) {
  string elem = tmp("_elem");
  t_field felem(tset->get_elem_type(), elem);
  local_vars << "  " << declare_field(&felem) << endl;
  generate_deserialize_field(out, is_xception, &felem, "", local_vars);
  indent_impl(out) << prefix << ".Add(" << elem << ");" << endl;
}

void t_delphi_generator::generate_deserialize_list_element(ostream& out,
                                                           bool is_xception,
                                                           t_list* tlist,
                                                           string prefix,
                                                           ostream& local_vars) {
  string elem = tmp("_elem");
  t_field felem(tlist->get_elem_type(), elem);
  local_vars << "  " << declare_field(&felem) << endl;
  generate_deserialize_field(out, is_xception, &felem, "", local_vars);
  indent_impl(out) << prefix << ".Add(" << elem << ");" << endl;
}

void t_delphi_generator::generate_serialize_field(ostream& out,
                                                  bool is_xception,
                                                  t_field* tfield,
                                                  string prefix,
                                                  ostream& local_vars) {
  (void)local_vars;

  t_type* type = tfield->get_type();
  while (type->is_typedef()) {
    type = ((t_typedef*)type)->get_type();
  }

  string name = prefix + prop_name(tfield, is_xception);

  if (type->is_void()) {
    throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name;
  }

  if (type->is_struct() || type->is_xception()) {
    generate_serialize_struct(out, (t_struct*)type, name, local_vars);
  } else if (type->is_container()) {
    generate_serialize_container(out, is_xception, type, name, local_vars);
  } else if (type->is_base_type() || type->is_enum()) {

    indent_impl(out) << "oprot.";

    if (type->is_base_type()) {
      t_base_type::t_base tbase = ((t_base_type*)type)->get_base();

      switch (tbase) {
      case t_base_type::TYPE_VOID:
        throw "compiler error: cannot serialize void field in a struct: " + name;
        break;
      case t_base_type::TYPE_STRING:
        if (type->is_binary()) {
          if (ansistr_binary_) {
            out << "WriteAnsiString(";
          } else {
            out << "WriteBinary(";
          }
        } else {
          out << "WriteString(";
        }
        out << name << ");";
        break;
      case t_base_type::TYPE_BOOL:
        out << "WriteBool(" << name << ");";
        break;
      case t_base_type::TYPE_I8:
        out << "WriteByte(" << name << ");";
        break;
      case t_base_type::TYPE_I16:
        out << "WriteI16(" << name << ");";
        break;
      case t_base_type::TYPE_I32:
        out << "WriteI32(" << name << ");";
        break;
      case t_base_type::TYPE_I64:
        out << "WriteI64(" << name << ");";
        break;
      case t_base_type::TYPE_DOUBLE:
        out << "WriteDouble(" << name << ");";
        break;
      default:
        throw "compiler error: no Delphi name for base type " + t_base_type::t_base_name(tbase);
      }
    } else if (type->is_enum()) {
      out << "WriteI32(System.Integer(" << name << "));";
    }
    out << endl;
  } else {
    printf("DO NOT KNOW HOW TO SERIALIZE '%s%s' TYPE '%s'\n",
           prefix.c_str(),
           tfield->get_name().c_str(),
           type_name(type).c_str());
  }
}

void t_delphi_generator::generate_serialize_struct(ostream& out,
                                                   t_struct* tstruct,
                                                   string prefix,
                                                   ostream& local_vars) {
  (void)local_vars;
  (void)tstruct;
  out << indent_impl() << prefix << ".Write(oprot);" << endl;
}

void t_delphi_generator::generate_serialize_container(ostream& out,
                                                      bool is_xception,
                                                      t_type* ttype,
                                                      string prefix,
                                                      ostream& local_vars) {
  string obj;
  if (ttype->is_map()) {
    obj = tmp("map");
    local_vars << "  " << obj << " : TThriftMap;" << endl;
    indent_impl(out) << "Thrift.Protocol.Init( " << obj << ", "
                     << type_to_enum(((t_map*)ttype)->get_key_type()) << ", "
                     << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << prefix
                     << ".Count);" << endl;
    indent_impl(out) << "oprot.WriteMapBegin( " << obj << ");" << endl;
  } else if (ttype->is_set()) {
    obj = tmp("set_");
    local_vars << "  " << obj << " : TThriftSet;" << endl;
    indent_impl(out) << "Thrift.Protocol.Init( " << obj << ", "
                     << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << prefix
                     << ".Count);" << endl;
    indent_impl(out) << "oprot.WriteSetBegin( " << obj << ");" << endl;
  } else if (ttype->is_list()) {
    obj = tmp("list_");
    local_vars << "  " << obj << " : TThriftList;" << endl;
    indent_impl(out) << "Thrift.Protocol.Init( " << obj << ", "
                     << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix
                     << ".Count);" << endl;
    indent_impl(out) << "oprot.WriteListBegin( " << obj << ");" << endl;
  }

  string iter = tmp("_iter");
  if (ttype->is_map()) {
    local_vars << "  " << iter << ": " << type_name(((t_map*)ttype)->get_key_type()) << ";" << endl;
    indent_impl(out) << "for " << iter << " in " << prefix << ".Keys do" << endl;
    indent_impl(out) << "begin" << endl;
    indent_up_impl();
  } else if (ttype->is_set()) {
    local_vars << "  " << iter << ": " << type_name(((t_set*)ttype)->get_elem_type()) << ";"
               << endl;
    indent_impl(out) << "for " << iter << " in " << prefix << " do" << endl;
    indent_impl(out) << "begin" << endl;
    indent_up_impl();
  } else if (ttype->is_list()) {
    local_vars << "  " << iter << ": " << type_name(((t_list*)ttype)->get_elem_type()) << ";"
               << endl;
    indent_impl(out) << "for " << iter << " in " << prefix << " do" << endl;
    indent_impl(out) << "begin" << endl;
    indent_up_impl();
  }

  if (ttype->is_map()) {
    generate_serialize_map_element(out, is_xception, (t_map*)ttype, iter, prefix, local_vars);
  } else if (ttype->is_set()) {
    generate_serialize_set_element(out, is_xception, (t_set*)ttype, iter, local_vars);
  } else if (ttype->is_list()) {
    generate_serialize_list_element(out, is_xception, (t_list*)ttype, iter, local_vars);
  }

  indent_down_impl();
  indent_impl(out) << "end;" << endl;

  if (ttype->is_map()) {
    indent_impl(out) << "oprot.WriteMapEnd();" << endl;
  } else if (ttype->is_set()) {
    indent_impl(out) << "oprot.WriteSetEnd();" << endl;
  } else if (ttype->is_list()) {
    indent_impl(out) << "oprot.WriteListEnd();" << endl;
  }
}

void t_delphi_generator::generate_serialize_map_element(ostream& out,
                                                        bool is_xception,
                                                        t_map* tmap,
                                                        string iter,
                                                        string map,
                                                        ostream& local_vars) {
  t_field kfield(tmap->get_key_type(), iter);
  generate_serialize_field(out, is_xception, &kfield, "", local_vars);
  t_field vfield(tmap->get_val_type(), map + "[" + iter + "]");
  generate_serialize_field(out, is_xception, &vfield, "", local_vars);
}

void t_delphi_generator::generate_serialize_set_element(ostream& out,
                                                        bool is_xception,
                                                        t_set* tset,
                                                        string iter,
                                                        ostream& local_vars) {
  t_field efield(tset->get_elem_type(), iter);
  generate_serialize_field(out, is_xception, &efield, "", local_vars);
}

void t_delphi_generator::generate_serialize_list_element(ostream& out,
                                                         bool is_xception,
                                                         t_list* tlist,
                                                         string iter,
                                                         ostream& local_vars) {
  t_field efield(tlist->get_elem_type(), iter);
  generate_serialize_field(out, is_xception, &efield, "", local_vars);
}

void t_delphi_generator::generate_property(ostream& out,
                                           t_field* tfield,
                                           bool isPublic,
                                           bool is_xception) {
  generate_delphi_property(out, is_xception, tfield, isPublic, "Get");
}

void t_delphi_generator::generate_delphi_property(ostream& out,
                                                  bool struct_is_xception,
                                                  t_field* tfield,
                                                  bool isPublic,
                                                  std::string fieldPrefix) {
  (void)isPublic;

  t_type* ftype = tfield->get_type();
  bool is_xception = ftype->is_xception();
  generate_delphi_doc(out, tfield);
  indent(out) << "property " << prop_name(tfield, struct_is_xception) << ": "
              << type_name(ftype, false, true, is_xception, true) << " read "
              << fieldPrefix + prop_name(tfield, struct_is_xception) << " write Set"
              << prop_name(tfield, struct_is_xception) << ";" << endl;
}

std::string t_delphi_generator::prop_name(t_field* tfield, bool is_xception) {
  return prop_name(tfield->get_name(), is_xception);
}

std::string t_delphi_generator::prop_name(string name, bool is_xception) {
  string ret = name;
  ret[0] = toupper(ret[0]);
  return normalize_name(ret, true, is_xception);
}

std::string t_delphi_generator::constructor_param_name(string name) {
  string ret = name;
  ret[0] = toupper(ret[0]);
  ret = "A" + ret;
  return normalize_name(ret, false, false);
}

string t_delphi_generator::normalize_clsnm(string clsnm, string prefix, bool b_no_check_keyword) {
  if (clsnm.size() > 0) {
    clsnm[0] = toupper(clsnm[0]);
  }
  if (b_no_check_keyword) {
    return prefix + clsnm;
  } else {
    return normalize_name(prefix + clsnm);
  }
}

string t_delphi_generator::type_name(t_type* ttype,
                                     bool b_cls,
                                     bool b_no_postfix,
                                     bool b_exception_factory,
                                     bool b_full_exception_factory) {

  if (ttype->is_typedef()) {
    t_typedef* tdef = (t_typedef*)ttype;
    if (tdef->is_forward_typedef()) { // forward types according to THRIFT-2421
      if (tdef->get_type() != NULL) {
        return type_name(tdef->get_type(),
                         b_cls,
                         b_no_postfix,
                         b_exception_factory,
                         b_full_exception_factory);
      } else {
        throw "unresolved forward declaration: " + tdef->get_symbolic();
      }
    } else {
      return normalize_name("T" + tdef->get_symbolic());
    }
  }

  string typ_nm;

  string s_factory;

  if (ttype->is_base_type()) {
    return base_type_name((t_base_type*)ttype);
  } else if (ttype->is_enum()) {
    b_cls = true;
    b_no_postfix = true;
  } else if (ttype->is_map()) {
    t_map* tmap = (t_map*)ttype;
    if (b_cls) {
      typ_nm = "TThriftDictionaryImpl";
    } else {
      typ_nm = "IThriftDictionary";
    }
    return typ_nm + "<" + type_name(tmap->get_key_type()) + ", " + type_name(tmap->get_val_type())
           + ">";
  } else if (ttype->is_set()) {
    t_set* tset = (t_set*)ttype;
    if (b_cls) {
      typ_nm = "THashSetImpl";
    } else {
      typ_nm = "IHashSet";
    }
    return typ_nm + "<" + type_name(tset->get_elem_type()) + ">";
  } else if (ttype->is_list()) {
    t_list* tlist = (t_list*)ttype;
    if (b_cls) {
      typ_nm = "TThriftListImpl";
    } else {
      typ_nm = "IThriftList";
    }
    return typ_nm + "<" + type_name(tlist->get_elem_type()) + ">";
  }

  string type_prefix;

  if (b_cls) {
    type_prefix = "T";
  } else {
    type_prefix = "I";
  }

  string nm = normalize_clsnm(ttype->get_name(), type_prefix);

  if (b_exception_factory) {
    nm = nm + "Factory";
  }

  if (b_cls) {
    if (!b_no_postfix) {
      nm = nm + "Impl";
    }
  }

  if (b_exception_factory && b_full_exception_factory) {
    return type_name(ttype, true, true, false, false) + "." + nm;
  }

  return nm;
}

// returns "const " for some argument types
string t_delphi_generator::input_arg_prefix(t_type* ttype) {

  // base types
  if (ttype->is_base_type()) {
    switch (((t_base_type*)ttype)->get_base()) {

    // these should be const'ed for optimal performamce
    case t_base_type::TYPE_STRING: // refcounted pointer
    case t_base_type::TYPE_I64:    // larger than 32 bit
    case t_base_type::TYPE_DOUBLE: // larger than 32 bit
      return "const ";

    // all others don't need to be
    case t_base_type::TYPE_I8:
    case t_base_type::TYPE_I16:
    case t_base_type::TYPE_I32:
    case t_base_type::TYPE_BOOL:
    case t_base_type::TYPE_VOID:
      return "";

    // we better always report any unknown types
    default:
      throw "compiler error: no input_arg_prefix() for base type "
          + t_base_type::t_base_name(((t_base_type*)ttype)->get_base());
    }

    // enums
  } else if (ttype->is_enum()) {
    return ""; // usually <= 32 bit

    // containers
  } else if (ttype->is_map()) {
    return "const "; // refcounted pointer

  } else if (ttype->is_set()) {
    return "const "; // refcounted pointer

  } else if (ttype->is_list()) {
    return "const "; // refcounted pointer
  }

  // any other type, either TSomething or ISomething
  return "const "; // possibly refcounted pointer
}

string t_delphi_generator::base_type_name(t_base_type* tbase) {
  switch (tbase->get_base()) {
  case t_base_type::TYPE_VOID:
    // no "void" in Delphi language
    return "";
  case t_base_type::TYPE_STRING:
    if (tbase->is_binary()) {
      if (ansistr_binary_) {
        return "System.AnsiString";
      } else {
        return "SysUtils.TBytes";
      }
    } else {
      return "System.string";
    }
  case t_base_type::TYPE_BOOL:
    return "System.Boolean";
  case t_base_type::TYPE_I8:
    return "System.ShortInt";
  case t_base_type::TYPE_I16:
    return "System.SmallInt";
  case t_base_type::TYPE_I32:
    return "System.Integer";
  case t_base_type::TYPE_I64:
    return "System.Int64";
  case t_base_type::TYPE_DOUBLE:
    return "System.Double";
  default:
    throw "compiler error: no Delphi name for base type "
        + t_base_type::t_base_name(tbase->get_base());
  }
}

string t_delphi_generator::declare_field(t_field* tfield,
                                         bool init,
                                         std::string prefix,
                                         bool is_xception_class) {
  (void)init;

  t_type* ftype = tfield->get_type();
  bool is_xception = ftype->is_xception();

  string result = prefix + prop_name(tfield, is_xception_class) + ": "
                  + type_name(ftype, false, true, is_xception, true) + ";";
  return result;
}

string t_delphi_generator::function_signature(t_function* tfunction,
                                              bool for_async,
                                              std::string full_cls,
                                              bool is_xception) {
  t_type* ttype = tfunction->get_returntype();
  string prefix;
  if (full_cls == "") {
    prefix = "";
  } else {
    prefix = full_cls + ".";
  }

  if( for_async) {
    if (is_void(ttype)) {
      return "function " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "Async("
             + argument_list(tfunction->get_arglist()) + "): IFuture<Integer>;";  // no IFuture<void> in Delphi
    } else {
      return "function " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "Async("
             + argument_list(tfunction->get_arglist()) + "): IFuture<"
             + type_name(ttype, false, true, is_xception, true) + ">;";
    }
  } else {
    if (is_void(ttype)) {
      return "procedure " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "("
             + argument_list(tfunction->get_arglist()) + ");";
    } else {
      return "function " + prefix + normalize_name(tfunction->get_name(), true, is_xception) + "("
             + argument_list(tfunction->get_arglist()) + "): "
             + type_name(ttype, false, true, is_xception, true) + ";";
    }
  }
}

string t_delphi_generator::argument_list(t_struct* tstruct) {
  string result = "";
  const vector<t_field*>& fields = tstruct->get_members();
  vector<t_field*>::const_iterator f_iter;
  bool first = true;
  t_type* tt;

  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
    if (first) {
      first = false;
    } else {
      result += "; ";
    }

    tt = (*f_iter)->get_type();
    result += input_arg_prefix(tt); // const?
    result += normalize_name((*f_iter)->get_name()) + ": "
              + type_name(tt, false, true, tt->is_xception(), true);
  }
  return result;
}

string t_delphi_generator::constructor_argument_list(t_struct* tstruct, string current_indent) {
  ostringstream result;
  const vector<t_field*>& fields = tstruct->get_members();
  vector<t_field*>::const_iterator f_iter;
  bool first = true;
  t_type* tt;
  string line = "";
  string newline_indent = current_indent + "  ";

  bool firstline = true;

  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
    if (first) {
      first = false;
    } else {
      line += ";";
    }

    if (line.size() > 80) {
      if (firstline) {
        result << endl << newline_indent;
        firstline = false;
      }
      result << line << endl;
      line = newline_indent;
    } else if (line.size() > 0) {
      line += " ";
    }

    tt = (*f_iter)->get_type();
    line += input_arg_prefix(tt); // const?
    line += constructor_param_name((*f_iter)->get_name()) + ": "
            + type_name(tt, false, true, tt->is_xception(), true);
  }

  if (line.size() > 0) {
    result << line;
  }

  string result_str;

  if (firstline) {
    result_str = " " + result.str();
  } else {
    result_str = result.str();
  }

  return result_str;
}

string t_delphi_generator::type_to_enum(t_type* type) {
  while (type->is_typedef()) {
    type = ((t_typedef*)type)->get_type();
  }

  if (type->is_base_type()) {
    t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
    switch (tbase) {
    case t_base_type::TYPE_VOID:
      throw "NO T_VOID CONSTRUCT";
    case t_base_type::TYPE_STRING:
      return "TType.String_";
    case t_base_type::TYPE_BOOL:
      return "TType.Bool_";
    case t_base_type::TYPE_I8:
      return "TType.Byte_";
    case t_base_type::TYPE_I16:
      return "TType.I16";
    case t_base_type::TYPE_I32:
      return "TType.I32";
    case t_base_type::TYPE_I64:
      return "TType.I64";
    case t_base_type::TYPE_DOUBLE:
      return "TType.Double_";
    }
  } else if (type->is_enum()) {
    return "TType.I32";
  } else if (type->is_struct() || type->is_xception()) {
    return "TType.Struct";
  } else if (type->is_map()) {
    return "TType.Map";
  } else if (type->is_set()) {
    return "TType.Set_";
  } else if (type->is_list()) {
    return "TType.List";
  }

  throw "INVALID TYPE IN type_to_enum: " + type->get_name();
}

string t_delphi_generator::empty_value(t_type* type) {
  while (type->is_typedef()) {
    type = ((t_typedef*)type)->get_type();
  }

  if (type->is_base_type()) {
    t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
    switch (tbase) {
    case t_base_type::TYPE_VOID:
      return "0";
    case t_base_type::TYPE_STRING:
      if (type->is_binary()) {
        if (ansistr_binary_) {
          return "''";
        } else {
          return "nil";
        }
      } else {
        return "''";
      }
    case t_base_type::TYPE_BOOL:
      return "False";
    case t_base_type::TYPE_I8:
    case t_base_type::TYPE_I16:
    case t_base_type::TYPE_I32:
    case t_base_type::TYPE_I64:
      return "0";
    case t_base_type::TYPE_DOUBLE:
      return "0.0";
    }
  } else if (type->is_enum()) {
    return "T" + type->get_name() + "(0)";
  } else if (type->is_struct() || type->is_xception()) {
    return "nil";
  } else if (type->is_map()) {
    return "nil";
  } else if (type->is_set()) {
    return "nil";
  } else if (type->is_list()) {
    return "nil";
  }

  throw "INVALID TYPE IN type_to_enum: " + type->get_name();
}

void t_delphi_generator::generate_delphi_property_writer_definition(ostream& out,
                                                                    t_field* tfield,
                                                                    bool is_xception_class) {
  t_type* ftype = tfield->get_type();
  bool is_xception = ftype->is_xception();

  indent(out) << "procedure Set" << prop_name(tfield, is_xception_class)
              << "( const Value: " << type_name(ftype, false, true, is_xception, true) << ");"
              << endl;
}

void t_delphi_generator::generate_delphi_property_reader_definition(ostream& out,
                                                                    t_field* tfield,
                                                                    bool is_xception_class) {
  t_type* ftype = tfield->get_type();
  bool is_xception = ftype->is_xception();

  indent(out) << "function Get" << prop_name(tfield, is_xception_class) << ": "
              << type_name(ftype, false, true, is_xception, true) << ";" << endl;
}

void t_delphi_generator::generate_delphi_isset_reader_definition(ostream& out,
                                                                 t_field* tfield,
                                                                 bool is_xception) {
  indent(out) << "function Get__isset_" << prop_name(tfield, is_xception) << ": System.Boolean;" << endl;
}

void t_delphi_generator::generate_delphi_clear_union_value(ostream& out,
                                                           std::string cls_prefix,
                                                           std::string name,
                                                           t_type* type,
                                                           t_field* tfield,
                                                           std::string fieldPrefix,
                                                           bool is_xception_class,
                                                           bool is_union,
                                                           bool is_xception_factory,
                                                           std::string xception_factory_name) {
  (void)cls_prefix;
  (void)name;
  (void)type;
  (void)is_union;
  (void)is_xception_factory;
  (void)xception_factory_name;

  t_type* ftype = tfield->get_type();
  bool is_xception = ftype->is_xception();

  indent_impl(out) << "if F__isset_" << prop_name(tfield, is_xception_class) << " then begin"
                   << endl;
  indent_up_impl();
  indent_impl(out) << "F__isset_" << prop_name(tfield, is_xception_class) << " := False;" << endl;
  indent_impl(out) << fieldPrefix << prop_name(tfield, is_xception_class) << " := "
                   << "Default( " << type_name(ftype, false, true, is_xception, true) << ");"
                   << endl;
  indent_down_impl();
  indent_impl(out) << "end;" << endl;
}

void t_delphi_generator::generate_delphi_property_writer_impl(ostream& out,
                                                              std::string cls_prefix,
                                                              std::string name,
                                                              t_type* type,
                                                              t_field* tfield,
                                                              std::string fieldPrefix,
                                                              bool is_xception_class,
                                                              bool is_union,
                                                              bool is_xception_factory,
                                                              std::string xception_factroy_name) {
  (void)type;

  t_type* ftype = tfield->get_type();
  bool is_xception = ftype->is_xception();

  indent_impl(out) << "procedure " << cls_prefix << name << "."
                   << "Set" << prop_name(tfield, is_xception_class)
                   << "( const Value: " << type_name(ftype, false, true, is_xception, true) << ");"
                   << endl;
  indent_impl(out) << "begin" << endl;
  indent_up_impl();
  if (is_union) {
    indent_impl(out) << "ClearUnionValues;" << endl;
  }
  if (tfield->get_req() != t_field::T_REQUIRED) {
    indent_impl(out) << "F__isset_" << prop_name(tfield, is_xception_class) << " := True;" << endl;
  }
  indent_impl(out) << fieldPrefix << prop_name(tfield, is_xception_class) << " := Value;" << endl;

  if (is_xception_class && (!is_xception_factory)) {
    indent_impl(out) << "F" << xception_factroy_name << "." << prop_name(tfield, is_xception_class)
                     << " := Value;" << endl;
  }

  indent_down_impl();
  indent_impl(out) << "end;" << endl << endl;
}

void t_delphi_generator::generate_delphi_property_reader_impl(ostream& out,
                                                              std::string cls_prefix,
                                                              std::string name,
                                                              t_type* type,
                                                              t_field* tfield,
                                                              std::string fieldPrefix,
                                                              bool is_xception_class) {
  (void)type;

  t_type* ftype = tfield->get_type();
  bool is_xception = ftype->is_xception();

  indent_impl(out) << "function " << cls_prefix << name << "."
                   << "Get" << prop_name(tfield, is_xception_class) << ": "
                   << type_name(ftype, false, true, is_xception, true) << ";" << endl;
  indent_impl(out) << "begin" << endl;
  indent_up_impl();
  indent_impl(out) << "Result := " << fieldPrefix << prop_name(tfield, is_xception_class) << ";"
                   << endl;
  indent_down_impl();
  indent_impl(out) << "end;" << endl << endl;
}

void t_delphi_generator::generate_delphi_isset_reader_impl(ostream& out,
                                                           std::string cls_prefix,
                                                           std::string name,
                                                           t_type* type,
                                                           t_field* tfield,
                                                           std::string fieldPrefix,
                                                           bool is_xception) {
  (void)type;

  string isset_name = "__isset_" + prop_name(tfield, is_xception);
  indent_impl(out) << "function " << cls_prefix << name << "."
                   << "Get" << isset_name << ": System.Boolean;" << endl;
  indent_impl(out) << "begin" << endl;
  indent_up_impl();
  indent_impl(out) << "Result := " << fieldPrefix << isset_name << ";" << endl;
  indent_down_impl();
  indent_impl(out) << "end;" << endl << endl;
}

void t_delphi_generator::generate_delphi_create_exception_impl(ostream& out,
                                                               string cls_prefix,
                                                               t_struct* tstruct,
                                                               bool is_exception) {
  (void)cls_prefix;

  string exception_cls_nm = type_name(tstruct, true, true);
  string cls_nm = type_name(tstruct, true, false, is_exception, is_exception);

  indent_impl(out) << "function " << cls_nm << ".CreateException: " << exception_cls_nm << ";"
                   << endl;

  indent_impl(out) << "begin" << endl;
  indent_up_impl();

  indent_impl(out) << "Result := " << exception_cls_nm << ".Create;" << endl;
  string factory_name = normalize_clsnm(tstruct->get_name(), "", true) + "Factory";
  indent_impl(out) << "Result." << factory_name << " := Self;" << endl;

  const vector<t_field*>& fields = tstruct->get_members();
  vector<t_field*>::const_iterator f_iter;

  string propname;

  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
    propname = prop_name(*f_iter, is_exception);
    if ((*f_iter)->get_req() != t_field::T_REQUIRED) {
      indent_impl(out) << "if __isset_" << propname << " then" << endl;
      indent_impl(out) << "begin" << endl;
      indent_up_impl();
    }
    indent_impl(out) << "Result." << propname << " := " << propname << ";" << endl;
    if ((*f_iter)->get_req() != t_field::T_REQUIRED) {
      indent_down_impl();
      indent_impl(out) << "end;" << endl;
    }
  }

  indent_impl(out) << "Result.UpdateMessageProperty;" << endl;

  indent_down_impl();
  indent_impl(out) << "end;" << endl << endl;
}

void t_delphi_generator::generate_delphi_struct_reader_impl(ostream& out,
                                                            string cls_prefix,
                                                            t_struct* tstruct,
                                                            bool is_exception) {

  ostringstream local_vars;
  ostringstream code_block;

  const vector<t_field*>& fields = tstruct->get_members();
  vector<t_field*>::const_iterator f_iter;

  indent_impl(code_block) << "begin" << endl;
  indent_up_impl();

  indent_impl(local_vars) << "tracker : IProtocolRecursionTracker;" << endl;
  indent_impl(code_block) << "tracker := iprot.NextRecursionLevel;" << endl;

  // local bools for required fields
  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
    if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
      indent_impl(local_vars) << "_req_isset_" << prop_name(*f_iter, is_exception) << " : System.Boolean;"
                              << endl;
      indent_impl(code_block) << "_req_isset_" << prop_name(*f_iter, is_exception) << " := FALSE;"
                              << endl;
    }
  }

  indent_impl(code_block) << "struc := iprot.ReadStructBegin;" << endl;

  indent_impl(code_block) << "try" << endl;
  indent_up_impl();

  indent_impl(code_block) << "while (true) do" << endl;
  indent_impl(code_block) << "begin" << endl;
  indent_up_impl();

  indent_impl(code_block) << "field_ := iprot.ReadFieldBegin();" << endl;

  indent_impl(code_block) << "if (field_.Type_ = TType.Stop) then" << endl;
  indent_impl(code_block) << "begin" << endl;
  indent_up_impl();
  indent_impl(code_block) << "Break;" << endl;
  indent_down_impl();
  indent_impl(code_block) << "end;" << endl;

  bool first = true;

  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {

    if (first) {
      indent_impl(code_block) << "case field_.ID of" << endl;
      indent_up_impl();
    }

    first = false;
    if (f_iter != fields.begin()) {
      code_block << ";" << endl;
    }
    indent_impl(code_block) << (*f_iter)->get_key() << ": begin" << endl;
    indent_up_impl();
    indent_impl(code_block) << "if (field_.Type_ = " << type_to_enum((*f_iter)->get_type())
                            << ") then begin" << endl;
    indent_up_impl();

    generate_deserialize_field(code_block, is_exception, *f_iter, "Self.", local_vars);

    // required field?
    if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
      indent_impl(code_block) << "_req_isset_" << prop_name(*f_iter, is_exception) << " := TRUE;"
                              << endl;
    }

    indent_down_impl();

    indent_impl(code_block) << "end else begin" << endl;
    indent_up_impl();
    indent_impl(code_block) << "TProtocolUtil.Skip(iprot, field_.Type_);" << endl;
    indent_down_impl();
    indent_impl(code_block) << "end;" << endl;
    indent_down_impl();
    indent_impl(code_block) << "end";
  }

  if (!first) {
    code_block << endl;
    indent_impl(code_block) << "else begin" << endl;
    indent_up_impl();
  }

  indent_impl(code_block) << "TProtocolUtil.Skip(iprot, field_.Type_);" << endl;

  if (!first) {
    indent_down_impl();
    indent_impl(code_block) << "end;" << endl;
    indent_down_impl();
    indent_impl(code_block) << "end;" << endl;
  }

  indent_impl(code_block) << "iprot.ReadFieldEnd;" << endl;

  indent_down_impl();

  indent_impl(code_block) << "end;" << endl;
  indent_down_impl();

  indent_impl(code_block) << "finally" << endl;
  indent_up_impl();
  indent_impl(code_block) << "iprot.ReadStructEnd;" << endl;
  indent_down_impl();
  indent_impl(code_block) << "end;" << endl;

  // all required fields have been read?
  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
    if ((*f_iter)->get_req() == t_field::T_REQUIRED) {
      indent_impl(code_block) << "if not _req_isset_" << prop_name(*f_iter, is_exception) << endl;
      indent_impl(code_block)
          << "then raise TProtocolExceptionInvalidData.Create("
          << "'required field " << prop_name(*f_iter, is_exception) << " not set');"
          << endl;
    }
  }

  indent_down_impl();
  indent_impl(code_block) << "end;" << endl << endl;

  string cls_nm;

  cls_nm = type_name(tstruct, true, false, is_exception, is_exception);

  indent_impl(out) << "procedure " << cls_prefix << cls_nm << ".Read( const iprot: IProtocol);"
                   << endl;
  indent_impl(out) << "var" << endl;
  indent_up_impl();
  indent_impl(out) << "field_ : TThriftField;" << endl;
  indent_impl(out) << "struc : TThriftStruct;" << endl;
  indent_down_impl();
  out << local_vars.str() << endl;
  out << code_block.str();
}

void t_delphi_generator::generate_delphi_struct_result_writer_impl(ostream& out,
                                                                   string cls_prefix,
                                                                   t_struct* tstruct,
                                                                   bool is_exception) {

  ostringstream local_vars;
  ostringstream code_block;

  string name = tstruct->get_name();
  const vector<t_field*>& fields = tstruct->get_sorted_members();
  vector<t_field*>::const_iterator f_iter;

  indent_impl(code_block) << "begin" << endl;
  indent_up_impl();

  indent_impl(local_vars) << "tracker : IProtocolRecursionTracker;" << endl;
  indent_impl(code_block) << "tracker := oprot.NextRecursionLevel;" << endl;

  indent_impl(code_block) << "Thrift.Protocol.Init( struc, '" << name << "');" << endl;
  indent_impl(code_block) << "oprot.WriteStructBegin(struc);" << endl;

  if (fields.size() > 0) {
    indent_impl(code_block) << "Thrift.Protocol.Init( field_);" << endl;
    for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
      indent_impl(code_block) << "if (__isset_" << prop_name(*f_iter, is_exception) << ") then"
                              << endl;
      indent_impl(code_block) << "begin" << endl;
      indent_up_impl();
      indent_impl(code_block) << "field_.Name := '" << (*f_iter)->get_name() << "';" << endl;
      indent_impl(code_block) << "field_.Type_  := " << type_to_enum((*f_iter)->get_type()) << ";"
                              << endl;
      indent_impl(code_block) << "field_.ID := " << (*f_iter)->get_key() << ";" << endl;
      indent_impl(code_block) << "oprot.WriteFieldBegin(field_);" << endl;
      generate_serialize_field(code_block, is_exception, *f_iter, "Self.", local_vars);
      indent_impl(code_block) << "oprot.WriteFieldEnd();" << endl;
      indent_down_impl();
    }
  }

  indent_impl(code_block) << "oprot.WriteFieldStop();" << endl;
  indent_impl(code_block) << "oprot.WriteStructEnd();" << endl;

  indent_down_impl();
  indent_impl(code_block) << "end;" << endl << endl;

  string cls_nm;

  cls_nm = type_name(tstruct, true, false, is_exception, is_exception);

  indent_impl(out) << "procedure " << cls_prefix << cls_nm << ".Write( const oprot: IProtocol);"
                   << endl;
  indent_impl(out) << "var" << endl;
  indent_up_impl();
  indent_impl(out) << "struc : TThriftStruct;" << endl;

  if (fields.size() > 0) {
    indent_impl(out) << "field_ : TThriftField;" << endl;
  }

  out << local_vars.str();
  indent_down_impl();
  out << code_block.str();
}

void t_delphi_generator::generate_delphi_struct_writer_impl(ostream& out,
                                                            string cls_prefix,
                                                            t_struct* tstruct,
                                                            bool is_exception) {

  ostringstream local_vars;
  ostringstream code_block;

  string name = tstruct->get_name();
  const vector<t_field*>& fields = tstruct->get_sorted_members();
  vector<t_field*>::const_iterator f_iter;

  indent_impl(code_block) << "begin" << endl;
  indent_up_impl();

  indent_impl(local_vars) << "tracker : IProtocolRecursionTracker;" << endl;
  indent_impl(code_block) << "tracker := oprot.NextRecursionLevel;" << endl;

  indent_impl(code_block) << "Thrift.Protocol.Init( struc, '" << name << "');" << endl;
  indent_impl(code_block) << "oprot.WriteStructBegin(struc);" << endl;

  if (fields.size() > 0) {
    indent_impl(code_block) << "Thrift.Protocol.Init( field_);" << endl;
  }

  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
    string fieldname = prop_name((*f_iter), is_exception);
    bool null_allowed = type_can_be_null((*f_iter)->get_type());
    bool is_required = ((*f_iter)->get_req() == t_field::T_REQUIRED);
    bool has_isset = (!is_required);
    if (is_required && null_allowed) {
      null_allowed = false;
      indent_impl(code_block) << "if (Self." << fieldname << " = nil)" << endl;
      indent_impl(code_block) << "then raise TProtocolExceptionInvalidData.Create("
                              << "'required field " << fieldname << " not set');"
                              << endl;
    }
    if (null_allowed) {
      indent_impl(code_block) << "if (Self." << fieldname << " <> nil)";
      if (has_isset) {
        code_block << " and __isset_" << fieldname;
      }
      code_block << " then begin" << endl;
      indent_up_impl();
    } else {
      if (has_isset) {
        indent_impl(code_block) << "if (__isset_" << fieldname << ") then begin" << endl;
        indent_up_impl();
      }
    }
    indent_impl(code_block) << "field_.Name := '" << (*f_iter)->get_name() << "';" << endl;
    indent_impl(code_block) << "field_.Type_  := " << type_to_enum((*f_iter)->get_type()) << ";"
                            << endl;
    indent_impl(code_block) << "field_.ID := " << (*f_iter)->get_key() << ";" << endl;
    indent_impl(code_block) << "oprot.WriteFieldBegin(field_);" << endl;
    generate_serialize_field(code_block, is_exception, *f_iter, "Self.", local_vars);
    indent_impl(code_block) << "oprot.WriteFieldEnd();" << endl;
    if (null_allowed || has_isset) {
      indent_down_impl();
      indent_impl(code_block) << "end;" << endl;
    }
  }

  indent_impl(code_block) << "oprot.WriteFieldStop();" << endl;
  indent_impl(code_block) << "oprot.WriteStructEnd();" << endl;

  indent_down_impl();
  indent_impl(code_block) << "end;" << endl << endl;

  string cls_nm;

  cls_nm = type_name(tstruct, true, false, is_exception, is_exception);

  indent_impl(out) << "procedure " << cls_prefix << cls_nm << ".Write( const oprot: IProtocol);"
                   << endl;
  indent_impl(out) << "var" << endl;
  indent_up_impl();
  indent_impl(out) << "struc : TThriftStruct;" << endl;
  if (fields.size() > 0) {
    indent_impl(out) << "field_ : TThriftField;" << endl;
  }
  out << local_vars.str();
  indent_down_impl();
  out << code_block.str();
}

void t_delphi_generator::generate_delphi_struct_tostring_impl(ostream& out,
                                                              string cls_prefix,
                                                              t_struct* tstruct,
                                                              bool is_exception,
                                                              bool is_x_factory) {

  const vector<t_field*>& fields = tstruct->get_members();
  vector<t_field*>::const_iterator f_iter;

  string cls_nm;

  if (is_exception) {
    cls_nm = type_name(tstruct, true, (!is_x_factory), is_x_factory, true);
  } else {
    cls_nm = type_name(tstruct, true, false);
  }

  string tmp_sb = tmp("_sb");
  string tmp_first = tmp("_first");
  bool useFirstFlag = false;

  indent_impl(out) << "function " << cls_prefix << cls_nm << ".ToString: string;" << endl;
  indent_impl(out) << "var" << endl;
  indent_up_impl();
  indent_impl(out) << tmp_sb << " : TThriftStringBuilder;" << endl;
  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
    bool is_optional = ((*f_iter)->get_req() != t_field::T_REQUIRED);
    if (is_optional) {
      indent_impl(out) << tmp_first << " : System.Boolean;" << endl;
      useFirstFlag = true;
    }
    break;
  }
  indent_down_impl();
  indent_impl(out) << "begin" << endl;
  indent_up_impl();

  indent_impl(out) << tmp_sb << " := TThriftStringBuilder.Create('(');" << endl;
  indent_impl(out) << "try" << endl;
  indent_up_impl();

  if (useFirstFlag) {
    indent_impl(out) << tmp_first << " := TRUE;" << endl;
  }

  bool had_required = false; // set to true after first required field has been processed

  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
    bool null_allowed = type_can_be_null((*f_iter)->get_type());
    bool is_optional = ((*f_iter)->get_req() != t_field::T_REQUIRED);
    if (null_allowed) {
      indent_impl(out) << "if (Self." << prop_name((*f_iter), is_exception) << " <> nil)";
      if (is_optional) {
        out << " and __isset_" << prop_name(*f_iter, is_exception);
      }
      out << " then begin" << endl;
      indent_up_impl();
    } else {
      if (is_optional) {
        indent_impl(out) << "if (__isset_" << prop_name(*f_iter, is_exception) << ") then begin"
                         << endl;
        indent_up_impl();
      }
    }

    if (useFirstFlag && (!had_required)) {
      indent_impl(out) << "if not " << tmp_first << " then " << tmp_sb << ".Append(',');" << endl;
      if (is_optional) {
        indent_impl(out) << tmp_first << " := FALSE;" << endl;
      }
      indent_impl(out) << tmp_sb << ".Append('" << prop_name((*f_iter), is_exception) << ": ');"
                       << endl;
    } else {
      indent_impl(out) << tmp_sb << ".Append(', " << prop_name((*f_iter), is_exception) << ": ');"
                       << endl;
    }

    t_type* ttype = (*f_iter)->get_type();
    while (ttype->is_typedef()) {
      ttype = ((t_typedef*)ttype)->get_type();
    }

    if (ttype->is_xception() || ttype->is_struct()) {
      indent_impl(out) << "if (Self." << prop_name((*f_iter), is_exception) << " = nil) then " << tmp_sb
                       << ".Append('<null>') else " << tmp_sb << ".Append( Self."
                       << prop_name((*f_iter), is_exception) << ".ToString());" << endl;
    } else if (ttype->is_enum()) {
      indent_impl(out) << tmp_sb << ".Append(System.Integer( Self." << prop_name((*f_iter), is_exception)
                       << "));" << endl;
    } else {
      indent_impl(out) << tmp_sb << ".Append( Self." << prop_name((*f_iter), is_exception) << ");"
                       << endl;
    }

    if (null_allowed || is_optional) {
      indent_down_impl();
      indent_impl(out) << "end;" << endl;
    }

    if (!is_optional) {
      had_required = true; // now __first must be false, so we don't need to check it anymore
    }
  }

  indent_impl(out) << tmp_sb << ".Append(')');" << endl;
  indent_impl(out) << "Result := " << tmp_sb << ".ToString;" << endl;
  if (useFirstFlag) {
    indent_impl(out) << "if " << tmp_first << " then {prevent warning};" << endl;
  }

  indent_down_impl();
  indent_impl(out) << "finally" << endl;
  indent_up_impl();
  indent_impl(out) << tmp_sb << ".Free;" << endl;
  indent_down_impl();
  indent_impl(out) << "end;" << endl;

  indent_down_impl();
  indent_impl(out) << "end;" << endl << endl;
}

bool t_delphi_generator::is_void(t_type* type) {
  while (type->is_typedef()) {
    type = ((t_typedef*)type)->get_type();
  }

  if (type->is_base_type()) {
    t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
    if (tbase == t_base_type::TYPE_VOID) {
      return true;
    }
  }
  return false;
}

THRIFT_REGISTER_GENERATOR(
    delphi,
    "delphi",
    "    ansistr_binary:  Use AnsiString for binary datatype (default is TBytes).\n"
    "    register_types:  Enable TypeRegistry, allows for creation of struct, union\n"
    "                     and container instances by interface or TypeInfo()\n"
    "    constprefix:     Name TConstants classes after IDL to reduce ambiguities\n"
    "    events:          Enable and use processing events in the generated code.\n"
    "    xmldoc:          Enable XMLDoc comments for Help Insight etc.\n"
    "    async:           Generate IAsync interface to use Parallel Programming Library (XE7+ only).\n")
