:- module(to_java, [to_java/1 /* +List */ ]).

:- use_module(library(strings)).
:- use_module(cmem).
:- use_module(format).

/* to_java takes a list of things to send to Java. The list contains members from
   the set

     unsigned_byte(B)  an unsigned byte
     unsigned_short(S) a 2 byte unsigned integer
     int(I)	       a 4 byte signed integer
     double(D)	       an 8 byte IEEE 754 floating-point "double precision" 
     atom(A)	       a Prolog atom
     chars(C),	       a list of character codes
     chars(L,C)	       a list of character codes (C) and its length (L)
     format(Format,Args) ... converted to chars(L,C)

note that only atoms and chars composed of characters between 0x1 and 0x7f are
supported. Although it would be trivial to fix, there is a performance & complexity
penalty that isn't worth it considering the need has never arisen.

At the Java end, you would read this using DataInputStream.

*/

to_java(X) :-
   size(X, Sized, 0, Size), malloc(Size, Buf), java(Sized, Buf),
   current_output(Stream), stream_code(Stream, Code),
   write_buffer_to_stream(Code, Buf, Size, Status),
   free(Buf),
   (  Status >= 0
   -> true
   ;  Errno is -Status,
      raise_exception(blame_on(write_error(Stream,errno(Errno)),to_java(X)))
   ).

size([], [], S, S).
size([H|T], [Sized0|Sized], Size0, Size) :-
   size_it(H, H, Sized0, X), Size1 is Size0 + X, size(T, Sized, Size1, Size).

size_it(atom(Atom), _, atom(AtomSize,Atom), X) :-
   string_size(Atom, AtomSize), X is 2 + AtomSize.
size_it(chars(Chars), _, chars(Length,Chars), X) :-
   length(Chars, Length), X is 2 + Length.
size_it(chars(Length,_), H, H, X) :- X is 2 + Length.
size_it(double(_), H, H, 8).
size_it(int(_), H, H, 4).
size_it(unsigned_byte(_), H, H, 1).
size_it(unsigned_short(_), H, H, 2).
size_it(format(Format,Args), _, Sized, X) :-
   format_to_chars(Format, Args, Chars), size_it(chars(Chars), _, Sized, X).

java([], _).
java([H|T], Buf0) :- java1(H, Buf0, Buf), java(T, Buf).

java1(atom(AtomSize,Atom), Buf0, Buf) :-
   to_string(Buf0, AtomSize, Buf1, Buf), memcpy(Buf1, Atom, AtomSize).
java1(chars(Length,Chars), Buf0, Buf) :-
   to_string(Buf0, Length, Buf1, Buf), cp(Chars, Buf1).
java1(double(D), Buf0, Buf) :- Buf is Buf0 + 8, to_java_double(Buf0, D).
java1(int(I), Buf0, Buf) :- Buf is Buf0 + 4, to_java_int(Buf0, I).
java1(unsigned_byte(B), Buf0, Buf) :-
   Buf is Buf0 + 1, assign(unsigned_8_at(Buf0), B).
java1(unsigned_short(U), Buf0, Buf) :-
   Buf is Buf0 + 2, to_java_unsigned_short(Buf0, U).

to_string(Buf0, I, Buf1, Buf) :-
   Buf1 is Buf0 + 2, Buf is Buf1 + I, to_java_unsigned_short(Buf0, I).

cp([], _).
cp([H|T], Buf0) :- assign(unsigned_8_at(Buf0), H), Buf is Buf0 + 1, cp(T, Buf).

% memcpy is actually from libc
foreign(memcpy, memcpy(+address, +string, +integer)).

% the rest are from to_java.c
foreign(to_java_double, to_java_double(+address,+float)).
foreign(to_java_int, to_java_int(+address,+integer)).
foreign(to_java_unsigned_short, to_java_unsigned_short(+address,+integer)).

foreign_file(to_java, [memcpy,write_buffer_to_stream,to_java_double,to_java_int,
		       to_java_unsigned_short]).
foreign(write_buffer_to_stream,
	write_buffer_to_stream(+integer, +address, +integer, [-integer])).

:- load_foreign_executable(to_java), abolish(foreign_file, 2), abolish(foreign, 3).

end_of_file.

:- add_advice(to_java:to_java(_, X),call,
	      (basics:member(Y, X), portray_clause(Y), fail ; true)), check_advice.
