(in-package :clog-tools)

(defun project-tree-select (panel item &key method)
  (unless (equal item "")
    (cond ((and (> (length item) 5)
                (equalp (subseq item (- (length item) 5)) ".clog"))
            (if (or (eq method :tab)
                 (and (not (eq method :here)) (or *open-external* *open-external-panels-always* )))
                (on-new-builder-panel-ext panel :open-file item) ;; need ext for both
                (on-new-builder-panel panel :open-file item)))
          (t
            (if (eq method :emacs)
                (swank:ed-in-emacs item)
                (if (or (eq method :tab)
                     (and (not (eq method :here)) *open-external*))
                    (on-open-file-ext panel :open-file item)
                    (progn
                      (let ((win (on-open-file panel :open-file item)))
                        (when *project-tree-sticky-open*
                          (when win
                            (set-geometry win
                                          :top (menu-bar-height win)
                                          :left *builder-left-panel-size*
                                          :height "" :width ""
                                          :bottom 5 :right 0)
                            (clog-ace:resize (window-param win))
                            (set-on-window-move win (lambda (obj)
                                                      (setf (width obj) (width obj))
                                                      (setf (height obj) (height obj))))))))))))))
(defun update-static-root (app)
  (setf *static-root*
        (merge-pathnames (if (equalp (current-project app) "clog")
                             "./static-root/"
                             "./www/")
                         (format nil "~A" (asdf:system-source-directory (current-project app)))))
  (when (static-root-display app)
    (setf (text-value (static-root-display app)) (format nil "static-root: ~A" *static-root*))))

