(in-package :com.tee-it-up-golf.web)

;; Application

(defclass golf-admin-application (golf-application secure-application-mixin)
  ()
  (:default-initargs
   :insecure-components '(error-message)
    ;:login-component 'user-login-window
    ))

(defmethod application-find-user ((app golf-admin-application) username)
  (golf-auth::find-user username))

(defmethod application-check-password ((app golf-admin-application) user pass)
  (golf-auth::check-password user pass))

(defcomponent golf-user-login (user-login)
  ())

(defcomponent golf-user-login-window (golf-window)
  ()
  (:default-initargs
      :title "Authorization Required"
    :body (make-instance 'golf-user-login)))

;; Chooser

(defcomponent admin-show-chooser (show-chooser)
  ()
  (:default-initargs :viewer-class 'admin-show-viewer))

(defcomponent ranged-admin-show-chooser (admin-show-chooser
					 standard-ranged-show-list)
  ())

(defmethod render-controls :after ((self admin-show-chooser))
  (let ((show (viewer-item self)))
    (component-controls
      ("Edit" (call 'show-editor :show show))
      ("Delete" (progn
		  (elephant:drop-instances (list show))
		  (setf (page-offset self) (page-offset self)))))))


;; Individual Viewer

(defcomponent admin-show-viewer (full-show-viewer)
  ())

(defmethod render-show :after ((viewer admin-show-viewer) (show radio-show))
  (<:p (awhen (audio-data show)
	 (<:as-html (format nil "Type: ~S" (audio-file-type it))))))

(defmethod render-controls :after ((self admin-show-viewer))
  (let ((show (viewer-item self)))
    (component-controls ("Edit" (call 'show-editor :show show))
			("Play" (play-file self show))
			("Refresh" (refresh-component self)))))

;; Show Editor

(define-html-form show-editor (file-upload-form)
    ((title (string-field :input-size 80
			  :validators (make-validators 'not-empty-validator)))
     (date  mdy-date-field)
     (audio-file  file-upload-field)
     (transcript (textarea-field :rows 30 :cols 80)))
    ((show :initarg :show :accessor radio-show :initform nil)))

(defmethod initialize-instance :after ((editor show-editor) &rest keys)
  (declare (ignore keys))
  (if-bind show (radio-show editor)
    (with-slots (title date audio-file transcript)
	editor
      (setf (value title) (show-title show))
      (awhen (show-date show) (setf (value date) it))
      (setf (value transcript) (transcript show)))
    (setf (value (cdr (assoc 'date (form-fields editor))))
	  (get-universal-time))))

(defun set-audio-file (show content-type body-stream)
  ;; with IOlib no-file -> empty file stream!
  (if (> (file-length body-stream) 0)
      (let ((file (make-instance 'audio-file)))
	(setf (audio-data file) body-stream)
	(setf (audio-file-type file) content-type)
	(setf (audio-data show) file))))

(defmethod/cc process-form ((editor show-editor))
  (update-show editor))

(defaction update-show ((editor show-editor))
  (let ((show (or (radio-show editor) (make-instance 'radio-show))))
    (with-slots (title date audio-file transcript)
	editor
      (setf (show-date show) (value date))
      (setf (show-title show) (value title))
      (if (value audio-file)
	  (set-audio-file show (cdr (assoc "Content-Type"
					   (mime-part-headers (value audio-file))
					   :test #'string=))
			  (mime-part-body (value audio-file))))
      (setf (transcript show) (value transcript))))
  (answer t))


;; User Management

(defcomponent user-admin-tool ()
  ())

(defmethod render ((self user-admin-tool))
  (<:ul
   (<:li (<ucw:a :action (call 'password-change-tool :user (session-user))
		 "Change your password"))
   (<:li (<ucw:a :action (call 'user-list) "Manage Users"))))

(defclass fields-not-equal (form-validator)
  ((field-1 :initarg :field-1)
   (field-2 :initarg :field-2)
   (equal-predicate :initarg :equalp :initform #'equalp)))

(defmethod ucw::validate ((form simple-form) (validator fields-not-equal))
  (with-slots (field-1 field-2 equal-predicate) validator
    (funcall equal-predicate
	     (value (slot-value form field-1))
	     (value (slot-value form field-2)))))

(define-html-form password-change-tool ()
    ((current-password (password-field
			:validators (make-validators 'not-empty-validator)))
     (new-password-1 (password-field
		      :validators (make-validators 'not-empty-validator)
		      :initially-validate nil))
     (new-password-2 (password-field
		      :validators (make-validators 'not-empty-validator)
		      :initially-validate nil)))
    ((user :initarg :user))
    (:validators (fields-not-equal
		  :field-1 'new-password-1
		  :field-2 'new-password-2
		  :message "New passwords do not match")
		 (user-password-correct
		  :field 'current-password
		  :message "Current password is incorrect")))

(defclass user-password-correct (form-validator)
  ())

(defmethod ucw::validate ((form password-change-tool)
			  (validator user-password-correct))
  (with-slots (user) form
    (golf-auth:check-password user (client-value
				    (slot-value form
						(slot-value validator 'field))))))

(defmethod render :before ((self password-change-tool))
  (<:h1 (<:format "Change password for ~A"
		 (golf-auth:username (slot-value self 'user)))))

(defmethod/cc process-form ((self password-change-tool))
  (with-slots (user current-password new-password-1 new-password-2)
      self
    (cond ((and (golf-auth:check-password user (value current-password))
		(string= (value new-password-1) (value new-password-2)))
	   (setf (golf-auth:password user) (value new-password-1))
	   (answer t)))))

;; User List

(defcomponent user-list (query-view elephant-query-view-mixin)
  ()
  (:default-initargs
      :index-class 'golf-auth:user :index-slot 'golf-auth:username))

(defmethod render ((self user-list))
  (<:ul
   (mapc (lambda (user)
	   (<:li (<:as-html (golf-auth:username user))))
	 (current-items self))))

;; Navigation

(defcomponent top-level-show-editor (golf-widget-component)
  ())

(defmethod render ((self top-level-show-editor))
  (<:ul :class "golf-controls"
	(component-controls ("New Show" (call 'show-editor)))))

(defcomponent golf-admin-window (golf-window)
  ()
  (:default-initargs
    :title "Admin Interface"
    :body (make-instance 'tabbed-pane
		       :current-component-key 'chooser
		       :css-class "golf-admin-pane"
		       :remember-selected-child t
		       :contents
		       `((chooser . ,(make-instance 'ranged-admin-show-chooser
						    :reverse t))
			 (create  . ,(make-instance 'top-level-show-editor))
			 (users   . ,(make-instance 'user-admin-tool))))))


(defmethod update-url ((c golf-admin-window) url)
  (setf (ucw-core::uri.path url) "/admin/admin.ucw")
  url)