          params
          (for [[k v] params :when (nil? v)] k)))
 
-(defn in?
+(defn- in?
   "true if seq contains element"
   [seq element]
   (some #(= element %) seq))
+
+(defn list-to-member [prefix list]
+  (apply hash-map (flatten (map #(do [(str prefix ".member." %1) %2]) (iterate inc 1) list))))
+
+(defn- build-network-params [index [k v]]
+  (if (= (name k) "IpRanges")
+    {(str "IpPermissions." index ".IpRanges.1.CidrIp") v}
+    {(str "IpPermissions." index "." (name k)) v}))
 
 (defn- process-list-of-maps [func opts]
   (loop [count (count opts)

(defn- create
  "Creates a security group and returns the id"
  [opts]
  (let [cr-params (create-params opts)]
    (if-let [response (ec2-request (merge cr-params {"Action" "CreateSecurityGroup"}))]
      (do
        (first (xml-> (xml-to-map response) :groupId text))))))


(defn process [action params]
  (condp = (keyword action)
    :CreateSecurityGroup (create params)
    (ec2-request (merge params {"Action" (name action)}))))

(defn- build-network-params [index [k v]]
  (if (= (name k) "IpRanges")
    {(str "IpPermissions." index ".IpRanges.1.CidrIp") v}
    {(str "IpPermissions." index "." (name k)) v}))

(defn- network-action [sg-id opts action]
  (let [params (process-list-of-maps build-network-params opts)]

   "Filter.3.Name" "description"
   "Filter.3.Value" (get opts :GroupDescription)})

(defn- network-config [params]
  (remove-nil {:IpProtocol (xml1-> params :ipProtocol text)
               :FromPort (xml1-> params :fromPort text)
               :ToPort (xml1-> params :toPort text)
               :IpRanges (vec (xml-> params :ipRanges :item :cidrIp text))}))

(defn- build-config [opts]
  {:Ingress (vec (map network-config (xml-> opts :securityGroupInfo :item :ipPermissions :item)))
   :Egress (vec (map network-config (xml-> opts :securityGroupInfo :item :ipPermissionsEgress :item)))})

(defn- balance-ingress [sg-id opts]
  (let [revoke-config (nth opts 0)
        add-config (nth opts 1)]
    (when-not (empty? add-config)
      (network-action sg-id add-config :AuthorizeSecurityGroupIngress))
    (when-not (empty? revoke-config)
      (network-action sg-id revoke-config :RevokeSecurityGroupIngress))))

(defn- balance-egress [sg-id opts]
  (let [revoke-config (nth opts 0)
        add-config (nth opts 1)]
    (when-not (empty? add-config)
      (network-action sg-id add-config :AuthorizeSecurityGroupEgress))
    (when-not (empty? revoke-config)
      (network-action sg-id revoke-config :RevokeSecurityGroupEgress))))

(defn- compare-sg [sg-id aws local]
  (let [remote (build-config aws)
        ingress (compare-config  (expand local :Ingress :IpRanges) (expand remote :Ingress :IpRanges))
        egress  (compare-config  (expand local :Egress :IpRanges) (expand remote :Egress :IpRanges))]
    (balance-ingress sg-id ingress)
    (balance-egress sg-id egress)))

(defn- process [action params]
  (condp = (keyword action)
    :CreateSecurityGroup (create params)
    (ec2-request (merge params {"Action" (name action)}))))

(defn ensure
  "Describe the security security group
   if not present create and apply ingress/outgress
   if present compare with the local config and apply changes if needed"
  [opts]
  (let [response (xml-to-map (process :DescribeSecurityGroups (filter-params opts)))]
    (if-let [sg-id (first (xml-> response :securityGroupInfo :item :groupId text)) ]
      (compare-sg sg-id response opts)
