(in-package :clim-doc)

;;;; Images (epsfig)

(define-tex-macro "epsfig" "#1" (spec)
  (let ((spec (skot spec)))
    (cond ((= 0 (search "file=" spec))
           (let ((filename (subseq spec (length "file="))))
             (let ((png-file (convert-espi filename)))
               (list `(:IMG :SRC ,(namestring png-file))))))
          (t
           (error "Do not know how to handle \\epsfig{~A}." spec)) )))

;;;; Converting EPSI files to PNG files

(defun parse-integer-list (string &key (start 0) (end (length string)))
  (multiple-value-bind (value start) (parse-integer string :start start :end end :junk-allowed t)
    (cond ((null value)
           nil)
          (t
           (cons value (parse-integer-list string :start start :end end))))))

(defun epsi-bounding-box (filename)
  (with-open-file (input filename :direction :input)
    (let ((q "%%BoundingBox: "))
      (do ((x (read-line input nil :eof) (read-line input nil :eof)))
          ((eq x :eof)
           (error "No eps bounding box line file ~S." filename))
        (when (eql 0 (search q x))
          (let ((bb (parse-integer-list x :start (length q))))
            (unless (= (length bb) 4)
              (error "Strange eps bounding box line: ~S." x))
            (return (values-list bb))))))))

(defmacro with-temponary-file ((var) &body body)
  `(invoke-with-temponary-file #'(lambda (,var) ,@body)))

(defun invoke-with-temponary-file (cont)
  (loop
    (let ((fn (merge-pathnames (make-pathname 
                                :name (write-to-string (random (expt 36 10)) :base 36))
                               *tmp-dir*)))
      (unwind-protect
          (when (with-open-file (f fn 
                                 :direction :io 
                                 :if-exists nil
                                 :if-does-not-exist :create)
                  f)
            (funcall cont fn)
            (return))
        (ignore-errors (delete-file fn))))))

(defun convert-epsi-to-png (filename out-filename 
                            &key (scale (/ *html-dpi* 72))
                                 (oversample 4)
                                 (border 5))
  "Converts an epsi file to a png file."
  (with-temponary-file (tmpfile)
    (with-temponary-file (tmpfile2)
      (multiple-value-bind (llx lly urx ury) (epsi-bounding-box filename)
        (let ((cmd (format nil "epsffit -s ~D ~D ~D ~D ~A ~A" 
                           border border 
                           (+ border (- urx llx))
                           (+ border (- ury lly))
                           (escape-string-for-shell (#+CMU ext:unix-namestring #-CMU namestring filename))
                           (escape-string-for-shell (#+CMU ext:unix-namestring #-CMU namestring tmpfile)))))
          (unless (zerop
                   (shell cmd))
            (error "Execution of shell command ~S failed." cmd)))
        (unless (zerop
                 (shell (format nil "gs -q -sDEVICE=pnmraw -sOutputFile=~A -dNOPAUSE -dBATCH ~
                                     -r~D -g~Dx~D ~
                                     ~A"
                                (escape-string-for-shell (namestring tmpfile2))
                                (ceiling (* oversample scale 72))
                                (ceiling (* oversample scale (+ (* 2 border) (- urx llx))))
                                (ceiling (* oversample scale (+ (* 2 border) (- ury lly))))
                                (escape-string-for-shell (namestring tmpfile)) )))
          (error "Executing of ghostscript failed."))
        (ensure-directories-exist out-filename :verbose t)
        (unless (zerop
                 (shell (format nil "pnmscale ~D < ~A | pnmtopng > ~A"
                                (/ 1.0 oversample)
                                (escape-string-for-shell (namestring tmpfile2))
                                (escape-string-for-shell (namestring out-filename)))))
          (error "Executing of pnmtopng failed.")) ))))

#+:CMU
(defun shell (command)
  (ext:process-exit-code
   (ext:run-program "/bin/sh" (list "-c" command) :wait t :input nil :output nil
                    :error *standard-output*)))

(defun ensure-png-file (eps-file png-file)
  (when (> (file-write-date eps-file)
           (or (file-write-date png-file) 0))
    (format T "~&;; Converting ~A" eps-file)
    (convert-epsi-to-png eps-file png-file)))

(defun convert-espi (eps-file)
  (let ((eps-file (merge-pathnames eps-file *input-filename-defaults*)))
    (let ((png-file (make-pathname :name (pathname-name eps-file)
                                   :type "png")))
      (ensure-png-file eps-file (merge-pathnames png-file *output-directory*))
      png-file)))

