   {:author "Jon Anthony"}
 
   (:require
-   [clojure.java.io          :as io]
-   [clojure.string           :as str]
-   [clojure.set              :as set]
-   [clojure.data.json        :as json]
+   [clojure.string :as str]
+   [clojure.set :as set]
+   [clojure.data.json :as json]
+   [clojure.java.io :as io]
    [clojure.tools.reader.edn :as edn]
-
-   [ring.middleware.defaults]
-   [ring.util.codec :as ruc]
-
-   [compojure.core     :as comp
-    :refer (defroutes GET POST)]
-
-   [compojure.route    :as route]
-   [hiccup.core        :as hiccup]
-
    [clojure.core.async :as async
     :refer (<! <!! >! >!! put! chan go go-loop alt! timeout alt! alts!!)]
 
-   [taoensso.timbre    :as timbre
+   [ring.middleware.defaults]
+   [ring.middleware.gzip :refer [wrap-gzip]]
+   [ring.middleware.cljsjs :refer [wrap-cljsjs]]
+   [ring.util.response :refer (resource-response content-type)]
+   #_[ring.util.codec :as ruc]
+
+   [compojure.core :as comp :refer (routes GET POST)]
+   [compojure.route :as route]
+
+   [hiccup.core :as hiccup]
+
+   [taoensso.timbre :as timbre
     :refer (tracef debugf infof warnf errorf)]
-   [taoensso.sente     :as sente]
-
-   [org.httpkit.server :as http-kit]
-   [taoensso.sente.server-adapters.http-kit :refer (sente-web-server-adapter)]
 
    ;; Watch tool config directory to automatically configure new tools
    [clojure-watch.core :refer [start-watch]]
 
-   ;; BinaryJS msg protocol
-   [aerial.msgpacket.binaryjs :as bjs :refer [newBinaryJsMsg]]
+   [com.rpl.specter :as sp]
+
+   [aerial.hanasu.server :as srv]
+   [aerial.hanasu.common :as com]
 
    [aerial.fs :as fs]
    [aerial.utils.misc :as aum]
-   [aerial.utils.io   :as aio]
+   [aerial.utils.io :as aio]
    [aerial.utils.string :as austr]
    [aerial.utils.coll :as coll :refer [in takev-until dropv-until ensure-vec]]
 

     [:hr]
     [:ul
      (map #(vector :li [:a {:href (fs/join resource-dir (args :dir) %)} %])
           files)]))
  )


(def dbg (atom {}))

(declare
 get-jobinfo
 get-toolinfo)

