   (:require 
             [clojure.core.async :refer :all :as async :exclude [map into]]
             [zaffre.color :as zcolor]
-            ;[robinson.viewport :as rv]
             [zaffre.terminal :as zat]
-            [zaffre.animation.animatedterminal :as zaat]
             [zaffre.util :as zutil]
             [taoensso.timbre :as log]
-            [overtone.at-at :as atat])
-  #?(:clj
-     (:import  ;zaffre.terminal.Terminal
-               zaffre.animation.animatedterminal.AAnimatedTerminal
-               zaffre.animation.animatedterminal.AId
-               zaffre.animation.animatedterminal.AEffect
-               zaffre.animation.animatedterminal.AFilter
-               zaffre.animation.animatedterminal.AMask
-               zaffre.animation.animatedterminal.ARequiresState
-               zaffre.animation.animatedterminal.APalette)))
+            [overtone.at-at :as atat]))
 
 
 #?(:clj
 (set! *warn-on-reflection* true))
 
-(def frame-count (atom 0))
 (def ^:dynamic *rain-rate* 0.96)
 
 

    (fn [terminal]
      (RainEffect. terminal layer-id rain-state))))

(defn swap-rain-mask! [terminal f]
  (zaat/swap-matching-effect-or-filter! terminal
                              (fn [fx] (= (zaat/id fx) :rain))
                              (fn [rain-fx] (zaat/swap-mask! rain-fx f))))

(defn reset-rain-mask! [terminal v]
  (zaat/swap-matching-effect-or-filter! terminal
                              (fn [fx] (= (zaat/id fx) :rain))
                              (fn [rain-fx]
                                (let [[columns rows] (size terminal)]
                                  (zaat/reset-mask! rain-fx (repeat rows (repeat columns v)))))))

(defn transform [terminal mask state transforms]
  #_(println (format "drawing %d transforms" (count transforms)))
  (doseq [transform transforms]
    (let [path (zutil/line-segment-fast-without-endpoints (get transform :from-xy) (get transform :to-xy))
          n    (int (/ @frame-count 1))]
      (when (< n (count path))
        #_(log/info "n" n "count path" (count path))
        (let [[vx vy] path #_(rv/world-xy->screen-xy state (nth path n))]
          (when (or true (get-in mask [vy vx]))
            #_(println "drawing * @ " vx vy)
            ;(zat/set-char! terminal :fx vx vy \*)
            (zat/set-fg! terminal :fx vx vy (zcolor/color->rgb :white))
            (zat/set-bg! terminal :fx vx vy (zcolor/color->rgb :black))))))))
  
(defrecord TransformEffect
  [terminal mask state]
  AId
  (id [this]
    :transform)
  AEffect
  (apply-effect! [this terminal]
    (dosync 
      (when-let [state @state]
        (transform terminal @mask state (get-in state [:fx :transform])))))
  AMask
  (swap-mask! [this f]
    (swap! mask f)
    ;(println "=====================")
    ;(doseq [line @mask]
    ;  (println (mapv (fn [v] (if v 1 0)) line)))
    this)
  (reset-mask! [this new-mask]
    (reset! mask new-mask)
    this)
  ARequiresState
  (reset-state! [this new-state]
    (reset! state new-state)
    this)
  Object
  (toString [this]
    (format "TransformEffect terminal, mask, state")))

(defn make-transform-effect
  [terminal cell-opts]
  (let [[vw vh]    (size terminal)
        mask       (atom (repeat vh (repeat vw true)))]
    (TransformEffect. terminal mask (atom nil))))

(defn blink [terminal state blinks]
  ;(prinln (format "drawing %d blinks" (count blinks)))
  (doseq [blink blinks]
    #_(println @frame-count (mod @frame-count 15))
    (when (< (mod @frame-count 15) 7)
      (let [[vx vy] (get blink :xy)]
          #_(println "drawing * @ " vx vy)
          (zat/put-chars! terminal :fx [{:x vx :y vy :c \space}])))))

(defrecord BlinkEffect
  [terminal state]
  AId
  (id [this]
    :blink)
  AEffect
  (apply-effect! [this terminal]
    (dosync 
      (when-let [state @state]
        (blink terminal state (get-in state [:fx :blink])))))
  ARequiresState
  (reset-state! [this new-state]
    (reset! state new-state)
    this)
  Object
  (toString [this]
    (format "BlinkEffect terminal, state")))

(defn make-blink-effect
  [terminal cell-opts]
  (let [[vw vh]    (size terminal)]
    (BlinkEffect. terminal (atom nil))))

(defn blip [terminal state blips]
  (doseq [blip blips]
    (let [[vx vy] blip #_(rv/world-xy->screen-xy state (get blip :xy))]
      ;(println "drawing blip" blip "@" vx vy "frame-count" @frame-count)
      ;; get the last instant before @frame-count
      (when-let [instant (->> (get blip :instants)
                              (filter (fn [instant]
                                        (< (get instant :time) @frame-count)))
                              (sort-by (fn [instant]
                                         (get instant :time)))
                              last)]
        ;(println "drawing instant" instant)
        #_(when-let [ch (get instant :ch)]
          (zat/set-char! terminal :fx vx vy ch))
        (when-let [fg (get instant :fg)]
          (zat/set-fg! terminal :fx vx vy fg))
        (when-let [bg (get instant :bg)]
          (zat/set-bg! terminal :fx vx vy bg))))))
    
(defrecord BlipEffect
  [terminal state]
  AId
  (id [this]
    :blip)
  AEffect
  (apply-effect! [this terminal]
    (dosync 
      (when-let [state @state]
        (blip terminal state (get-in state [:fx :blip])))))
  ARequiresState
  (reset-state! [this new-state]
    (reset! state new-state)
    this)
  Object
  (toString [this]
    (format "BlipEffect terminal, state")))

(defn make-blip-effect
  [terminal cell-opts]
  (let [[vw vh]    (size terminal)]
    (BlipEffect. terminal (atom nil))))

;;; Queue cmds in a chan and foreard them to dst-chan on `(refresh)`.
(defrecord WrappedAnimatedTerminal [terminal cmds dst-chan]

           nil)
         (rain-transition advanced-cell old-cell))))))))

(defprotocol ACmdStream
  (stream [this]))

