require "structure_mapper/version"


class Object
  def self.from_structure data
    data
  end

  def to_structure
    self
  end
end

class Array

  def from_structure data
    inner_type=first
    if inner_type && data
      data.map{|x| inner_type.from_structure x}
    else
      data
    end

  end

  def to_structure
    map{|v| v.to_structure}
  end
end

class Hash

  def from_structure data
    key_type, value_type=first
    if key_type && value_type && data
      Hash[data.map{|k,v| [key_type.from_structure(k), value_type.from_structure(v)]}]
    else
      data
    end
  end

  def to_structure
    Hash[self.map do |k,v|
      [k.to_structure, v.to_structure]
    end]
  end
end

module Boolean
  def self.from_structure data
    data
  end
end


module StructureMapper

  module Array
    def self.included(base)
      base.extend ClassMethods
    end

    def to_structure
      (self.class.attributes || []).map do |name, _|
        public_send(name).to_structure
      end
    end

    def == other
      return false unless self.class == other.class
      self.class.attributes.each do |name,_|
        return false unless self.send(name) == other.send(name)
      end
      true
    end


    module ClassMethods

      attr_reader :attributes

      def attribute options
        name,type=options.first
        attr_accessor name
        @attributes=(@attributes || [])
        @attributes << [name,type]
      end

      def from_structure data
        return nil unless data
        result=self.new
        @attributes.each_with_index do |(name, type), index|
          result.public_send ("%s=" % name), type.from_structure(data[index])
        end
        result
      end
    end


  end

  module Hash

    def self.included(base)
      base.extend ClassMethods
    end

    def to_structure
      ::Hash[
        (self.class.attributes || {}).map do |name,type|
          [name.to_s,public_send(name).to_structure]
        end
      ]
    end

    def == other
      return false unless self.class == other.class
      self.class.attributes.each do |name,_|
        return false unless self.send(name) == other.send(name)
      end
      true
    end

    module ClassMethods

      attr_reader :attributes

      def attribute options
        name,type=options.first
        attr_accessor name
        @attributes=(@attributes || {})
        @attributes[name]=type
      end

      def from_structure data
        return nil unless data
        result=self.new
        @attributes.each do |name, type|
          result.public_send ("%s=" % name), type.from_structure(data[name.to_s])
        end
        result
      end

    end


  end
end



