require 'rubygems'
require 'json'

name = 'amqp-0.8.json'
path = File.dirname(__FILE__)+'/'+name
s = JSON.parse(File.read(path))

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

require 'erb'

puts ERB.new(%q[
  #:stopdoc:
  # this file was autogenerated on <%= Time.now.to_s %>
  # using <%= name.ljust(16) %> (mtime: <%= File.mtime(path) %>)
  #
  # DO NOT EDIT! (edit protocol/codegen.rb instead, and run `rake codegen`)

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

    class Frame
      def self.types
        @types ||= {}
      end

      def self.Frame id
        (@_base_frames ||= {})[id] ||= Class.new(Frame) do
          class_eval %[
            def self.inherited klass
              klass.const_set(:ID, #{id})
              Frame.types[#{id}] = klass
            end
          ]
        end
      end

      <%- s['constants'].select{|c| (1..8).include? c['value'] }.each do |c| -%>
      class <%= c['name'].gsub(/^FRAME-/,'').split('-').map{|w| w.downcase.capitalize}.join.ljust(9) -%> < Frame( <%= c['value'] -%> ); end
      <%- 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 section() 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.section.methods[#{id}] = klass
                klass.section.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
    end
  end
    
  module AMQP
    module Protocol
      <%- s['classes'].each do |c| -%>
      class <%= c['name'].capitalize.ljust(12) %> < Class( <%= c['id'].to_s.rjust(3) %>, :<%= c['name'].ljust(12) %> ); 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'].to_s.rjust(3) %>, :<%= m['name'].tr('- ','_').ljust(14) %> ); 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)
