#!/usr/bin/sbcl --script
(require "asdf")

(load "config.lsp")

(defvar *apollo-path* (merge-pathnames ".apollo/" (user-homedir-pathname)))
(defvar *default-storage-path* (merge-pathnames "storage/" *apollo-path*))
(defvar *config-path* (merge-pathnames "config" *apollo-path*))
(defvar *default-note-path* (merge-pathnames "general/" *default-storage-path*))

(defun print-info ()
    (format t "Usage: apollo [OPTIONS] [ARGS...]~%")
    (format t "Try 'apollo help' for more information~%")
)

(defun print-help ()
    (format t "Usage: apollo [OPTIONS] [ARGS...]~%")
    (format t "Create and manage notes~%")
    (format t "Example: apollo note mywork.txt work programming~%")
    (format t "Note is just text file, nothing more. All notes stored in storage~%")
    (format t "You can create your own storage, or use default one - ~~/.apollo/storage~%")
    (terpri)
    (format t "Options:~%")
    (format t "init                 ~T Initializes storage and config that will be used to store notes and settings~%")
    (format t "note [NAME] [TAG...] ~T Creates a note with possible tag~%")
    (format t "open [NAME]          ~T Opens a note by name~%")
    (format t "list [TAG...]        ~T Lists notes by tags~%")
    (format t "grep [TEXT] [TAG...] ~T Grep by text, you can use it with tags~%")
)

(defun create-config (path-to-config storage-path)
    (with-open-file (stream path-to-config :direction :output
                                           :if-does-not-exist :create
                                           :if-exists :overwrite)
        (file-position stream :start)
        (format stream "storage=~A~%" storage-path)
    )
)

(defun concatenate-tags-do (tags concatenated-tag)
    (if (null tags)
        concatenated-tag
        (
            progn
            (if (cdr tags)
                (setf concatenated-tag (concatenate 'string (concatenate 'string concatenated-tag (string (car tags))) "_"))
                (setf concatenated-tag (concatenate 'string concatenated-tag (string (car tags))))
            )
            (concatenate-tags-do (cdr tags) concatenated-tag)
        )
    )
)

(defun concatenate-tags (tags)
    (let
        (
            (concatenated-tag nil) 
        )
        (setf concatenated-tag (concatenate-tags-do tags concatenated-tag))
        concatenated-tag
    )
)

(defun init ()
    (ensure-directories-exist *apollo-path*)
    (ensure-directories-exist *default-storage-path*)
    (ensure-directories-exist *default-note-path*)
    (unless (probe-file *config-path*)
        (create-config *config-path* *default-storage-path*)
    )
)

(defun create-note (args)
    (concatenate-tags (cdr args))
    (when (car args)
        (let*
            (
                (editor (uiop:getenv "EDITOR"))
                (note-name (car args))
                (tag-name (car (cdr args)))
                (storage-path (apollo-config::get-config-value "storage"))
                (path-to-note (merge-pathnames (string note-name) (merge-pathnames "general/" storage-path)))
            )
            (ensure-directories-exist path-to-note)
            (when (>= (length tag-name) 1)
                (let
                    (
                        (tag-path (merge-pathnames (concatenate 'string (concatenate-tags (cdr args)) "/") storage-path))
                    )
                    (ensure-directories-exist tag-path)
                    (setf path-to-note (merge-pathnames tag-path (string note-name)))
                )
            )
            (format t "~A~%" path-to-note)
            (uiop:run-program (list editor (namestring path-to-note)) :output :interactive 
                                                                      :input  :interactive)
        )
    )
)

(defun find-note-do (files note-name)
    (when files
        (if (string= (file-namestring (car files)) (string (car note-name)))
            (car files)
            (find-note-do (cdr files) note-name)
        )
    )
)

(defun find-note (tag-directory note-name)
    (let
        (
            (files (directory (merge-pathnames tag-directory "*")))
            (found-note nil)
        )
        (when files
            (setf found-note (find-note-do files note-name))
            found-note
        )
    )
)

(defun open-note-do (note-name found-notes directories)
    (let
        (
            (notes nil)
        )
        (when (cdr directories)
            (let
                (
                    (found-note (find-note (car (cdr directories)) note-name))
                )
                (when found-note
                    (setf notes (append found-notes (list (namestring found-note))))
                )
                (open-note-do note-name found-notes (cdr directories))
            )
        )
        notes
    )
)

(defun open-note (note-name)
    (let*
        (
            (storage-path (apollo-config::get-config-value "storage"))
            (found-notes nil)
            (storage-wildcard (merge-pathnames (apollo-config::get-config-value "storage") "/*"))
            (tags-directories (directory storage-wildcard))
            (editor (uiop:getenv "EDITOR"))
        )
        (setf found-notes (open-note-do note-name nil (cdr tags-directories)))
        (format t "~A~%" found-notes)
        (if found-notes
            (uiop:run-program (list editor (namestring (car found-notes))) :output :interactive 
                                                                     :input  :interactive)
            (format t "No found notes~%")
        )
    )
)

(defun parse-arguments (args)
    (dolist (argument args)
        (cond
            ((string= argument "help") (print-help))
            ((string= argument "init") (init))
            ((string= argument "note") (create-note (cdr args)))
            ((string= argument "open") (open-note (cdr args)))
        )
    )
)

(defun main ()
    (let*   
        (
            (args (cdr *posix-argv*))
            (str (car args))
        )
        (if args 
            (parse-arguments args)
            (print-info)
        )
    )
)

;(main)
(sb-ext:save-lisp-and-die "apollo" :toplevel #'main :executable t :compression 9)
