;;;; cl-phantasmagoria.lisp

;;;Requires:
;;; #:cl-ppcre #:usocket

;;;(in-package #:cl-phantasmagoria)

(defvar *threads* (list))
(defvar *last-post* 0)
(defvar *port* 8070)
(defvar *domain* "localhost")

(defun create-server ()
 (let ((socket (usocket:socket-listen "127.0.0.1" *port*  :reuse-address t)))
   (unwind-protect
       (loop for connection = (usocket:socket-accept socket :element-type 'character)
             do (unwind-protect
                    (progn
                      (gopher-respond connection)
                      (force-output (usocket:socket-stream connection)))
                  (progn
                    (when connection
                      (usocket:socket-close connection)))))
     (when socket usocket:socket-close socket))))

(defun add-thread (name)
 (push (list (incf *last-post*) name "") *threads*))

(defun filter-posts (lambda)
 (delete-if lambda *threads*))

(defun blank-from (cutoff postno)
 (setf (third (assoc postno *threads*))
       (subseq (third (assoc postno *threads*))
               0
               (nth-value
                0
                (cl-ppcre:scan
                 cutoff
                 (third (assoc postno *threads*)))))))

(defun append-to-thread (thrno words)
 (setf (third (assoc thrno *threads*))
       (format nil "~a~%~%~a:~%~a~%"
               (third (assoc thrno *threads*))
               (incf *last-post*)
               words)))

(defun print-gophermap (stream)
 (format stream "phantasmagoria~%~%")
 (format stream "~a~a  ~a      ~a      ~a~%"
         7 "add thread" "add thread" *domain* *port*)
 (format stream "~a~a  ~a      ~a      ~a~%"
         7 "respond thread" "respond thread" *domain* *port*)

 (dolist (s *threads*)
   (format stream "0~a: ~a     ~a      ~a      ~a~%"
           (first s) (second s) (first s) *domain* *port*)))

(defun print-thread (thrno stream)
 (format stream "phantasmagoria~%~a: ~a~%~a"
         thrno
         (second (assoc thrno *threads*))
         (third (assoc thrno *threads*))))

(defun gopher-respond (connection)
 "lynx gopher://localhost:8070/1/"
 (let* ((safety 0)
       (terms (mapcar
          (lambda (x) (coerce x 'string))
          (loop
           for done = nil
           collect
           (loop
            for y = (read-char (usocket:socket-stream connection))
            do (when (char= y #\return) (setf done t))
            do (when (> (incf safety) 400)
                 (setf done t)
                 (return))
            while (not (or (char= y #\tab) (char= y #\return)))
            collect y)
           while (not done)))))

   (cond ((and (= 1 (length terms))
               (string= "/" (car terms)))
          (print-gophermap (usocket:socket-stream connection)))

         ((and (= 1 (length terms))
               (assoc (parse-integer (first terms))
                      *threads*))
          (print-thread (parse-integer (first terms))
                        (usocket:socket-stream connection)))

         ((and (= 2 (length terms))
               (string= "add thread" (car terms)))
          (add-thread (second terms))
          (print-thread *last-post* (usocket:socket-stream connection)))

         ((and (= 2 (length terms))
               (string= "respond thread" (car terms)))
          (let* ((idx (parse-integer (subseq
                              (second terms)
                              0
                              (nth-value
                              0
                              (cl-ppcre:scan
                               '(:POSITIVE-LOOKAHEAD ":")
                               (second terms)))))))
            (append-to-thread idx
                            (second terms))
            (print-thread idx
                          (usocket:socket-stream connection))))

         (t (error ":-(")))))