#!/usr/bin/perl
# 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" )

use warnings;

$\="\n";

# Feel free to hardcode this if you installed ratpoison in
# a non-standard path, but note that you can also use the RATPOISON
# environment variable
$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=$ENV{RATPOISON} ? $ENV{RATPOISON} : "',$RATPOISON,'";';
print PERL 'sub command { my $a = "@_"; $a =~ s/(\')/\'\\\\\'\'/g; return `$RATPOISON -c \'$a\'`; }';

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

(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=ENV.fetch('RATPOISON', "$RATPOISON")

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

# Common Lisp preamble

print COMMONLISP <<PREAMBLE;
(require :asdf :asdf-utils)
(defpackage :ratpoison
  (:use :cl))

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

(in-package :ratpoison)

(defvar ratpoison-program
  (or (asdf-utils:getenv "RATPOISON")
      "$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 = os.getenv('RATPOISON') or '$RATPOISON'
def rp_command( *args ):
    p = os.popen( ratpoison + ' -c ' + '"' + (' '.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";

# COMMON-LISP postamble
# nothing

# 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";
