 (ns bytebuf.spec
   (:refer-clojure :exclude [type read])
   (:require [bytebuf.types :as types]
+            [bytebuf.proto :as proto :refer [IStaticSize]]
             [bytebuf.buffer :as buffer]))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; (defprotocol ISpec
;;   (type [_] "Get the type of spec."))

(defprotocol ISpecSize
  (size [_] [_ data] "Calculate the size for this spec."))

(defprotocol IReadableSpec
  (read [_ buff] "Read all data from buffer."))

(defprotocol IWritableSpec
  (write [_ buff data] "Read all data from buffer."))

(defprotocol IAssocReadableSpec
  (read-field [_ buff field] "Read one concrete field from buffer."))

(defprotocol IAssocWritableSpec
  (write-field [_ buff field] "Read one concrete field from buffer."))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Types implementation

(defn associative-static-spec
  [data dict types]
  (reify
    clojure.lang.Counted
    (count [_]
      (count types))

    IStaticSize
    (size [_]
      (reduce #(+ %1 (types/size %2)) 0 types))

    IReadableSpec
    (read [_ buff offset]
      (loop [index offset result {} pairs data]
        (if-let [[fieldname type] (first pairs)]
          (let [[readeddata readedbytes] (types/read type buff index)]
            (recur (+ index readedbytes)
                   (assoc result fieldname readeddata)
                   (rest pairs)))
          [(- index offset) result])))

    IWritableSpec
    (write [_ buff offset data']
      (let [written (reduce (fn [index [fieldname type]]
                              (let [value (get data' fieldname nil)
                                    written (types/write type buff index value)]
                                (+ index written)))
                            offset data)]
        (- written offset)))))

    ;; IAssocReadableSpec
    ;; (read-field [_ buff field]
    ;;   (let [ftype (get dict field)
    ;;         startpos (reduce (fn [acc [key val]]
    ;;                            (if (= key field)
    ;;                              (reduced acc)
    ;;                              (+ acc (types/size val))))
    ;;                          0 data)]
    ;;     ;; Set the buffer position
    ;;     (buffer/seek buff startpos)
    ;;     (types/read ftype buff)))))

(defn associative-dynamic-spec
  [])
