;;;; src/main.lisp
;;;;
;;;; Main App Definition And Entry Point
;;;;

(in-package :cl-user)

(defpackage #:cl-deck-builder2
  (:use #:cl
        #:cl-deck-builder2.db
        #:cl-deck-builder2.draw
        #:cl-deck-builder2.toolkit
        #:cl-deck-builder2.models
        ;;#:cl-deck-builder2-test
        )
  (:local-nicknames
   (#:v #:org.shirakumo.verbose))
  (:import-from :cl-deck-builder2.config
                :config
                :*app-log-file*)
  (:import-from :clack
                :clackup)
  (:export
   :start
   :stop
   :my/start
   :main)
  (:documentation "The main package for the deck builder project.

This package exports the START and STOP functions, as well as the convenience function MY/START."))

(in-package :cl-deck-builder2)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar *appfile-path*
  (asdf:system-relative-pathname :cl-deck-builder2 #P"app.lisp")
  "The application path we pass to CLACK:CLACKUP.")

(defvar *clack-handler* nil
  "The CLACK handler currently running. NIL if there is no server running.")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun start (&rest args &key server port debug &allow-other-keys)
  "Start the app. If the app is already running, raise an ERROR."
  (declare (ignore server port debug))
  (when *clack-handler*
    (restart-case (error "Server is already running.")
      (restart-server ()
        :report "Restart the server"
        (stop))))
  (v:info :main "CLACKUP")
  (setf *clack-handler* (apply #'clackup *appfile-path* args)))

(defun stop ()
  "Stop the app, if it is running."
  (v:info :main "CLACK:STOP")
  (prog1
      (clack:stop *clack-handler*)
    (setf *clack-handler* nil)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun my/start (&rest args)
  "Convenience function for invoking RESTART-SERVER in the case that the server is already running."
  (handler-bind
      ((simple-error (lambda (c)
                       (format t "~A~&Invoking RESTART-SERVER." c)
                       (invoke-restart 'restart-server))))
    ;; TODO Multiple Addresses?
    ;; TODO Put it behind NGINX?
    ;; TODO sslh?
    (apply #'start args)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Loader Customize
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defmacro start-helper (service-name &body body)
  `(progn
     (v:info :app "~&Starting ~a ...~&" ,service-name)
     (if ,@body
         (v:info :app "~&... started.~&")
         (v:info :app "~&... failed?~&"))))

;; Initialize Logging
;;
;; (v:output-here *standard-output*)
;;
(defun main ()
  (v:output-here (open *app-log-file* :direction :output
                                      :if-does-not-exist :create
                                      :if-exists :append))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  (v:start v:*global-controller*)

  (start-helper "slynk server"
                (apply #'slynk:create-server (config :slynk)))

  (v:info :app "~&Constructing database...~&")
  ;; (apply #'create-table *class-list*)
  (ensure-tables-exist)
  (v:info :app "~&...complete~%")

  (start-helper "web app"
    (apply #'my/start (config :server)))

  (start-helper "WebSocket Server Side Client"
                (handler-case
                    (apply #'cl-deck-builder2.web::make-chat-client (config :websocket))
                  (usocket:connection-refused-error () nil)
                  (usocket:connection-reset-error () nil))))
