require 'rubygems'
require 'json'

s = JSON.parse(File.read('amqp-0.8.json'))

# require 'pp'
# pp(s)
# exit

require 'erb'

puts ERB.new(%q[
  module AMQP
    HEADER        = <%= s['name'].dump %>.freeze
    VERSION_MAJOR = <%= s['major-version'] %>
    VERSION_MINOR = <%= s['minor-version'] %>
    PORT          = <%= s['port'] %>

    class Frame
      TYPES = [
        nil,
        <%- s['constants'].select{|c| (1..8).include? c['value'] }.each do |c| -%>
        :<%= c['name'].tr('-', '_').gsub(/^FRAME_/,'').upcase -%>,
        <%- end -%>
      ]
      FOOTER = <%= frame_end = s['constants'].find{|c| c['name'] == 'FRAME-END' }['value'] %>
    end

    RESPONSES = {
      <%- s['constants'].select{|c| c['value'] != frame_end and (200..500).include? c['value'] }.each do |c| -%>
      <%= c['value'] %> => :<%= c['name'].tr('-', '_').gsub(/^FRAME_/,'').upcase -%>,
      <%- end -%>
    }

    FIELDS = [
      <%- s['domains'].select{|d| d.first == d.last }.each do |d| -%>
      :<%= d.first -%>,
      <%- end -%>
    ]

    module Protocol
      class Class
        class << self
          FIELDS.each do |f|
            class_eval %[
              def #{f} name
                properties << [ :#{f}, name ] unless properties.include?([:#{f}, name])
                attr_accessor name
              end
            ]
          end
          
          def properties() @properties ||= [] end

          def id()   self::ID end
          def name() self::NAME end
        end

        class Method
          class << self
            FIELDS.each do |f|
              class_eval %[
                def #{f} name
                  arguments << [ :#{f}, name ] unless arguments.include?([:#{f}, name])
                  attr_accessor name
                end
              ]
            end
            
            def arguments() @arguments ||= [] end

            def parent() Protocol.const_get(self.to_s[/Protocol::(.+?)::/,1]) end
            def id()     self::ID end
            def name()   self::NAME end
          end

          def == b
            self.class.arguments.inject(true) do |eql, (type, name)|
              eql and __send__("#{name}") == b.__send__("#{name}")
            end
          end
        end
      
        def self.methods() @methods ||= {} end
      
        def self.Method(id, name)
          @_base_methods ||= {}
          @_base_methods[id] ||= ::Class.new(Method) do
            class_eval %[
              def self.inherited klass
                klass.const_set(:ID, #{id})
                klass.const_set(:NAME, :#{name.to_s})
                klass.parent.methods[#{id}] = klass
                klass.parent.methods[klass::NAME] = klass
              end
            ]
          end
        end
      end

      def self.classes() @classes ||= {} end

      def self.Class(id, name)
        @_base_classes ||= {}
        @_base_classes[id] ||= ::Class.new(Class) do
          class_eval %[
            def self.inherited klass
              klass.const_set(:ID, #{id})
              klass.const_set(:NAME, :#{name.to_s})
              Protocol.classes[#{id}] = klass
              Protocol.classes[klass::NAME] = klass
            end
          ]
        end
      end
      
      <%- s['classes'].each do |c| -%>
      class <%= c['name'].capitalize.ljust(12) %> < Class(<%= c['id'] %>, :<%= c['name'] %>); end
      <%- end -%>

      <%- s['classes'].each do |c| -%>
      class <%= c['name'].capitalize %>
        <%- c['properties'].each do |p| -%>
        <%= p['type'].ljust(10) %> :<%= p['name'].tr('-','_') %>
        <%- end if c['properties'] -%>

        <%- c['methods'].each do |m| -%>
        class <%= m['name'].capitalize.gsub(/-(.)/){ "#{$1.upcase}"}.ljust(12) %> < Method(<%= m['id'] %>, :<%= m['name'].tr('- ','_') %>); end
        <%- end -%>

        <%- c['methods'].each do |m| -%>
        class <%= m['name'].capitalize.gsub(/-(.)/){ "#{$1.upcase}"} %>
          <%- m['arguments'].each do |a| -%>
          <%- if a['domain'] -%>
          <%= s['domains'].find{|k,v| k == a['domain']}.last.ljust(10) %> :<%= a['name'].tr('- ','_') %>
          <%- else -%>
          <%= a['type'].ljust(10) %> :<%= a['name'].tr('- ','_') %>
          <%- end -%>
          <%- end if m['arguments'] -%>
        end

        <%- end -%>
      end

      <%- end -%>
    end
  end
].gsub!(/^  /,''), nil, '>-%').result(binding)