(ns stone.init
  (:require
   [wisp.runtime :refer [identity odd? even? dictionary? dictionary
                         keys vals key-values merge satisfies?
                         contains-vector? map-dictionary error?
                         string? number? date? boolean? re-pattern?
                         object? nil? true? false? re-find re-matches
                         re-pattern inc dec str char int subs and or
                         print to-string = max min fn? vector?]]
   [wisp.ast :refer [symbol symbol? keyword? keyword-name name
                     namespace gensym unquote? unquote-splicing?
                     quote? syntax-quote? quote-string pr-str
                     meta with-meta]]
   [wisp.sequence :refer [lazy-seq lazy-seq? list? list cons
                          sequential? reverse map filter reduce
                          count empty? first second third rest
                          last butlast take take-while drop conj
                          assoc concat seq seq? vec sort repeat
                          every? some partition interleave nth]]
   [wisp.string :refer [split split-lines join upper-case lower-case
                        capitalize pattern-escape replace-first replace
                        blank? reverse triml trimr trim]
                        :rename {reverse string-reverse}]
   [wisp.expander :refer [macroexpand]]
   [Immutable :refer [List OrderedSet Map Seq]]
   [Handlebars]))

(defmacro advise
  [fname parentName args & body]
  `(set! ~fname ((fn [~parentName] (fn ~args ~@body))
                 ~fname)))

(defprotocol ISeq
  (iseq-seq [col])
  (iseq-reverse [col])
  (iseq-map [f col])
  (iseq-filter [f? col])
  (iseq-count [col])
  (iseq-first [col])
  (iseq-second [col])
  (iseq-third [col])
  (iseq-rest [col])
  (iseq-last [col])
  (iseq-take [col n])
  (iseq-drop [col n])
  (iseq-concat [col & collections])
  (iseq-vec [col])
  (iseq-interleave [col1 col1])
  (iseq-butlast [col])
  ;(iseq-reduce [f & params])
  ;(iseq-sort [col])
  ;(iseq-partition [col])
  )

(extend-type List
  ISeq
  (iseq-seq [col] (.toArray col))
  (iseq-reverse [col] (.reverse col))
  (iseq-map [col f] (.map col f))
  (iseq-filter [col f?] (.filter col f?))
  (iseq-count [col] (.length col))
  (iseq-first [col] (.first col))
  (iseq-second [col] (.get col 1))
  (iseq-third [col] (.get col 2))
  (iseq-rest [col] (.rest col))
  (iseq-last [col] (.last col))
  (iseq-take [col n] (.take col n))
  (iseq-drop [col n] (.skip col n))
  (iseq-concat [col & args] (col.concat.apply col args))
  (iseq-vec [col] (.toArray col))
  (iseq-interleave [col1 col2] (.interleave col1 col2))
  (iseq-butlast [col] (.skipLast col 1))
  ;(iseq-sort [???] (.sort ???))
  ;(iseq-reduce [???] (.reduce ???))
  ;(iseq-partition [???] (???))
  )

(defmacro advise-delegate
  [fname args protocol method method-args]
  `(advise ~fname __advise-parent
           ~args
           (if (satisfies? ~protocol ~(first method-args))
             (~method ~@method-args)
             (__advise-parent ~@args))))

(advise-delegate Wisp.wisp.sequence.seq [col] ISeq iseq-seq [col])
(advise-delegate Wisp.wisp.sequence.reverse [col] ISeq iseq-reverse [col])
(advise-delegate Wisp.wisp.sequence.map [f col] ISeq iseq-map [col f])
(advise-delegate Wisp.wisp.sequence.filter [f? col] ISeq iseq-filter [col f?])
(advise-delegate Wisp.wisp.sequence.count [col] ISeq iseq-count [col])
(advise-delegate Wisp.wisp.sequence.first [col] ISeq iseq-first [col])
(advise-delegate Wisp.wisp.sequence.second [col] ISeq iseq-second [col])
(advise-delegate Wisp.wisp.sequence.third [col] ISeq iseq-third [col])
(advise-delegate Wisp.wisp.sequence.rest [col] ISeq iseq-rest [col])
(advise-delegate Wisp.wisp.sequence.last [col] ISeq iseq-last [col])
(advise-delegate Wisp.wisp.sequence.take [n col] ISeq iseq-take [col n])
(advise-delegate Wisp.wisp.sequence.drop [n col] ISeq iseq-drop [col n])
(advise Wisp.wisp.sequence.concat
        parent
        [col & args]
        (if (satisfies? ISeq col)
          (iseq-concat.apply null arguments)
          (parent.apply null arguments)))
