module Rocx
  module Elements
    class BaseElement
      class << self

        def tag_name(*args)
          @tag_name = args.first if args.any?
          @tag_name
        end

        def namespace(*args)
          @namespace = args.first if args.any?
          @namespace
        end

        def attribute(name, limit_to: nil, xml_name: nil, regex: nil)
          attr_reader name

          if limit_to
            define_method "valid_#{name}" do
              limit_to
            end

            define_method "valid_#{name}?" do |value|
              send("valid_#{name}").member? value
            end

            define_method "#{name}=" do |value|
              valid_values = send("valid_#{name}").join(", ")
              raise ArgumentError, "Invalid #{name}; must be one of #{valid_values}" unless send("valid_#{name}?", value)
              instance_variable_set("@#{name}", value)
            end
          elsif regex
            define_method "valid_#{name}?" do |value|
              !!(regex =~ value)
            end

            define_method "#{name}=" do |value|
              raise ArgumentError, "Invalid #{name}; must match #{regex.inspect}" unless send("valid_#{name}?", value)
              instance_variable_set("@#{name}", value)
            end
          else
            attr_writer name
          end

          attributes[name] = xml_name || name
        end

        def attributes
          @attributes ||= {}
        end
      end

      def initialize(**args)
        args.each do |property, value|
          self.send("#{property}=", value)
        end
      end

      def to_xml(xml)
        if namespace
          xml[namespace].public_send tag_name, xml_attributes
        else
          xml.public_send tag_name, xml_attributes
        end
      end

    protected

      def xml_attributes
        attributes.each_with_object({}) do |(attribute, name), props|
          value = send(attribute)
          props[name] = value unless value.nil?
        end
      end

      def attributes
        self.class.attributes
      end

      def tag_name
        self.class.tag_name
      end

      def namespace
        self.class.namespace
      end

    end
  end
end