#!/bin/sh
#|-*- mode:lisp -*-|#
#|Manage templates
exec ros -Q -m roswell -N roswell -- $0 "$@"
|#
(progn
  (roswell:include "util-template"))
;; TBD care windows someday
(defpackage :ros.script.template.3670908195
  (:use :cl :roswell.util :roswell.util.template))
(in-package :ros.script.template.3670908195)

(defvar *subcommands*  '(("init"  . template-init)
                         ("deinit" . template-deinit)
                         ("list" . template-list)
                         ("checkout" . template-checkout)
                         ("add" . template-add)
                         ("cat" . template-cat)
                         ("edit" . template-edit)
                         ("path" . template-native-path)
                         ("rm" . template-rm)
                         ("delete" . template-rm)
                         ("type" . template-type)
                         ("chmod" . template-chmod)
                         ("rewrite" . template-rewrite)
                         ("export" . template-export)
                         ("import" . template-import)
                         ("help" . template-help)))

(defun template-init (names)
  "Create new template"
  (if names
      (let* ((name (first names))
             (path (template-asd-path name)))
        (when (probe-file path)
          (format *error-output* "already exist ~A~%" path)
          (ros:quit 1))
        (template-create name)
        (format *error-output* "template written ~A~%" path))
      (format *error-output* "template init need template name.~%")))

(defun template-deinit (names)
  "Remove a template"
  (let* ((name (first names))
         (path (template-asd-path name)))
    (unless name
      (format *error-output* "template name is required.~%")
      (ros:quit 1))
    (when (equal name "default")
      (format *error-output* "can't remove \"default\".~%")
      (ros:quit 1))
    (unless (probe-file path)
      (format *error-output* "template: ~S not found.~%" name)
      (ros:quit 1))
    (template-remove name)
    (when (equal (template-default) name)
      (setf (template-default) "default"))))

(defun template-list (_)
  "List the installed templates"
  (let ((name (or (first _) (template-default))))
    (if (equal name "default")
        (format t "\"default\" doesn't have list~%")
        (let ((path (first (templates-list :filter name))))
          (when (and path (equal (pathname-type path) "asd"))
            (mapc (lambda (x)
                    (format t "~5A ~5A ~A ~A~%"
                            (or (getf x :chmod) "")
                            (getf x :method "copy")
                            (getf x :name)
                            (if (getf x :rewrite)
                                (format nil "-> ~S" (getf x :rewrite))
                                "")))
                  (template-directory name)))))))

(defun template-checkout (_)
  "Checkout default template to edit."
  (let ((name (first _)))
    (unless name
      (format *error-output* "current default is ~S~%~%candidates:~%~{~A~%~}"
              (template-default)
              (templates-list :name t))
      (ros:quit 0))
    (let ((path (templates-list :filter name)))
      (unless path
        (format *error-output* "template: ~S not found.~%" name)
        (ros:quit 1))
      (setf (template-default) name))))

(defun template-add (_)
  "Add files to template."
  (let ((name (template-default)))
    (when (equal name "default")
      (setf name (pop _)))
    (unless (and (templates-list :filter name)
                 (not (equal name "default")))
      (format *error-output* "template ~S is not editable.~%" name)
      (ros:quit 1))
    (loop for i in _
          do (template-add-file name i i))))

(defun template-cat (_)
  "Show file contents"
  (let ((name (template-default)))
    (when (equal name "default")
      (setf name (pop _)))
    (unless (and (templates-list :filter name)
                 (not (equal name "default")))
      (format *error-output* "template ~S does not contain files.~%" name)
      (ros:quit 1))
    (loop for i in _
          do (format t "~A" (uiop:read-file-string (template-file-path name i))))))

(defun template-edit (_)
  "Edit file contents"
  (let ((name (template-default)))
    (when (equal name "default")
      (setf name (pop _)))
    (unless (and (templates-list :filter name)
                 (not (equal name "default")))
      (format *error-output* "template ~S does not contain files.~%" name)
      (ros:quit 1))
    #+unix
    (roswell:exec `("/usr/bin/editor"
                    ,@(loop for i in _
                            collect (uiop:native-namestring (template-file-path name i)))))))

(defun template-native-path (_)
  "show native path in the template"
  (let ((name (template-default)))
    (when (equal name "default")
      (setf name (pop _)))
    (unless (and (templates-list :filter name)
                 (not (equal name "default")))
      (format *error-output* "template ~S does not contain files.~%" name)
      (ros:quit 1))
    (loop for i in _
          do (format t "~A~%" (uiop:native-namestring (template-file-path name i))))))

(defun template-rm (_)
  "Remove (delete) files from template."
  (let ((name (template-default)))
    (when (equal name "default")
      (setf name (pop _)))
    (unless (and (templates-list :filter name)
                 (not (equal name "default")))
      (format *error-output* "template ~S is not editable.~%" name)
      (ros:quit 1))
    (loop for i in _
          do (template-remove-file name i))))

(defun template-type (_)
  "Set template type for a file."
  (let ((name (template-default)))
    (when (equal name "default")
      (setf name (pop _)))
    (let ((type (first _))
          (sets (loop for i on *template-function-plist* by #'cddr
                      collect (string-downcase (first i)))))
      (unless type
        (let ((type (getf (template-parameter name) :default-method)))
          (format t "current default type is \"~A\"~%" (if type type "copy")))
        (ros:quit 0))
      (unless (find type sets
                    :test 'equal)
        (format *error-output* "should be one of~%~{~A~%~}" sets)
        (ros:quit 1))
      (if (rest _)
          (loop for i in (rest _)
                do (template-attr-file name i :method type))
          (template-attr-common name :default-method type)))))

(defun template-chmod (_)
  "Set mode for a file."
  (let ((name (template-default)))
    (when (equal name "default")
      (setf name (pop _)))
    (let ((mod (first _)))
      (unless (integerp (ignore-errors (parse-integer mod :radix 8)))
        (setf mod ""))
      (loop for i in (rest _)
            do (template-attr-file name i :chmod mod)))))

(defun template-rewrite (_)
  "Set path rewrite rule for a file"
  (let ((name (template-default)))
    (when (equal name "default")
      (setf name (pop _)))
    (template-attr-file name (first _) :rewrite (second _))))

(defun template-export (_)
  "Export template to directory"
  (let ((name (template-default))
        (dst "./"))
    (when (and (equal name "default") (first _))
      (setf name (pop _)))
    (unless (and (templates-list :filter name)
                 (not (equal name "default")))
      (format *error-output* "template ~S can't be exported.~%" name)
      (ros:quit 1))
    (when (first _)
      (setf dst (first _))
      (unless (string= dst "/" :start1 (1- (length dst)))
        (setf dst (format nil "~A/" dst))))
    (template-export-files name dst)))

(defun template-import (_)
  "Import template"
  (let ((src "./"))
    (when (first _)
      (setf src (first _))
      (unless (string= src "/" :start1 (1- (length src)))
        (setf src (format nil "~A/" src))))
    (template-import-files src)))

(defun template-help (_)
  (declare (ignore _))
  "Print usage and subcommands description"
  (format *error-output*
          "~&Usage: ~A template [subcommand] ~%"
          (pathname-name (opt "wargv0")))
  (loop
    for (command . func) in *subcommands*
    do
       (format *error-output* "~&~15A ~A~%" command (documentation func 'function))))

(defun main (&optional sub &rest argv)
  (let ((func (cdr (assoc sub *subcommands* :test #'equal))))
    (if func
        (funcall func argv)
        (template-help ()))))
;;; vim: set ft=lisp lisp:
