#!/usr/bin/env ruby

$:.unshift(File.dirname(__FILE__))
require 'all-code'
require 'set'

EXCEPTION_INCLUDES = Set.new [
  '<ironautomata/eudoxus_subautomata.h>',
  '"eudoxus_subengine.c"',
  '<boost/preprocessor/iteration/local.hpp>'
]

CANONICAL_INCLUDE_ORDER = [
  '"ironbee_config_auto.h"',

  '<uuid.h>', # uuid.h is a mess.

  :self,
  :private,

  '"config-parser.h"',

  # Dirty hack section
  '"user_agent_private.h"',
  '"txlog_json.hpp"',
  '"xrules.hpp"',
  '"xrules_acls.hpp"',
  '"xrules_exception.hpp"',
  '<ironbee/module_sym.h>',

  # predicate
  '<ironbee/predicate/bfs.hpp>',
  '<ironbee/predicate/call_factory.hpp>',
  '<ironbee/predicate/call_helpers.hpp>',
  '<ironbee/predicate/dag.hpp>',
  '<ironbee/predicate/dot.hpp>',
  '<ironbee/predicate/dot2.hpp>',
  '<ironbee/predicate/eval.hpp>',
  '<ironbee/predicate/functional.hpp>',
  '<ironbee/predicate/ironbee.hpp>',
  '<ironbee/predicate/less.hpp>',
  '<ironbee/predicate/leaves.hpp>',
  '<ironbee/predicate/merge_graph.hpp>',
  '<ironbee/predicate/meta_call.hpp>',
  '<ironbee/predicate/parse.hpp>',
  '<ironbee/predicate/pre_eval_graph.hpp>',
  '<ironbee/predicate/reporter.hpp>',
  '<ironbee/predicate/standard.hpp>',
  '<ironbee/predicate/standard_boolean.hpp>',
  '<ironbee/predicate/standard_development.hpp>',
  '<ironbee/predicate/standard_filter.hpp>',
  '<ironbee/predicate/standard_ironbee.hpp>',
  '<ironbee/predicate/standard_predicate.hpp>',
  '<ironbee/predicate/standard_string.hpp>',
  '<ironbee/predicate/standard_template.hpp>',
  '<ironbee/predicate/standard_list.hpp>',
  '<ironbee/predicate/standard_math.hpp>',
  '<ironbee/predicate/transform_graph.hpp>',
  '<ironbee/predicate/tree_copy.hpp>',
  '<ironbee/predicate/validate.hpp>',
  '<ironbee/predicate/validate_graph.hpp>',
  '<ironbee/predicate/value.hpp>',

  # CLIPP
  '<clipp/aggregate_modifier.hpp>',
  '<clipp/apache_generator.hpp>',
  '<clipp/clipp.pb.h>',
  '<clipp/configuration_parser.hpp>',
  '<clipp/connection_modifiers.hpp>',
  '<clipp/control.hpp>',
  '<clipp/echo_generator.hpp>',
  '<clipp/edit_modifier.hpp>',
  '<clipp/fill_body_modifier.hpp>',
  '<clipp/header_modifiers.hpp>',
  '<clipp/htp_consumer.hpp>',
  '<clipp/htp_generator.hpp>',
  '<clipp/input.hpp>',
  '<clipp/ironbee.hpp>',
  '<clipp/limit_modifier.hpp>',
  '<clipp/modsec_audit_log.hpp>',
  '<clipp/modsec_audit_log_generator.hpp>',
  '<clipp/null_consumer.hpp>',
  '<clipp/parse_modifier.hpp>',
  '<clipp/pb_consumer.hpp>',
  '<clipp/pb_generator.hpp>',
  '<clipp/pcap_generator.hpp>',
  '<clipp/random_support.hpp>',
  '<clipp/raw_consumer.hpp>',
  '<clipp/raw_generator.hpp>',
  '<clipp/select_modifier.hpp>',
  '<clipp/split_modifier.hpp>',
  '<clipp/suricata_generator.hpp>',
  '<clipp/time_modifier.hpp>',
  '<clipp/unparse_modifier.hpp>',
  '<clipp/view.hpp>',

  # Modules
  '<ironbee/module/constant.h>',
  '<ironbee/module/parser_suite.hpp>',
  '<ironbee/module/ibmod_predicate_core.hpp>',

  '<ironbeepp/abi_compatibility.hpp>',
  '<ironbeepp/action.hpp>',
  '<ironbeepp/all.hpp>',
  '<ironbeepp/byte_string.hpp>',
  '<ironbeepp/catch.hpp>',
  '<ironbeepp/clock.hpp>',
  '<ironbeepp/common_semantics.hpp>',
  '<ironbeepp/configuration_directives.hpp>',
  '<ironbeepp/configuration_map.hpp>',
  '<ironbeepp/configuration_parser.hpp>',
  '<ironbeepp/connection.hpp>',
  '<ironbeepp/connection_data.hpp>',
  '<ironbeepp/context.hpp>',
  '<ironbeepp/c_trampoline.hpp>',
  '<ironbeepp/data.hpp>',
  '<ironbeepp/engine.hpp>',
  '<ironbeepp/exception.hpp>',
  '<ironbeepp/field.hpp>',
  '<ironbeepp/hash.hpp>',
  '<ironbeepp/hooks.hpp>',
  '<ironbeepp/ironbee.hpp>',
  '<ironbeepp/list.hpp>',
  '<ironbeepp/memory_manager.hpp>',
  '<ironbeepp/memory_pool.hpp>',
  '<ironbeepp/memory_pool_lite.hpp>',
  '<ironbeepp/module.hpp>',
  '<ironbeepp/module_bootstrap.hpp>',
  '<ironbeepp/module_delegate.hpp>',
  '<ironbeepp/notifier.hpp>',
  '<ironbeepp/operator.hpp>',
  '<ironbeepp/parsed_header.hpp>',
  '<ironbeepp/parsed_request_line.hpp>',
  '<ironbeepp/parsed_response_line.hpp>',
  '<ironbeepp/parser_suite_adaptors.hpp>',
  '<ironbeepp/server.hpp>',
  '<ironbeepp/site.hpp>',
  '<ironbeepp/test_fixture.hpp>',
  '<ironbeepp/throw.hpp>',
  '<ironbeepp/transaction.hpp>',
  '<ironbeepp/transaction_data.hpp>',
  '<ironbeepp/transformation.hpp>',
  '<ironbeepp/var.hpp>',
  '"collection_manager_private.h"',
  '"core_audit_private.h"',
  '"core_private.h"',
  '"engine_private.h"',
  '"engine_manager_log_private.h"',
  '"ironbee_private.h"',
  '"json_yajl_private.h"',
  '"kvstore_private.h"',
  '"lua/ironbee.h"',
  '"lua_common_private.h"',
  '"lua_modules_private.h"',
  '"lua_private.h"',
  '"lua_rules_private.h"',
  '"lua_runtime_private.h"',
  '"moddevel_private.h"',
  '"module_private.h"',
  '"persistence_framework.h"',
  '"persistence_framework_private.h"',
  '"rule_engine_private.h"',
  '"rule_logger_private.h"',
  '"rules_lua_private.h"',
  '"state_notify_private.h"',
  '"txlog.h"',
  '"user_agent_private.h"',

  # Automata
  '<ironautomata/bits.h>',
  '<ironautomata/eudoxus.h>',
  '<ironautomata/eudoxus_automata.h>',
  '<ironautomata/vls.h>',

  '<ironautomata/buffer.hpp>',
  '<ironautomata/deduplicate_outputs.hpp>',
  '<ironautomata/eudoxus_compiler.hpp>',
  '<ironautomata/generator/aho_corasick.hpp>',
  '<ironautomata/intermediate.hpp>',
  '<ironautomata/intermediate.pb.h>',
  '<ironautomata/logger.hpp>',
  '<ironautomata/optimize_edges.hpp>',
  '<ironautomata/translate_nonadvancing.hpp>',
  # End Automata

  '<ironbee/action.h>',
  '<ironbee/ahocorasick.h>',
  '<ironbee/array.h>',
  '<ironbee/build.h>',
  '<ironbee/bytestr.h>',
  '<ironbee/capture.h>',
  '<ironbee/cfgmap.h>',
  '<ironbee/clock.h>',
  '<ironbee/collection_manager.h>',
  '<ironbee/config.h>',
  '<ironbee/context.h>',
  '<ironbee/context_selection.h>',
  '<ironbee/core.h>',
  '<ironbee/decode.h>',
  '<ironbee/dso.h>',
  '<ironbee/engine.h>',
  '<ironbee/engine_manager.h>',
  '<ironbee/engine_state.h>',
  '<ironbee/engine_types.h>',
  '<ironbee/escape.h>',
  '<ironbee/field.h>',
  '<ironbee/file.h>',
  '<ironbee/flags.h>',
  '<ironbee/hash.h>',
  '<ironbee/ident.h>',
  '<ironbee/ip.h>',
  '<ironbee/ipset.h>',
  '<ironbee/json.h>',
  '<ironbee/kvstore.h>',
  '<ironbee/kvstore_filesystem.h>',
  '<ironbee/list.h>',
  '<ironbee/lock.h>',
  '<ironbee/log.h>',
  '<ironbee/logevent.h>',
  '<ironbee/logformat.h>',
  '<ironbee/logger.h>',
  '<ironbee/mm.h>',
  '<ironbee/mm_mpool.h>',
  '<ironbee/mm_mpool_lite.h>',
  '<ironbee/module.h>',
  '<ironbee/module_sym.h>',
  '<ironbee/mpool.h>',
  '<ironbee/mpool_lite.h>',
  '<ironbee/operator.h>',
  '<ironbee/path.h>',
  '<ironbee/ipset.h>',
  '<ironbee/parsed_content.h>',
  '<ironbee/queue.h>',
  '<ironbee/release.h>',
  '<ironbee/resource_pool.h>',
  '<ironbee/rule_defs.h>',
  '<ironbee/rule_engine.h>',
  '<ironbee/rule_logger.h>',
  '<ironbee/server.h>',
  '<ironbee/site.h>',
  '<ironbee/state_notify.h>',
  '<ironbee/string.h>',
  '<ironbee/stringset.h>',
  '<ironbee/string_assembly.h>',
  '<ironbee/string_lower.h>',
  '<ironbee/string_trim.h>',
  '<ironbee/string_whitespace.h>',
  '<ironbee/stream.h>',
  '<ironbee/strval.h>',
  '<ironbee/transformation.h>',
  '<ironbee/types.h>',
  '<ironbee/util.h>',
  '<ironbee/uuid.h>',
  '<ironbee/var.h>',
  '<ironbee/vector.h>',

  '<boost/algorithm/string/classification.hpp>',
  '<boost/algorithm/string/join.hpp>',
  '<boost/algorithm/string/predicate.hpp>',
  '<boost/algorithm/string/split.hpp>',
  '<boost/algorithm/string/trim.hpp>',
  '<boost/any.hpp>',
  '<boost/assign.hpp>',
  '<boost/assign/list_of.hpp>',
  '<boost/bind.hpp>',
  '<boost/bind/protect.hpp>',
  '<boost/call_traits.hpp>',
  '<boost/chrono.hpp>',
  '<boost/date_time/gregorian/gregorian_types.hpp>',
  '<boost/date_time/local_time/local_time.hpp>',
  '<boost/date_time/posix_time/posix_time.hpp>',
  '<boost/date_time/posix_time/posix_time_types.hpp>',
  '<boost/date_time/posix_time/ptime.hpp>',
  '<boost/date_time/time_facet.hpp>',
  '<boost/date_time/time_zone_base.hpp>',
  '<boost/dynamic_bitset.hpp>',
  '<boost/enable_shared_from_this.hpp>',
  '<boost/exception/all.hpp>',
  '<boost/filesystem.hpp>',
  '<boost/filesystem/fstream.hpp>',
  '<boost/foreach.hpp>',
  '<boost/format.hpp>',
  '<boost/function.hpp>',
  '<boost/function_output_iterator.hpp>',
  '<boost/fusion/adapted.hpp>',
  '<boost/fusion/adapted/boost_tuple.hpp>',
  '<boost/iostreams/device/mapped_file.hpp>',
  '<boost/iterator/iterator_adaptor.hpp>',
  '<boost/iterator/iterator_facade.hpp>',
  '<boost/iterator/transform_iterator.hpp>',
  '<boost/lexical_cast.hpp>',
  '<boost/make_shared.hpp>',
  '<boost/mpl/or.hpp>',
  '<boost/noncopyable.hpp>',
  '<boost/operators.hpp>',
  '<boost/preprocessor/arithmetic/add.hpp>',
  '<boost/preprocessor/arithmetic/sub.hpp>',
  '<boost/preprocessor/cat.hpp>',
  '<boost/preprocessor/iteration/local.hpp>',
  '<boost/preprocessor/repetition.hpp>',
  '<boost/program_options.hpp>',
  '<boost/random.hpp>',
  '<boost/range/iterator_range.hpp>',
  '<boost/regex.hpp>',
  '<boost/scoped_array.hpp>',
  '<boost/scoped_ptr.hpp>',
  '<boost/shared_ptr.hpp>',
  '<boost/spirit/include/phoenix.hpp>',
  '<boost/spirit/include/qi.hpp>',
  '<boost/static_assert.hpp>',
  '<boost/thread.hpp>',
  '<boost/tuple/tuple.hpp>',
  '<boost/type_traits/is_class.hpp>',
  '<boost/type_traits/is_convertible.hpp>',
  '<boost/type_traits/is_const.hpp>',
  '<boost/type_traits/is_float.hpp>',
  '<boost/type_traits/is_pointer.hpp>',
  '<boost/type_traits/is_same.hpp>',
  '<boost/type_traits/is_signed.hpp>',
  '<boost/type_traits/is_unsigned.hpp>',
  '<boost/type_traits/remove_const.hpp>',
  '<boost/utility.hpp>',
  '<boost/utility/enable_if.hpp>',
  '<boost/uuid/uuid.hpp>',
  '<boost/version.hpp>',
  # Special case for engine_shutdown.cpp
  '<boost/atomic.hpp>',

  '<google/protobuf/io/gzip_stream.h>',
  '<google/protobuf/io/zero_copy_stream_impl_lite.h>',

  '<curl/curl.h>',
  '<dslib.h>',
  '<GeoIP.h>',
  '<htp.h>',
  '<htp_private.h>',
  '<lauxlib.h>',
  '<libinjection.h>',
  '<libinjection_sqli.h>',
  '<libinjection_xss.h>',
  '<lua.h>',
  '<lualib.h>',
  '<modp_ascii.h>',
  '<modp_b16.h>',
  '<modp_b64.h>',
  '<modp_b64w.h>',
  '<modp_burl.h>',
  '<nids.h>',
  '<pcre.h>',
  '<pcreposix.h>',
  '<pthread.h>',
  '<sqli_normalize.h>',
  '<sqli_fingerprints.h>',
  '<sqlparse.h>',
  '<sqlparse_private.h>',
  '<sqltfn.h>',
  '<uuid.h>',
  '<valgrind/memcheck.h>',
  '<valgrind/valgrind.h>',
  '<yajl/yajl_common.h>',
  '<yajl/yajl_gen.h>',
  '<yajl/yajl_parse.h>',
  '<yajl/yajl_tree.h>',

  '<algorithm>',
  '<fstream>',
  '<iostream>',
  '<list>',
  '<map>',
  '<ostream>',
  '<queue>',
  '<set>',
  '<sstream>',
  '<stdexcept>',
  '<string>',
  '<vector>',

  '<cassert>',
  '<csignal>',
  '<cstdlib>',
  '<cstring>',
  '<ctime>',

  '<assert.h>',
  '<ctype.h>',
  '<dirent.h>',
  '<dlfcn.h>',
  '<errno.h>',
  '<fcntl.h>',
  '<glib.h>',
  '<getopt.h>',
  '<glob.h>',
  '<inttypes.h>',
  '<libgen.h>',
  '<limits.h>',
  '<math.h>',
  '<regex.h>',
  '<signal.h>',
  '<stdarg.h>',
  '<stdbool.h>',
  '<stddef.h>',
  '<stdint.h>',
  '<stdio.h>',
  '<stdlib.h>',
  '<string.h>',
  '<strings.h>',
  '<arpa/inet.h>',
  '<netinet/in.h>',
  '<time.h>',
  '<unistd.h>',
  '<sys/ipc.h>',
  '<sys/sem.h>',
  '<sys/socket.h>',
  '<sys/stat.h>',
  '<sys/time.h>',
  '<sys/types.h>',
  '<sys/un.h>'
]