(advise-delegate Wisp.wisp.sequence.vec [col] ISeq iseq-vec [col])
(advise-delegate Wisp.wisp.sequence.interleave [col1 col2] ISeq iseq-interleave [col1 col2])
(advise-delegate Wisp.wisp.sequence.butlast [col] ISeq iseq-butlast [col])
;(advise-delegate Wisp.wisp.sequence.sort [col] ISeq iseq-sort [col])
;(advise-delegate Wisp.wisp.sequence.reduce [col] ISeq iseq-reduce [col])
;(advise-delegate Wisp.wisp.sequence.partition [col] ISeq iseq-partition [col])

;; tests for List ISeq protocol implementation
(comment
  (console.log (seq (List [1 2])))
  (console.log (seq (reverse (List [1 2]))))
  (console.log (String (map #(+ 10 %) (List [1 2]))))
  (console.log (String (filter #(mod % 2) (List [1 2 3 4 5]))))
  (console.log (String (drop 2 (List [1 2 3 4 5]))))
  (console.log (String (concat (List [1 2]) (List [3 4]))))
  (console.log (String (vec (List [1 2]))))
  (console.log (interleave [1 2 3] [10 20]))
  (console.log (String (interleave (List [1 2 3]) (List [10 20])))))

(defmacro doseq
  "(doseq [var collection] ...)"
  [args & body]
  (let [arg (first args)
        sequence (second args)]
    `(let [s ~sequence]
       (loop [pos (if (vector? s) 0 s)]
         (if (if (vector? s) (< pos (.-length s)) pos)
           (let [~arg (if (vector? s) (get s pos) (first pos))]
             ~@body
             (recur (if (vector? s) (+ 1 pos) (rest pos)))))))))

(comment
  ;; test for doseq
 (console.log "doseq...")
 (doseq [x [1 2 3]]
   (console.log x))
 (console.log "done."))

(defn zip [a b]
  (if (and (empty? a) (empty? b))
    ()
    (cons (cons (if (empty? a) () (first a))
                (if (empty? b) () (first b)))
          (zip (if (empty? a) () (rest a))
               (if (empty? b) () (rest b))))))

(defn find [pred list]
  (cond (vector? list) (let [len (count list)]
                         (loop [pos 0]
                           (cond (>= pos len) nil
                                 (pred (get list pos)) (get list pos)
                                 :else (recur (inc pos)))))
        :else (loop [list list]
                (cond (empty? list) nil
                      (pred (first list)) (first list)
                      :else (recur (rest list))))))

(comment
  ;; test for find
  (console.log "even " (find (fn [x] (not (mod x 2))) '(1 2 3))))

(defn ->vec
  [obj]
  (Array.prototype.slice.call obj))

(defn Fork [promise err]
  (set! this.promise promise)
  (set! this.error (or err [false]))
  this)

(Object.assign
 Fork.prototype
 {:hasError (fn [] (get this.error 0))
  :setError (fn [] (set! (get this.error 0) true))
  :fork (fn [promise] (Fork. promise this.error))
  :then (fn [func]
          (console.log "SETUP THEN")
          (let [fork this]
            (this.fork (this.promise.then (fn [arg]
                                            (if (not (fork.has-error))
                                              (do
                                                (console.log "CALL THEN")
                                                (func arg))
                                              (console.log "BYPASS THEN")))))))
  :catch (fn [func]
          (console.log "SETUP CATCH")
           (let [fork this]
             (this.fork (this.promise.catch (fn [err]
                                              (if (not (fork.has-error))
                                                (do
                                                  (console.log "CALL CATCH")
                                                  (debugger!)
                                                  (fork.set-error)
                                                  (func err))
                                                (console.log "BYPASS CATCH")))))))})