(defn htseq-file-get [args reqmap]
  (infof "Args %s\nParams %s" args (reqmap :params))

;;; Logging config
;;; (sente/set-logging-level! :trace) ; Uncomment for more logging



;;; ------------------------------------------------------------------------;;;
;;;                    Tool Config and  Watcher                             ;;;
;;; ------------------------------------------------------------------------;;;

(def tool-configs (atom nil))

(defn bind-env [f env]
  (let [val (env :value)
        done? (env :done)]
    (if done?
      env
      (let [val (f val)]
        (assoc env :value val :done? (or (nil? val) (= val :done)))))))

(defn read-tool-config [f]
  (infof "Tool update: %s" f)
  (binding [*ns* (find-ns 'aerobio.server)]
    (if (= "clj" (fs/ftype f))
      (let [cfg (-> f slurp clojure.core/read-string)
            func (cfg :func)]
        (if (and func (or (symbol? func) (list? func)))
          (assoc cfg :func (eval func) :src func)
          cfg))
      (-> f slurp (json/read-str :key-fn keyword)))))

(defn read-tool-configs
  []
  (let [wd (fs/pwd)
        srvdir (fs/join wd "services")
        _ (assert (fs/directory? srvdir)
                  (format "Services dir %s missing!!" srvdir))
        configs (fs/re-directory-files srvdir #"\.(clj|js)")
        toolinfo (reduce
                  (fn[M f]
                    (assoc M (-> f fs/basename (str/split #"\.") first)
                           (read-tool-config f)))
                  {} configs)]
    toolinfo))


(def _watcher (atom nil))

(defmulti
  ^{:doc "Dispatch dynamic tool config based on service file event"
    :arglists '([event filename])}
  config-tool
  (fn [event filename] event))


(defmethod config-tool :create
  [_ f]
  (swap! tool-configs
         assoc (-> f fs/basename (str/split #"\.") first)
         (read-tool-config f)))

(defmethod config-tool :modify
  [_ f]
  (swap! tool-configs
         assoc (-> f fs/basename (str/split #"\.") first)
         (read-tool-config f)))

(defmethod config-tool :delete
  [_ f]
  (swap! tool-configs
         dissoc  (-> f fs/basename (str/split #"\.") first)))

(defn stop-tool-watcher []
  (when-let [stopw @_watcher] (stopw)))

(defn start-tool-watcher []
  (stop-tool-watcher)
  (reset! _watcher
          (start-watch
           [{:path (fs/join (fs/pwd) "services")
             :event-types [:create :modify :delete]
             :bootstrap (fn[_](swap! tool-configs (fn[_] (read-tool-configs))))
             :callback config-tool
             :options {:recursive true}}])))

(defn get-toolinfo [toolname]
  (assert (@tool-configs toolname)
          (format "No such tool %s" toolname))
  (merge {:inputOption "" :options "" :args ""}
         (@tool-configs toolname)))


;;; ------------------------------------------------------------------------;;;
;;;                     Job Config and  Watcher                             ;;;
;;; ------------------------------------------------------------------------;;;

(def job-configs (atom nil))

(defn read-job-config [f]
  (infof "Job update: %s" f)
  (if (= "clj" (fs/ftype f))
    (let [cfg (-> f slurp edn/read-string)
          func (cfg :func)]
      (if (and func (or (symbol? func) (list? func)))
        (assoc cfg :func (eval func) :src func)
        cfg))
    (-> f slurp (json/read-str :key-fn keyword))))

(defn read-job-configs
  []
  (let [wd (fs/pwd)
        jobdir (fs/join wd "Jobs")
        _ (assert (fs/directory? jobdir)
                  (format "Jobs dir %s missing!!" jobdir))
        configs (fs/re-directory-files jobdir #"\.(clj|js)")
        jobinfo (reduce
                  (fn[M f]
                    (assoc M (-> f fs/basename (str/split #"\.") first)
                           (read-job-config f)))
                  {} configs)]
    jobinfo))


(def _job-watcher (atom nil))

(defmulti
  ^{:doc "Dispatch dynamic job config based on job file event"
    :arglists '([event filename])}
  config-job
  (fn [event filename] event))


(defmethod config-job :create
  [_ f]
  (swap! job-configs
         assoc (-> f fs/basename (str/split #"\.") first)
         (read-job-config f)))

(defmethod config-job :modify
  [_ f]
  (swap! job-configs
         assoc (-> f fs/basename (str/split #"\.") first)
         (read-job-config f)))

(defmethod config-job :delete
  [_ f]
  (swap! job-configs
         dissoc  (-> f fs/basename (str/split #"\.") first)))

(defn stop-job-watcher []
  (when-let [stopw @_job-watcher] (stopw)))

(defn start-job-watcher []
  (stop-job-watcher)
  (reset! _job-watcher
          (start-watch
           [{:path (fs/join (fs/pwd) "Jobs")
             :event-types [:create :modify :delete]
             :bootstrap (fn[_](swap! job-configs (fn[_] (read-job-configs))))
             :callback config-job
             :options {:recursive true}}])))

(defn get-jobinfo [jobname]
  (assert (@job-configs jobname)
          (format "No such job %s" jobname))
  (@job-configs jobname))


;;; ------------------------------------------------------------------------;;;
;;;                     Server communication database                       ;;;
;;; ------------------------------------------------------------------------;;;

(defn uuid [] (str (java.util.UUID/randomUUID)))

(defonce app-bpsize 100)
(defonce app-db (atom {:rcvcnt 0 :sntcnt 0}))

(defn update-adb
  ([] (com/update-db app-db {:rcvcnt 0 :sntcnt 0}))
  ([keypath vorf]
   (com/update-db app-db keypath vorf))
  ([kp1 vof1 kp2 vof2 & kps-vs]
   (apply com/update-db app-db kp1 vof1 kp2 vof2 kps-vs)))

(defn get-adb
  ([] (com/get-db app-db []))
  ([key-path] (com/get-db app-db key-path)))


;;; ------------------------------------------------------------------------;;;
;;;                      Server routes and handlers                         ;;;
;;; ------------------------------------------------------------------------;;;


(defn landing-handler [request]
  #_(infof "landing handler request: %s" request)
  (content-type
   {:status 200
    :body (io/input-stream (io/resource "public/index.html"))}
   "text/html"))


(defn upload-file [args reqmap]
  (println :ARGS args)
