;;; xwem-weather.el --- Display weather information in XWEM dock.

;; Copyright (C) 2004 by Free Software Foundation, Inc.

;; Author: Steve Youngs <steve@youngs.au.com>
;; Created: 2004-06-22
;; Keywords: xwem
;; X-CVS: $Id: xwem-weather.el,v 1.1 2004/07/14 08:49:07 youngs Exp $

;; This file is part of XWEM.

;; XWEM is free software; you can redistribute it and/or modify it
;; under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; XWEM is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
;; License for more details.

;; You should have received a copy of the GNU General Public License
;; along with XEmacs; see the file COPYING.  If not, write to the Free
;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
;; 02111-1307, USA.

;;; Synched up with: Not in FSF

;;; Commentary:

;; Display weather information (current local temp) in the dock area.
;; Optionally display a frame with more detailed weather information.

;; Set this up by adding the following code to your ~/.xwem/xwemrc.el

;; (require 'xwem-weather)
;; (customize-set-variable 'xwem-weather-update-frequency 3600)
;; (add-hook 'xwem-after-init-hook 'xwem-weather-init)


;;; Code:


(require 'xwem-osd)
(require 'itimer)

(defgroup xwem-weather nil
  "XWEM Weather options."
  :prefix "xwem-weather-"
  :group 'xwem)

(defcustom xwem-weather-station-id "ybbn"
  "*The four letter weather station ID.

The default setting is the author's local station, Brisbane,
Australia.  So you can all be jealous of the wonderful weather we
have in Australia. :-P"
  :type 'string
  :group 'xwem-weather)

(defvar xwem-weather-osd nil)

(defvar xwem-weather-frequency)

(defun xwem-weather-alter-update-frequency (value)
  "Alters the update frequency of the weather updates.

DO NOT CALL THIS FUNCTION DIRECTLY.  Change the update frequency
by customising `xwem-weather-update-frequency'.  You MUST use
`customize-set-variable' to do so.

Argument SYM is the symbol name of what is changing the frequency.  It
will always be `xwem-weather-update-frequency'.

Argument VALUE is an integer determining how often, in seconds, to
update the weather data."
  (let ((itimer (get-itimer "xwem-weather-itimer")))
    (cond ((and (xwem-osd-p xwem-weather-osd)
		value
		itimer)
	   (set-itimer-value itimer value)
	   (set-itimer-restart itimer value))
	  ((and (xwem-osd-p xwem-weather-osd)
		(eq value 0)
		itimer)
	   (delete-itimer itimer))
	  ((and (xwem-osd-p xwem-weather-osd)
		(> value 0)
		(not itimer))
	   (start-itimer "xwem-weather-itimer"
			 'xwem-weather-update
			 value value))
	  (t nil))
    (setq xwem-weather-frequency value)
    (message (format "Weather update frequency set to: %d seconds" value))))

(defcustom xwem-weather-update-frequency 0
  "*The number of seconds between updates of the weather info.

Most weather stations only update once/hour so it might not be very
beneficial to set this to lower than an hour.

If this is set to 0 updates will not happen.

If you want to set this outside of the custom interface, you MUST use
`customize-set-variable'."
  :type '(choice
	  (integer :tag "Do not update" 0)
	  (integer :tag "Every 10 minutes" 600)
	  (integer :tag "Every 15 minutes" 900)
	  (integer :tag "Every 30 minutes" 1800)
	  (integer :tag "Every 60 minutes" 3600)
	  (integer :tag "Every 2 hours" 7200)
	  (integer :tag "Every 4 hours" 14400)
	  (integer :tag "Every 12 hours" 43200)
	  (integer :tag "Every 24 hours" 86400)
	  (integer :tag "Other"))
  :set '(lambda (sym value)
	  (xwem-weather-alter-update-frequency value))
  :initialize 'custom-initialize-default
  :group 'xwem-weather)

(defcustom xwem-weather-data-directory xwem-dir
  "*The directory to story weather data files."
  :type '(directory :must-match t)
  :group 'xwem-weather)

(defcustom xwem-weather-data-file
  (expand-file-name xwem-weather-station-id xwem-weather-data-directory)
  "*File to hold the weather data."
  :type 'file
  :group 'xwem-weather)

(defcustom xwem-weather-temperature-format 'celsius
  "*Display temperature in Celsius or Fahrenheit."
  :type '(choice
	  (const :tag "Celsius" celsius)
	  (const :tag "Fahrenheit" fahrenheit))
  :group 'xwem-weather)

(defface xwem-weather-osd-face
  '((((class color))
     (:foreground "cyan" :family "fixed" :size "12pt"))
    (t
     (:family "fixed" :size "12pt")))
  "*Face for the weather OSD."
  :group 'xwem-weather)

(defconst xwem-weather-url-fqdn "weather.noaa.gov"
  "FQDN part of the weather URL.")

(defconst xwem-weather-url-dir "pub/data/observations/metar/decoded"
  "Directory part of the weather URL.")

(defun xwem-weather-retrieve-update ()
  "Retrieve weather info."
  (let* ((host xwem-weather-url-fqdn)
	 (dir xwem-weather-url-dir)
	 (file (upcase (concat xwem-weather-station-id ".txt")))
	 (path (concat dir "/" file))
	 (user-agent (concat "XEmacs " emacs-program-version))
	 (http (open-network-stream "xwem-weather-update"
				    " *xwem-weather-update-buf*"
				    host 80))
	 (pbuf (process-buffer http)))
    (process-send-string
     http
     (concat "GET /" path " HTTP/1.1\r\n"
	     "MIME-Version: 1.0\r\n"
	     "Connection: close\r\n"
	     "Extension: Security/Digest Security/SSL\r\n"
	     "Host: " host "\r\n"
	     "Accept: */*\r\n"
	     "User-Agent: " user-agent "\r\n\r\n"))
    (while (eq (process-status http) 'open)
      (sleep-for 0.05))
    (with-temp-buffer
      (erase-buffer)
      (insert-buffer pbuf)
      (goto-char (point-min))
      (re-search-forward "^Content-Length: \\([0-9]+.*$\\)" nil t)
      (let* ((file-length (string-to-int (match-string 1)))
	     (file-begin (progn
			   (goto-char (point-min))
			   (re-search-forward "^Content-Type:" nil t)
			   (forward-line 2)
			   (point-at-bol))))
	(goto-char file-begin)
	(forward-char file-length)
	(narrow-to-region file-begin (point))
	(write-region (point-min) (point-max) xwem-weather-data-file)))
    (kill-buffer pbuf)))

(defun xwem-weather-get-temp ()
  "Return the temperature as a string from the weather data file."
  (with-temp-buffer
    (erase-buffer)
    (insert-file-contents-literally xwem-weather-data-file)
    (goto-char (point-min))
    (re-search-forward "^Temperature: \\(-?[0-9]+ F\\) (\\(-?[0-9]+ C\\))" nil t)
    (let ((temp-f (match-string 1))
	  (temp-c (match-string 2)))
      (if (eq xwem-weather-temperature-format 'celsius)
	  temp-c
	temp-f))))

(define-xwem-command xwem-weather-show-details ()
  "doc string"
  (interactive)
  (xwem-help-display
   (insert-file-contents xwem-weather-data-file)))

(define-xwem-command xwem-weather-update ()
  "*Update the weather OSD."
  (interactive)
  (xwem-weather-retrieve-update)
  (when (xwem-osd-p xwem-weather-osd)
    (let* ((text (xwem-weather-get-temp))
	   (face `xwem-weather-osd-face)
	   (width (+ 3 (X-Text-width
			(xwem-dpy)
			(X-Font-get (xwem-dpy)
				    (face-font-name face))
			text)))
	   (height (+ 3 (X-Text-height
			 (xwem-dpy)
			 (X-Font-get (xwem-dpy)
				     (face-font-name face))
			 text))))
      (xwem-osd-text xwem-weather-osd text)
      (xwem-osd-set-width xwem-weather-osd width)
      (xwem-osd-set-height xwem-weather-osd height))))

(define-key xwem-global-map (xwem-kbd "H-c W d") 'xwem-weather-show-details)
(define-key xwem-global-map (xwem-kbd "H-c W u") 'xwem-weather-update)

(defvar xwem-weather-osd-keymap
  (let ((map (make-sparse-keymap 'xwem-weather-osd-keymap)))
    (define-key map [button1] 'xwem-weather-show-details)
    (define-key map [button2] 'xwem-weather-update)
    map)
  "Keymap for weather OSD.")

(define-xwem-command xwem-weather-display-temp (&optional force)
  "*Display the current temperature using OSD."
  (xwem-interactive "P")
  (when force
    (xwem-weather-retrieve-update))
  (let* ((face `xwem-weather-osd-face)
	 (text (xwem-weather-get-temp))
	 (width (+ 3 (X-Text-width
		      (xwem-dpy)
		      (X-Font-get (xwem-dpy)
				  (face-font-name face))
		      text)))
	 (height (+ 3 (X-Text-height
		       (xwem-dpy)
		       (X-Font-get (xwem-dpy)
				   (face-font-name face))
		       text))))
    (if (xwem-osd-p xwem-weather-osd)
	(progn
	  (xwem-osd-text xwem-weather-osd text)
	  (xwem-osd-set-width xwem-weather-osd width)
	  (xwem-osd-set-height xwem-weather-osd height))      
      (setq xwem-weather-osd (xwem-osd-create-dock
			      (xwem-dpy)
			      width
			      height
			      (list 'keymap xwem-weather-osd-keymap)))
      (xwem-osd-set-color xwem-weather-osd (face-foreground-name face))
      (xwem-osd-set-font xwem-weather-osd (face-font-name face))
      (xwem-osd-text xwem-weather-osd text)
      (xwem-osd-show xwem-weather-osd))))

(defun xwem-weather-init ()			    
  "Initialise the weather dock."
  (when (xwem-osd-p xwem-weather-osd)
    (xwem-osd-destroy xwem-weather-osd))
  (when (itimerp (get-itimer "xwem-weather-itimer"))
    (delete-itimer (get-itimer "xwem-weather-itimer")))
  (xwem-weather-display-temp 'force)
  (unless (eq xwem-weather-frequency 0)
    (start-itimer "xwem-weather-itimer"
		  'xwem-weather-update
		  xwem-weather-frequency
		  xwem-weather-frequency)))


(provide 'xwem-weather)

;;; xwem-weather.el ends here
