#!/usr/bin/env python2.4

# Matlab-to-CI compiler
# Written by Boris Buegling <boris@icculus.org>
# Licensed under the GNU General Public License version 2.

import os, re, sys
sys.path.append('.')
from subprocess import Popen

def cmd (command):
	file3 = os.tempfile()
	proc = Popen(command, stdout=file3, stderr=file3)
	proc.communicate()
	file3.flush()
	file3.seek(0)
	output = file3.read()
	file3.close()
	return output[:-1]

if len(sys.argv) < 2:
	print >>sys.stderr, 'Usage:\n\t%s m-file' % sys.argv[0]
	sys.exit(1)

connect = 'connectID'
mfile = sys.argv[-1]
inc, init, suffix = '', '', ''
for opt in sys.argv[1:-1]:
	if opt.startswith('-s'):
		suffix = opt[2:]
	if opt.startswith('-u'):
		mfile2 = opt[2:]
		inc = '\n#include <lib%s.h>' % mfile2
		init = """\n\tlib%sInitialize();
\tlib%sTerminate();
\tmatlab_use_other("%s", "%s");""" % (mfile2, mfile2, mfile[:-2], mfile2)
	if opt == '-new':
		connect = 'connect'

try:
	mfile = file(mfile)
except IOError, e:
	print >>sys.stderr, e
	sys.exit(1)
line = mfile.readline()
if not line.startswith('function'):
	print >>sys.stderr, \
		'\'%s\' does not contain a function declaration.' % mfile
	sys.exit(1)
mytypes = None
for line2 in mfile.read().split('\n'):
	if re.match(r'^%.*@types .*', line2):
		mytypes = line2.split('@types')[1].strip().split(',')
mfile.close()

# Simple parser for Matlab function declarations
line = re.sub('function *', '', line).split('=')
has_res = True
if len(line) > 1:
	res = len(re.sub(r'\[|\]', '', line[0].strip()).split(','))
	line = line[1].strip()[:-1].split('(')
	name = line[0].strip()
else:
	has_res = False
	res = 1
	line = line[0].split('(')
	name = line[0].strip()
	line = ['foo', line[1].replace(')', '')]

retargs = ', '.join(['MWret%i' % i for i in range(res)])
retargs2 = ', '.join(['class FOO%i' % i for i in range(res-1)])
if len(retargs2) > 0:
	retargs2 = ', %s' % retargs2
retargs3 = ', '.join(['FOO%i &foo%i' % (i, i) for i in range(res-1)])
if len(retargs3) > 0:
	retargs3 = ', %s' % retargs3
args = [arg.strip() for arg in line[1].strip().split(',')]
templargs = [arg.upper()+arg for arg in args]
ntemplargs = len(templargs)
arglist = ', '.join(['const %s &%s' % (templargs[i], args[i]) for i in range(len(args))])
args2 = ', '.join(['MW%s(%s)' % (arg, arg) for arg in args])
args = ', '.join(['MW%s' % arg for arg in args])
templargs = ', '.join(['class %s' % arg for arg in templargs])

retargs_code = ''
for i in range(res-1):
	retargs_code += '\n\tfoo%i = (const FOO%i&)MWret%i;' % (i, i, i+1)

resme1, resme2, resme3, resme4 = '', '', '', ''
if has_res:
	resme1 = '%s, ' % retargs
	resme2 = '%i, %s, ' % (res, retargs)
	resme3 = '\n\tRET ret = MWret0;'
	resme4 = '\n\treturn ret;'

out = file('mlab_%s.h' % name, 'w')
out.write("""#include <lib%s.h>%s
#include <matlab.h>

template<class RET, %s%s> RET %s (%s%s)
{
	lib%sInitialize();%s
	myMwArray %s%s;
	%s(%s%s);%s%s
	lib%sTerminate();%s
}
""" % (name, inc, templargs, retargs2, name, arglist, retargs3, name, init, resme1, 
	args2, name, resme2, args, resme3, retargs_code, name, resme4))
out.close()

templargs2 = templargs.replace('class', 'const')
templargs = templargs.replace('class ', '')
if len(retargs2) > 0:
	templargs2 += retargs2.replace('class', '')
	templargs += retargs2.replace('class', '')
out = file('mlab_%s.ci' % name, 'w')
out.write("""#include <ctl.h>
#ifndef __MLAB_%s_H__
#define __MLAB_%s_H__

#define CTL_Library Matlab%s
#include CTL_LibBegin
#define CTL_FunctionTmpl1 RET, %s, (%s), %i, (RET, %s), %i
#include CTL_LibEnd

#endif // __MLAB_%s_H__

// vim: ft=cpp
""" % (name.upper(), name.upper(), suffix, name, templargs2, ntemplargs+res-1, 
	templargs, ntemplargs+res, name.upper()))
out.close()

out = file('mlab_%s.cpp' % name, 'w')
out.write("""#define CTL_Connect

#include <mlab_%s.ci>
#include <mlab_%s.h>

#include <fcntl.h>

void CTL_connect ()
{
		//int fd = open("/dev/null", O_WRONLY);
		//dup2(fd, 1);
""" % (name, name))

if mytypes:
	# FIXME: User should be able to specify a subset of types, too
	foo = []
	for type in mytypes:
		type = type.strip()
		if type in ['double', 'int', 'void']:
			foo.append(type)
		elif type == 'string':
			foo.append('std::string')
		elif type in ['array', 'vector']:
			foo.append('std::vector<double> ')
		elif type == 'matrix':
			foo.append('std::vector<std::vector<double> > ')
		else:
			print >>sys.stderr, 'Unknown type \'%s\' specified.' % type
			sys.exit(1)
	foo = ','.join(foo)
	out.write('Matlab%s::%s<1,%s>(::%s<%s>);\n' % (suffix, connect, foo, name, foo))
else:
	types = ['double', 'int', 'std::vector<double> ',
		'std::vector<std::vector<double> > ', 'std::string']
	tabw = ''
	code = ''
	for i in range(ntemplargs+res):
		code += tabw + 'for n%i in range(len(types)):\n' % i
		tabw += '\t'
	foo = ','.join(['%s' for i in range(res)])
	foo += ','+','.join(['%s' for i in range(ntemplargs)])
	foo2 = ','.join(['types[n%i]' % i for i in range(ntemplargs+res)])
	code += tabw + 'out.write("Matlab%s::%s<1,%s>(::%s<%s>);\\n" %% (%s, %s))' % \
		(suffix, connect, foo, name, foo, foo2, foo2)

	#print code
	code = compile(code, '<STRING>', 'exec')
	exec(code)

out.write('}\n')
out.close()