def extract_includes(path)
  result = []
  IO::foreach(path) do |line|
    line.strip!
    if line =~ /^#include (.+[">])$/
      result << $1
    end
  end
  result
end

all_ironbee_code do |path|
  next if path =~ /test/
  last_index = -1
  self_name = nil
  private_name = nil
  if path =~ /(\.c(pp)?)$/
    self_name = Regexp.new(
      "(ironbee|ironautomata|predicate|modules)/(.+/)?" + File.basename(path, $1) + '\.h(pp)?'
    )
    private_name = Regexp.new(
      '^"' + File.basename(path, $1) + '(_private)?\.h(pp)?'
    )
  end
  extract_includes(path).each do |i|
    next if EXCEPTION_INCLUDES.member?(i)
    index = nil
    if i =~ self_name
      index = CANONICAL_INCLUDE_ORDER.index(:self)
    elsif i =~ private_name
      index = CANONICAL_INCLUDE_ORDER.index(:private)
    end
    index ||= CANONICAL_INCLUDE_ORDER.index(i)

    if index.nil?
      puts "Unknown include in #{path}: #{i}"
    elsif index < last_index
      puts "Include out of order in #{path}: #{i} (#{index} vs #{last_index}=#{CANONICAL_INCLUDE_ORDER[last_index]})"
    else
      last_index = index
    end
  end
end
