(ns Path_Finder.pathfinder
  [:require [clojure.reflect :as r]
            [clojure.java.io :as io]]
  [:use      Path_Finder.drawing
             Path_Finder.tinycell]
  [:use     [clojure.pprint :only [print-table pprint]]]
  [:import   PathFinder
             ij.plugin.ImageCalculator])
  
; (use '[Path_Finder.main_ :only [params orig_str mem_str vtx_str]])

; Label vertices, find center of mass, create image with labeled vertex centers
(defn get-vertex-list [pthfdr vtx_str threshold]
  (def vimg (ij.IJ/openImage vtx_str))
  (def vproc (.. vimg getProcessor (convertToShort true)))
  (.threshold vproc threshold)
  (.setProcessor vimg vproc)
  (def vtx (label-vertices vimg))
  (def vertex_list (.getBlobCenters pthfdr vtx))
  (.close vtx)
  (.close vimg)
  vertex_list)

(defn resultname [orig_str]
  (let [f1      (io/file orig_str)
        base    (.getParent f1)
        oldname (.getName f1)
        sep     java.io.File/separator
        newbase (str base sep "results")]
    (.mkdir (io/file newbase))
    (str newbase sep oldname)))

(defn sum-imgs
   "Images should be same size, same bit depth. no error is thrown if not same
   bit-depth!"
 [ip1 ip2] 
  (.. (ImageCalculator.) (run "Add create" ip1 ip2)))

(def pthfdr  (PathFinder.))

; Take parameter map and filenames. Saves image stack to results/ directory.

(defn do-everything [params
                     mem_str
                     vtx_str
                     orig_str]

  (in-ns 'Path_Finder.pathfinder)
  ; open images, 
  ; (def params params)
  ; (def mem_str mem_str)
  ; (def vtx_str vtx_str)
  ; (def orig_str orig_str)
  (def orig    (ij.IJ/openImage orig_str))
  (def mem1    (ij.IJ/openImage mem_str))
  (def vtx     (ij.IJ/openImage vtx_str))
  (def mem     (sum-imgs mem1 vtx))
  (def vtxproc (.. vtx getProcessor (convertToShort true)))
  (def memproc (.. mem getProcessor (convertToShort true)))
  (.setProcessor vtx vtxproc)
  (.setProcessor mem memproc)
  
  (apply-f-to-img mem (params :metricfunc))
  (def vertex_list (get-vertex-list pthfdr vtx_str (params :vthresh)))
  (def simple_verts (make-16bit-img "Pixel Vertices" "Grays" (.getWidth mem) (.getHeight mem)))
  (doseq [[i px] (map list (range) vertex_list)
           :let [proc (.getProcessor simple_verts)]]
    (.set proc (aget px 0) (aget px 1) (inc i)))

  ; Construct voronoi tessellation, list of all paths and their scores
  (def vor       (make-16bit-img "Voronoi Tessellation" "glasbey" (.getWidth mem) (.getHeight mem)))
  ; `conn` updates `vor` in-place)
  (def conn      (.buildVoronoiImageAndConnections pthfdr vertex_list mem vor (params :distc) (params :mpixcutoff)))
  ; (def conn      (.buildVoronoiImageAndConnections pthfdr vertex_list mem vor (params :distc) 64880))
  ; (def conn      (.buildVoronoiImageAndConnections pthfdr vertex_list mem vor (params :distc) 64225))
  ; (def conn      (.buildVoronoiImageAndConnections pthfdr vertex_list mem vor (params :distc) 65210))
  (def edges     (get-path-edges vertex_list conn))
  (def end_links (filter identity  ; removes nil's from list
                   (for [[i j start end] edges] 
                     (.buildPath pthfdr start end mem vor (* 2 (params :distc))))))
  (def paths     (vec (for [e end_links] (.getPath pthfdr e))))
  (def scores    (vec (for [p paths] (qualityscore p))))
  (def allids    (range (count paths)))
  (defn paths_and_scores [ids]
    (map (fn [id] [(paths id) (scores id)]) ids))
  ;; (def vtxmap  (get-vtxmap path_tuple))
  (def vtxmap  (get-vtxmap-2 scores edges))

(comment
  ; path_tuple is lazy, but non computed twice because of clojure's auto-memoization?)
  ; must use vec to lookup paths by id)
  (def path_tuple  (get-pathinfo-from-edges pthfdr edges mem vor (params :distc)))
  (def paths   (vec (map first path_tuple)))
  (def scores  (vec (map last  path_tuple)))
)

  ; plot result before any cutoffs
  (def path_img  (make-16bit-img "Paths" "blue orange icb" (.getWidth mem) (.getHeight mem)))
  (def path_proc (.getProcessor path_img))
  (draw-paths (paths_and_scores allids) path_proc)

  ; calculate degree of each vertex
  ; get vertex_list of with only degree 0 | 1
  ; continue building voronoi tesselation with new cutoff and ONLY vertex-list vertices

  ;; NOT IMPLEMENTED YET

(comment
  ; ; a list of path_ids that pass degree_cutoff
  (def goodids1 (make-good-path-list vtxmap (params :degc)))

  ; plot results of degree cutoffs 
  (def deg_cut_img  (make-16bit-img "Paths" "blue orange icb" (.getWidth mem) (.getHeight mem)))
  (def deg_cut_proc (.getProcessor deg_cut_img))
  (draw-paths (paths_and_scores allids) deg_cut_proc)
)
  
  ; ; ; Small cell cutoff
  ; ; ; first time removes ≈ 400
   (def bad_ids_1 (remove-tiny-cells
              simple_verts
                      path_img
                      allids
                      paths
                      scores
                      (params :minarea)
            (params :cutoff)))
   (def path_img_2  (make-16bit-img "path_img_2" "blue orange icb" (.getWidth mem) (.getHeight mem)))
   (def path_proc_2 (.getProcessor path_img_2))
   (def goodids2 (clojure.set/difference (set allids) bad_ids_1))
   (draw-paths (paths_and_scores goodids2) path_proc_2)

  ; 2nd time removes ≈ 50
   (def bad_ids_2 (remove-tiny-cells
              simple_verts
                      path_img_2
                      goodids2
                      paths
                      scores
                      (params :minarea)
            (params :cutoff)))
   (def path_img_3  (make-16bit-img "path_img_3" "blue orange icb" (.getWidth mem) (.getHeight mem)))
   (def path_proc_3 (.getProcessor path_img_3))
   (def goodids3 (clojure.set/difference (set allids) bad_ids_1 bad_ids_2))
   (draw-paths (paths_and_scores goodids3) path_proc_3)

  ; ; 3rd times the charm
   (def bad_ids_3 (remove-tiny-cells
              simple_verts
                      path_img_3
                      goodids2
                      paths
                      scores
                      (params :minarea)
            (params :cutoff)))
   (def path_img_4  (make-16bit-img "path_img_4" "blue orange icb" (.getWidth mem) (.getHeight mem)))
   (def path_proc_4 (.getProcessor path_img_4))
   (def goodids4 (clojure.set/difference (set allids) bad_ids_1 bad_ids_2 bad_ids_3))
   (draw-paths (paths_and_scores goodids4) path_proc_4)
   
  ; todo:
  ; given the resulting paths, sort out vertices with degree 0 and 1 
  ; loop 
 
  ; ---- Final Segmentation After Bad-edge-removal ----
  (def final (get-cell-img path_img_4))
  ; (def final (get-cell-img path_img_2))

  ; ---- Scale The Resulting Paths Back Up To Original Size And Overlay On Orig ----
  (def scaled_up_img (get-scaled-up-img orig mem (paths_and_scores goodids4)))
  ; (.. scaled_up_img getProcessor invert)
  (def scaled_cells (get-cell-img scaled_up_img))
  (def proc (.. scaled_cells getProcessor (convertToShort false)))
  (.. scaled_cells (setProcessor proc))
  (.show scaled_cells)
  (def scaled_stack (.getStack scaled_cells))
  (.addSlice scaled_stack "Original" (.getProcessor orig))
  (.addSlice scaled_stack "Paths" (.. scaled_up_img getProcessor (convertToShort true)))
  (.setStack scaled_cells scaled_stack)
  
  (ij.IJ/save scaled_cells (clojure.string/replace (resultname orig_str) ".tif" "_scaled.tif"))

(comment
  ; ---- Find Overlap Points (after Removing Small Cells) ----
  (def overlap_points (get-overlap-points pthfdr (paths_and_scores goodids3) path_proc_3 (params :mpvc)))
  (ij.IJ/log (str "There are " (count overlap_points) " overlap points."))
  ;; (def s1 (set (map #(into [] %) vertex_list)))
  ;; (def s2 (set (map #(into [] %) overlap_points)))
  ; add overlap points to simple_verts
  (doseq [px overlap_points :let [proc (.getProcessor simple_verts)]]
    (.set proc (aget px 0) (aget px 1) 12000))
)
 
  ; ---- Make Downscaled Original ----
  (def origproc (.getProcessor orig))
  (def origproc_small (.resize origproc (.getWidth mem) (.getHeight mem) true))

  ; ---- Plot Everything In One Stack ----
  (def pathStack (.getStack simple_verts))
  (.addSlice pathStack "Original" origproc_small)
  (.addSlice pathStack "Mem Class" (.getProcessor mem))
  (.addSlice pathStack "Voronoi" (.getProcessor vor))
  (.addSlice pathStack "Initial Path Scores" path_proc)
  ; (.addSlice pathStack "After degree cutoff" deg_cut_proc)
  (.addSlice pathStack "Small Cells Removed 2" path_proc_2)
  (.addSlice pathStack "Small Cells Removed 3" path_proc_3)
  (.addSlice pathStack "Small Cells Removed 4" path_proc_4)
  (.addSlice pathStack "Segmented" (.. final getProcessor (convertToShort false)))
  (.setStack simple_verts pathStack)
  (ij.IJ/run "Make Composite" "display=Composite")
  (ij.IJ/save simple_verts (clojure.string/replace (resultname orig_str) ".tif" "_stackfinal.tif"))

  ; ; Now iterate on the path_proc_3
  ; (def path_img_5  (make-16bit-img "path_img_3" "blue orange icb" (.getWidth mem) (.getHeight mem)))
  ; (def path_proc_5 (.getProcessor path_img_5))
  ; [45000 45000 30250 25625 5312] ; reasonable
  ; (def goodids1_2 (make-good-path-list vtxmap [55000 43000 35000 31000 5000]))
  ; (def goodids5 (clojure.set/difference goodids1_2 bad_ids)) ; bad_ids_2 bad_ids_3))
  ; (redraw-paths (paths_and_scores goodids5) path_proc_5)
  ; (ij.IJ/run "Brightness/Contrast...")
  ; (ij.IJ/run path_img_5 "Enhance Contrast" "saturated=0.35")
  ; (def pathStack2 (.getStack path_img_5))
  ; (.addSlice pathStack2 "Original" origproc_small)
  ; (.setStack path_img_5 pathStack2)
  ; (ij.IJ/run "Make Composite" "display=Composite")
  ; IJ.run("LUT... ", "open=/Users/colemanbroaddus/Desktop/PKS_Work/MyersLab/tests/PaperImages/Paths.lut");

  ; ---- TEAR EVERYTHING DOWN ----
  (ij.IJ/run "Close All" "")
)