require 'forwardable'

module Woyo

module Attributes

  class AttributesHash < Hash

    alias_method :names, :keys
    alias_method :set, :[]=

    attr_reader :listeners

    def initialize
      @listeners = {}
    end

    def add_attribute_listener attr, listener
      @listeners[attr] = listener
    end
    
    def []= attr, value
      old_value = self[attr]
      super
      if ( listener = @listeners[attr] ) && value != old_value
        listener.notify attr, value
      end
    end

  end

  class Group

    extend Forwardable

    def_delegators :@members, :count
    def_delegators :@attributes, :[], :[]=

    attr_reader :members, :attributes

    def initialize attributes, *members
      @attributes = attributes
      @members = members
    end

    def names
      @members
    end

    def values
      @attributes.values_at *@members
    end

  end

  class BooleanGroup < Group

    attr_reader :default 
    
    def initialize attributes, *members
      super
      @default = @members.first
      self[@default] = true
      @members.each { |member| @attributes.add_attribute_listener member, self }
    end

    def []= this_member, value
      raise '#{this_member} is not a member of this group' unless @members.include? this_member
      super
      if value #true
        # sync group members via AttributesHash#set to prevent triggering notify
        @members.each { |member| @attributes.set(member,false) unless member == this_member }
      else     #false
        if self.count == 2  # binary group
          @members.each { |member| @attributes.set member ,(member != this_member ) }
        else
          self[@default] = true # revert to default
        end
      end
    end

    def notify this_member, value
      # assuming value==true for now
      # sync group members via AttributesHash#set to prevent re-triggering notify
      @members.each { |member| @attributes.set(member,false) unless member == this_member }
    end

    def value
      @members.detect { |member| @attributes[member] } # name of first member with true value 
    end

    def default!
      self[@default] = true
    end

  end

end

end