(defun on-project-tree (obj &key project)
  (let ((app (connection-data-item obj "builder-app-data")))
    (unless *no-quicklisp*
      (when (uiop:directory-exists-p #P"~/common-lisp/")
        (pushnew #P"~/common-lisp/"
                 (symbol-value (read-from-string "ql:*local-project-directories*"))
                 :test #'equalp)))
    (when project
      (setf (current-project app) project))
    (if (project-tree-win app)
        (window-focus (project-tree-win app))
        (let* ((*default-title-class*      *builder-title-class*)
               (*default-border-class*     *builder-border-class*)
               (entry-point "")
               (win         (create-gui-window obj :title "Project Tree"
                                               :width *builder-left-panel-size*
                                               :has-pinner t
                                               :keep-on-top t
                                               :client-movement *client-side-movement*))
               (projects    (create-select (window-content win)))
               (panel       (create-panel (window-content win) :background-color :silver
                                          :style "text-align:center;"
                                          :class "w3-tiny"
                                          :height 27 :top 30 :left 0 :right 0))
               (load-btn    (create-button panel :content "no project" :style "height:27px;width:72px"))
               (load-np     (background-color load-btn))
               (run-btn     (create-button panel :content "run" :style "height:27px;width:67px"))
               (filter-btn  (create-button panel :content "filter" :style "height:27px;width:67px"))
               (asd-btn     (create-button panel :content "asd edit" :style "height:27px;width:67px"))
               (refresh-btn (create-button panel :content "&#8635;" :style "height:27px;width:22px"))
               (tree       (create-panel (window-content win)
                                         :class "w3-small"
                                         :overflow :scroll
                                         :top 60 :bottom 0 :left 0 :right 0)))
          (setf (project-tree-win app) win)
          (set-geometry win :top (menu-bar-height win) :left 0 :height "" :bottom 5 :right "")
          (set-on-click asd-btn (lambda (obj)
                                  (on-show-project obj)))
          (set-on-window-move win (lambda (obj)
                                    (setf (height obj) (height obj))))
          (set-on-window-close win (lambda (obj)
                                     (browser-gc obj)
                                     (setf (project-tree-win app) nil)))
          (setf (positioning projects) :absolute)
          (set-geometry projects :height 27 :width "100%" :top 0 :left 0 :right 0)
          (set-on-click (create-span (window-icon-area win) :content "&larr;&nbsp;" :auto-place :top)
                        (lambda (obj)
                          (declare (ignore obj))
                          (set-geometry win :top (menu-bar-height win) :left 0 :height "" :bottom 5 :right "")))
          (set-on-click filter-btn (lambda (obj)
                                     (declare (ignore obj))
                                     (if (equalp (text-value filter-btn)
                                                 "filter")
                                         (setf (text-value filter-btn) "filter off")
                                         (setf (text-value filter-btn) "filter"))))
          (set-on-click run-btn
            (lambda (obj)
              (let* ((*default-title-class*      *builder-title-class*)
                     (*default-border-class*     *builder-border-class*))
                (update-static-root app)
                (input-dialog obj "Run form:"
                              (lambda (result)
                                (when result
                                  (setf entry-point result)
                                  (setf clog:*clog-debug*
                                        (lambda (event data)
                                          (with-clog-debugger (panel :standard-output (stdout app)
                                                                     :standard-input (stdin app))
                                                              (funcall event data))))
                                  (capture-eval result
                                                :clog-obj        obj
                                                :capture-console nil
                                                :capture-result  nil
                                                :eval-in-package "clog-user")))
                                :default-value entry-point)
                (alert-toast obj "Static Root Set"
                             *static-root* :color-class "w3-yellow" :time-out 3))))
          (labels ((project-tree-dir-select (node dir)
                     (let ((filter (equalp (text-value filter-btn)
                                           "filter")))
                       (dolist (item (sort (uiop:subdirectories dir)
                                           (lambda (a b)
                                             (string-lessp (format nil "~A" a) (format nil "~A" b)))))
                         (unless (and (ppcre:scan *project-tree-dir-filter* (string-downcase (format nil "~A" item)))
                                      filter)
                           (create-clog-tree (tree-root node)
                                             :fill-function (lambda (obj)
                                                              (project-tree-dir-select obj (format nil "~A" item)))
                                             :indent-level (1+ (indent-level node))
                                             :visible nil
                                             :on-context-menu
                                               (lambda (obj)
                                                 (browser-gc obj)
                                                 (let* ((disp (text-value (content obj)))
                                                        (menu (create-panel obj
                                                                            :left (left obj) :top (top obj)
                                                                            :width (width obj)
                                                                            :class *builder-window-desktop-class*
                                                                            :auto-place :top))
                                                        (title (create-div menu :content disp))
                                                        (op    (create-div menu :content "Toggle open" :class *builder-menu-context-item-class*))
                                                        (opd   (create-div menu :content "Open in dir tree" :class *builder-menu-context-item-class*))
                                                        (ops   (create-div menu :content "Open in pseudo shell" :class *builder-menu-context-item-class*))
                                                        (opo   (create-div menu :content "Open in os" :class *builder-menu-context-item-class*))
                                                        (ren   (create-div menu :content "Rename directory" :class *builder-menu-context-item-class*))
                                                        (grp   (create-div menu :content "Search directory" :class *builder-menu-context-item-class*)))
                                                   (declare (ignore title op))
                                                   (mapcar (lambda (file-extension)
                                                             (set-on-click (create-div menu :content (getf file-extension :name) :class *builder-menu-context-item-class*)
                                                                           (lambda (obj)
                                                                             (destroy menu)
                                                                             (funcall (getf file-extension :func)
                                                                                      nil item (current-project app)
                                                                                      obj))
                                                                           :cancel-event t))
                                                           *file-extensions*)
                                                   (set-on-click menu (lambda (i)
                                                                        (declare (ignore i))
                                                                        (destroy menu)))
                                                   (set-on-click grp (lambda (i)
                                                                       (declare (ignore i))
                                                                       (on-file-search obj :dir item))
                                                                 :cancel-event t)
                                                   (set-on-click opd (lambda (i)
                                                                       (declare (ignore i))
                                                                       (on-dir-tree obj :dir item))
                                                                 :cancel-event t)
                                                   (set-on-click ops (lambda (i)
                                                                       (declare (ignore i))
                                                                       (on-shell obj :dir item))
                                                                 :cancel-event t)
                                                   (set-on-click ren (lambda (i)
                                                                       (declare (ignore i))
                                                                       (let* ((*default-title-class*      *builder-title-class*)
                                                                              (*default-border-class*     *builder-border-class*))
                                                                         (input-dialog obj (format nil "Rename ~A to?" disp)
                                                                                       (lambda (result)
                                                                                         (when result
                                                                                           (rename-file item (format nil "~A~A/" dir result))
                                                                                           (setf item (format nil "~A~A/" dir result))
                                                                                           (setf (text-value (content obj)) result)))
                                                                                       :title "Rename Directory")))
                                                                 :cancel-event t)
                                                   (set-on-click opo (lambda (i)
                                                                       (declare (ignore i))
                                                                       (open-file-with-os item))
                                                                 :cancel-event t)
                                                   (set-on-mouse-leave menu (lambda (obj) (destroy obj)))))
                                             :content (first (last (pathname-directory item))))))
                       (dolist (item (sort (uiop:directory-files (directory-namestring dir))
                                           (lambda (a b)
                                             (if (equal (pathname-name a) (pathname-name b))
                                                 (string-lessp (format nil "~A" a)
                                                               (format nil "~A" b))
                                                 (string-lessp (format nil "~A" (pathname-name a))
                                                               (format nil "~A" (pathname-name b)))))))
                         (unless (and (ppcre:scan *project-tree-file-filter* (string-downcase (file-namestring item)))
                                      filter)
                           (create-clog-tree-item (tree-root node)
                                                  :on-context-menu
                                                    (lambda (obj)
                                                      (browser-gc obj)
                                                      (let* ((disp (text-value (content obj)))
                                                             (menu (create-panel obj
                                                                                 :left (left obj) :top (top obj)
                                                                                 :width (width obj)
                                                                                 :class *builder-window-desktop-class*
                                                                                 :auto-place :top))
                                                             (title (create-div menu :content disp))
                                                             (op    (create-div menu :content "Open" :class *builder-menu-context-item-class*))
                                                             (oph   (create-div menu :content "Open this tab" :class *builder-menu-context-item-class*))
                                                             (opt   (create-div menu :content "Open new tab" :class *builder-menu-context-item-class*))
                                                             (ope   (create-div menu :content "Open emacs" :class *builder-menu-context-item-class*))
                                                             (opo   (create-div menu :content "Open os default" :class *builder-menu-context-item-class*))
                                                             (ren   (create-div menu :content "Rename" :class *builder-menu-context-item-class*))
                                                             (del   (create-div menu :content "Delete" :class *builder-menu-context-item-class*)))
                                                        (declare (ignore title op))
                                                        (mapcar (lambda (file-extension)
                                                                  (set-on-click (create-div menu :content (getf file-extension :name) :class *builder-menu-context-item-class*)
                                                                                (lambda (obj)
                                                                                  (destroy menu)
                                                                                  (funcall (getf file-extension :func)
                                                                                           item nil (current-project app)
                                                                                           obj))
                                                                                :cancel-event t))
                                                                *file-extensions*)
                                                        (set-on-click menu (lambda (i)
                                                                             (declare (ignore i))
                                                                             (destroy menu)))
                                                        (set-on-click oph (lambda (i)
                                                                             (declare (ignore i))
                                                                             (project-tree-select obj (format nil "~A" item) :method :here))
                                                                      :cancel-event t)
                                                        (set-on-click opt (lambda (i)
                                                                             (declare (ignore i))
                                                                             (project-tree-select obj (format nil "~A" item) :method :tab))
                                                                      :cancel-event t)
                                                        (set-on-click ope (lambda (i)
                                                                             (declare (ignore i))
                                                                             (project-tree-select obj (format nil "~A" item) :method :emacs))
                                                                      :cancel-event t)
                                                        (set-on-click opo (lambda (i)
                                                                             (declare (ignore i))
                                                                             (open-file-with-os item))
                                                                      :cancel-event t)
                                                        (set-on-click ren (lambda (i)
                                                                            (declare (ignore i))
                                                                            (let* ((*default-title-class*      *builder-title-class*)
                                                                                   (*default-border-class*     *builder-border-class*))
                                                                              (input-dialog obj (format nil "Rename ~A to?" disp)
                                                                                            (lambda (result)
                                                                                              (when result
                                                                                                (rename-file item (format nil "~A~A" (directory-namestring item) result))
                                                                                                (setf item (format nil "~A~A" (directory-namestring item) result))
                                                                                                (setf (text-value (content obj)) result)))
                                                                                            :title "Rename File")))
                                                                      :cancel-event t)
                                                        (set-on-click del (lambda (i)
                                                                            (let* ((*default-title-class*      *builder-title-class*)
                                                                                   (*default-border-class*     *builder-border-class*))
                                                                              (confirm-dialog i (format nil "Delete ~A?" disp)
                                                                                              (lambda (result)
                                                                                                (when result
                                                                                                  (uiop:delete-file-if-exists item)
                                                                                                  (destroy obj))))))
                                                                      :cancel-event t)
                                                        (set-on-mouse-leave menu (lambda (obj) (destroy obj)))))
                                                  :on-click (lambda (obj)
                                                              (project-tree-select obj (format nil "~A" item)))
                                                  :content (file-namestring item))))))
                   (load-proj (sel)
                     (setf (text-value load-btn) "working")
                     (setf (background-color load-btn) :yellow)
                     (handler-case
                         (progn
                           (projects-load (format nil "~A/tools" sel))
                           (update-static-root app))
                       (error ()
                         (projects-load sel)))
                     (setf (text-value load-btn) "loaded")
                     (setf (background-color load-btn) load-np)
                     (window-focus win))
                   (on-change (obj)
                     (declare (ignore obj))
                     (setf (text tree) "")
                     (browser-gc tree)
                     (let* ((sel (value projects)))
                       (setf entry-point "")
                       (cond ((or (equal sel "") (equal sel "NIL"))
                               (setf (text-value load-btn) "no project")
                               (setf (advisory-title load-btn) "Choose project in drop down")
                               (setf (background-color load-btn) load-np)
                               (setf (current-project app) nil))
                             (t
                              (setf (text-value load-btn) "working")
                              (setf (background-color load-btn) :yellow)
                              (setf (advisory-title load-btn) "")
                              (let* ((system (asdf:find-system sel nil))
                                     (root   (when system (asdf:system-source-directory system)))
                                     (dir    (directory-namestring (uiop:truename* root))))
                                (cond (root
                                        (setf (text-value load-btn) "not loaded")
                                        (setf (advisory-title load-btn) "Click to load")
                                        (setf (background-color load-btn) :tomato)
                                        (setf (current-project app) sel)
                                        (setf (current-project-dir app) root)
                                        (create-clog-tree tree
                                                          :fill-function (lambda (obj)
                                                                           (project-tree-dir-select obj dir))
                                                          :node-html "&#129422;" ; lizard
                                                          :content root
                                                          :on-context-menu
                                                          (lambda (obj)
                                                            (browser-gc obj)
                                                            (let* ((disp sel)
                                                                   (item root)
                                                                   (menu (create-panel obj
                                                                                       :left (left obj) :top (top obj)
                                                                                       :width (width obj)
                                                                                       :class *builder-window-desktop-class*
                                                                                       :auto-place :top))
                                                                   (title (create-div menu :content disp))
                                                                   (op    (create-div menu :content "Toggle open" :class *builder-menu-context-item-class*))
                                                                   (opd   (create-div menu :content "Open in dir tree" :class *builder-menu-context-item-class*))
                                                                   (ops   (create-div menu :content "Open pseudo shell" :class *builder-menu-context-item-class*))
                                                                   (opa   (create-div menu :content "Open in ASDF browser" :class *builder-menu-context-item-class*))
                                                                   (opr   (create-div menu :content "Open REPL" :class *builder-menu-context-item-class*))
                                                                   (opo   (create-div menu :content "Open in os" :class *builder-menu-context-item-class*))
                                                                   (grp   (create-div menu :content "Search directory" :class *builder-menu-context-item-class*)))
                                                              (declare (ignore title op))
                                                              (set-on-click menu (lambda (i)
                                                                                   (declare (ignore i))
                                                                                   (destroy menu)))
                                                              (set-on-click opd (lambda (i)
                                                                                  (declare (ignore i))
                                                                                  (on-dir-tree obj :dir item))
                                                                            :cancel-event t)
                                                              (set-on-click grp (lambda (i)
                                                                                  (declare (ignore i))
                                                                                  (on-file-search obj :dir item))
                                                                            :cancel-event t)
                                                              (set-on-click ops (lambda (i)
                                                                                  (declare (ignore i))
                                                                                  (on-shell obj :dir item))
                                                                            :cancel-event t)
                                                              (set-on-click opa (lambda (i)
                                                                                  (declare (ignore i))
                                                                                  (on-new-asdf-browser obj))
                                                                            :cancel-event t)
                                                              (set-on-click opr (lambda (i)
                                                                                  (declare (ignore i))
                                                                                  (on-repl obj))
                                                                            :cancel-event t)
                                                              (set-on-click opo (lambda (i)
                                                                                  (declare (ignore i))
                                                                                  (open-file-with-os item))
                                                                            :cancel-event t)
                                                              (set-on-mouse-leave menu (lambda (obj) (destroy obj))))))
                                        (let ((already (asdf:already-loaded-systems)))
                                          (if (member sel already :test #'equalp)
                                              (progn
                                                (setf (text-value load-btn) "loaded")
                                                (setf (advisory-title load-btn) "Click to unload")
                                                (setf (background-color load-btn) load-np))
                                              (progn
                                                (setf (text-value load-btn) "not loaded")
                                                (setf (advisory-title load-btn) "Click to load")
                                                (setf (background-color load-btn) :tomato))))
                                        (setf entry-point (format nil "(~A)"
                                                                  (or (asdf/system:component-entry-point (asdf:find-system sel))
                                                                      ""))))
                                      (t
                                        (setf entry-point "")
                                        (setf (current-project app) nil)
                                        (setf (text-value load-btn) "no project")
                                        (setf (advisory-title load-btn) "Choose project in drop down")
                                        (setf (background-color load-btn) :load-np))))))))
                   (fill-projects ()
                     (setf (text projects) "")
                     (let ((pl (projects-list-local-systems)))
                       (when pl
                         (dolist (n (sort pl #'string-lessp))
                           (add-select-option projects n n :selected (equalp n (current-project app)))
                           (when (equalp n (current-project app))
                             (on-change (current-project app))))))
                     (add-select-option projects "" "Select Project" :selected (not (current-project app)))))
            (set-on-click load-btn (lambda (obj)
                                     (declare (ignore obj))
                                     (cond ((equalp (text-value load-btn) "loaded")
                                             (asdf:clear-system (value projects))
                                             (setf (text-value load-btn) "not loaded")
                                             (setf (advisory-title load-btn) "Click to load")
                                             (setf (background-color load-btn) :tomato))
                                           ((equalp (text-value load-btn) "not loaded")
                                            (setf (advisory-title load-btn) "Click to unload")
                                            (load-proj (value projects))))))
            (set-on-click refresh-btn (lambda (obj)
                                        (declare (ignore obj))
                                        (fill-projects)))
            (fill-projects)
            (set-on-change projects #'on-change))))))
