;;; Formula definitions!

(defun radians->degrees (angle)
  (* angle (/ 180 PI)))

(defun degrees->radians (angle)
  (* angle (/ PI 180)))

;;; Note:
;;; For the temperature formulas, Celsius is the 'master' format;
;;; instead of fahernheit->kelvin, I'll do fahernheit->celsius->kelvin.

(defun celsius->fahrenheit (temp)
  (+ (* temp (/ 9 5)) 32))

(defun fahrenheit->celsius (temp)
  (* (- temp 32) (/ 5 9)))

(defun celsius->kelvin (temp)
  (+ temp 273.15))

(defun kelvin->celsius (temp)
  (- temp 273.15))

(defparameter *conversion-lookup-table*
  `(("r" ("d" . ,#'radians->degrees))
	("d" ("r" . ,#'degrees->radians))
	("c" ("f" . ,#'celsius->fahrenheit)
	     ("k" . ,#'celsius->kelvin))
	("f" ("c" . ,#'fahrenheit->celsius)
	     ("k" . ,(lambda (x) (celsius->kelvin (fahrenheit->celsius x)))))
	("k" ("c" . ,#'kelvin->celsius)
	     ("f" . ,(lambda (x) (celsius->fahrenheit (kelvin->celsius x)))))))

(defun get-conversion-function (from to)
  (cdr (assoc to
		  (cdr (find from
			     *conversion-lookup-table*
			     :key #'car :test #'string=))
		  :test #'string=)))

(defun split-input (input-string)
  (let ((first-char (position-if #'alpha-char-p input-string)))
	(with-input-from-string (is-stream (subseq input-string 0 first-char))
	  (list (read is-stream nil nil)
		(subseq input-string first-char (1+ first-char))
		(subseq input-string (1+ first-char))))))

(defun convert-degree (input-string)
  (let* ((split-list (split-input input-string))
	     (conversion-function (get-conversion-function (second split-list)
							   (third split-list))))
	(if conversion-function
	    (format nil "~f~a"
		    (funcall conversion-function (first split-list))
		    (third split-list))
	    nil)))

;;; Results
(dolist (x '("3.1416rd"
		 "90dr"
		 "212fc"
		 "70cf"
		 "100cr"
		 "315.15kc"))
  (let ((result (convert-degree x)))
	(if result
	    (format t "~a~%" result)
	    (format t "No candidate for conversion~%"))))