#!/usr/bin/perl -w
# genrpbindings -- generate ratpoison bindings for various languages
#
# Copyright (C) 2003, 2004 Ryan Yeske, Doug Kearns, Shawn Betts
#
# currently generates bindings for:
# * Perl        (Ratpoison.pm)       Ryan Yeske <rcyeske@sfu.ca>
# * Emacs Lisp  (ratpoison-cmd.el)   Ryan Yeske <rcyeske@sfu.ca>
# * Ruby        (ratpoison.rb)       Doug Kearns <djkea2@mugc.its.monash.edu.au>
# * Common Lisp (ratpoison.lisp)     Shawn Betts <sabetts@vcn.bc.ca>
# * Python      (ratpoison.py)       Mike O'Connor <stew@vireo.org>
# add more languages!
#
# Bindings are just very thin wrappers, no argument checking is done.
# All of the functions return a string.
#
# Example: ratpoison --command='echo hello world'
#
# #!perl
# use Ratpoison;
# Ratpoison::echo ("hello world")
#
# ;;; elisp
# (require 'ratpoison-cmd)
# (ratpoison-echo "hello world")
#
# #!ruby
# require "ratpoison"
# Ratpoison.echo ("hello world")
#
# ;;; Common Lisp
# (load "ratpoison.lisp")
# (ratpoison:rp-echo "hello world")
#
# #!python
# import ratpoison
# ratpoison.echo( "hello world" )

$\="\n";

# set this to your rp binary
$RATPOISON=$ENV{RATPOISON} || "ratpoison";

# open source file
$ACTIONS_C="../src/actions.c";
open ACTIONS_C or die "Can't open $ACTIONS_C";

# open target files
$PERL_FILE="./Ratpoison.pm";
$ELISP_FILE="./ratpoison-cmd.el";
$RUBY_FILE="./ratpoison.rb";
$COMMONLISP_FILE="./ratpoison.lisp";
$PYTHON_FILE="./ratpoison.py";
open PERL, ">$PERL_FILE" or die "Can't create $PERL_FILE";
open ELISP, ">$ELISP_FILE" or die "Can't create $ELISP_FILE";
open RUBY, ">$RUBY_FILE" or die "Can't create $RUBY_FILE";
open COMMONLISP, ">$COMMONLISP_FILE" or die "Can't create $COMMONLISP_FILE";
open PYTHON, ">$PYTHON_FILE" or die "Can't create $PYTHON_FILE";

# PERL preamble
print PERL 'package Ratpoison;';
print PERL '$RATPOISON="',$RATPOISON,'";';
print PERL 'sub command { return `$RATPOISON -c "@_"`; }';

# ELISP preamble
print ELISP '(defvar ratpoison-program "',$RATPOISON,'")';
print ELISP <<PREAMBLE;

(defmacro defun-ratpoison (cmd)
  `(progn (defun ,(intern (concat "ratpoison-" (symbol-name cmd))) (&rest args)
          (apply 'ratpoison-cmd ,(symbol-name cmd) args))))

(defun ratpoison-cmd (cmd &rest args)
  (with-temp-buffer
    (call-process ratpoison-program nil (current-buffer) t
		  "-c" (format "%s %s"
			       cmd
			       (mapconcat (lambda (x)
					    (if (stringp x)
						x
					      (prin1-to-string x)))
					  args " ")))
    (buffer-substring (point-min) (if (> (point-max) 1)
				      (- (point-max) 1)
				    (point-max)))))
PREAMBLE

# RUBY preamble
print RUBY  <<PREAMBLE;
module Ratpoison

  RATPOISON="$RATPOISON"

  def command (command, *args)
    return `#{RATPOISON} -c "#{command} #{args.join(' ')}"`
  end
  module_function :command
PREAMBLE

# Scheme preamble

print COMMONLISP <<PREAMBLE;
(defpackage :ratpoison
  (:use :cl))

;; Needs the CLOCC PORT package
(asdf:operate 'asdf:load-op :port)

(in-package :ratpoison)

(defvar ratpoison-program "$RATPOISON")

(defmacro defun-ratpoison (cmd)
  (let ((sym (intern (concatenate 'string "RP-" (symbol-name cmd)))))
    `(progn (defun ,sym (&rest args)
	      (apply 'ratpoison-cmd ,(string-downcase (symbol-name cmd)) args))
	    (export ',sym))))

(defun ratpoison-cmd (cmd &rest args)
  (labels ((mapconcat (fn list sep)
		     (apply 'concatenate 'string
			    (loop for x on list
				  collect (if (cdr x)
					      (concatenate 'string (funcall fn (car x)) sep)
					    (funcall fn (car x))))))
	 (build-cmd (cmd args)
		    (mapconcat (lambda (x)
				 (if (stringp x)
				     x
				   (prin1-to-string x)))
			       (nconc (list cmd) args) " ")))
    (let ((stream (port:pipe-input ratpoison-program
				   "-c" (build-cmd cmd args))))
    (do ((line (read-line stream nil nil)
	       (read-line stream nil nil))
	 (accum nil (cons line accum)))
	((null line) accum)))))
PREAMBLE

# python preamble

print PYTHON <<PREAMBLE;
import os
ratpoison="ratpoison -c "
def rp_command( *args ):
    p = os.popen( ratpoison + '"' + (' '.join(  args  ) ) + '"', 'r' )
    r = p.readlines();
    p.close();
    return r 

PREAMBLE

# bindings
while (<ACTIONS_C>) {
    if (m!/\*\@begin !) {
	while (<ACTIONS_C>)
	{
	    last if (m!/\*\@end !);
	    if (/\s*add_command\s*\(\"([^\"]+)\",\s*[^\"]+,\s*([0-9]+),\s*[0-9]+,\s*([0-9]+)/) {
	      my $name = $1;
	      my $numargs = $2;
	      my $optargs = $3;

	      # Skip the arguments
	      for (my $i=0; $i<$numargs; $i++) {
		<ACTIONS_C>;
	      }

	      $nbindings++;
	      print PERL "sub $name { return command (\"$name\", \@_); }";
	      print ELISP "(defun-ratpoison $name)";
	      print COMMONLISP "(defun-ratpoison $name)";
	      print RUBY "  def $name (*args)";
	      print RUBY "    return command (\"$name\", args)";
	      print RUBY "  end";
	      print RUBY "  module_function :$name\n";
	      print PYTHON "def rp_$name( *args ): return rp_command ( '$name ' +  ' '.join( args ) )";
	    }
	}
    }
}
print "$nbindings bindings.";

# PERL postamble
# nothing

# ELISP postamble
print ELISP '(provide \'ratpoison-cmd)';

# RUBY postamble
print RUBY "end";

# PYTHON postamble
# nothing

close PERL;
print "Created $PERL_FILE";
close ELISP;
print "Created $ELISP_FILE";
close RUBY;
print "Created $RUBY_FILE";
close COMMONLISP;
print "Created $COMMONLISP_FILE";
close PYTHON;
print "Created $PYTHON_FILE";
