#! /bin/bash
###########################################################################
##                                                                       ##
##                              OByteLib                                 ##
##                                                                       ##
##                            Benoit Vaugon                              ##
##                                                                       ##
##    This file is distributed under the terms of the CeCILL license.    ##
##    See file LICENSE-en.                                               ##
##                                                                       ##
###########################################################################

function error () {
    echo "$@" 1>&2
    exit 1
}

function usage () {
    echo "$@" 1>&2
    error "Usage: $0 [ -prefix <dir> ] [ -libdir <dir> ] [ -mandir <dir> ]"
}

function check_command () {
    which "$1" > /dev/null
    if [ $? -ne 0 ]; then
	error "Error: \"$1\" not installed"
    fi
}

###

check_command "ocamlbuild"
check_command "ocamlrun"
check_command "mkdir"
check_command "pwd"
check_command "cp"
check_command "rm"

###

mkdir -p lib etc man

###

OCAMLBUILD=$(which ocamlbuild)
OCAMLRUN=$(which ocamlrun)
VERSION=$(cat VERSION)
PWD=$(pwd)

###

LIBDIR=/usr/local/lib/obytelib
MANDIR=/usr/local/man

while [ $# -ne 0 ]; do
    case "$1" in
	-libdir)   LIBDIR="$2"; shift ;;
	-mandir)   MANDIR="$2"; shift ;;
	-prefix)
	    LIBDIR="$2"/lib/obytelib
	    MANDIR="$2"/man
	    shift;;
	*)
	usage "Don't know what to do with \"$1\""
    esac
    shift
done

###

echo "\
VERSION = $VERSION

OCAMLBUILD = $OCAMLBUILD -cflags -w,Ae-44,-warn-error,A,-safe-string,-strict-formats,-strict-sequence -lflags -w,Ae-44,-warn-error,A,-safe-string,-strict-formats,-strict-sequence -no-links -classic-display

LIBDIR = $LIBDIR
MAN3DIR = $MANDIR/man3
" > etc/Makefile.conf

###

echo "\
let version = \"$VERSION\";;
" > etc/config.ml

###

MAX_PRIM_ARITY=5

PRIMS=$($OCAMLRUN -p |
  grep -v caml_spacetime_only_works_for_native_code |
  grep -v caml_register_code_fragment |
  grep -v caml_get_current_environment |
  grep -v caml_ensure_stack_capacity)

echo "\
include Strsec.Make(struct let section = Section.PRIM end)
" > etc/prim.ml

for prim in $PRIMS; do
    for ((i=1; i <= MAX_PRIM_ARITY; i++)); do
        echo -n "external ${prim}_$i : 'a" >> etc/prim.ml
        for ((j = 1; j <= i; j ++)); do
            echo -n " -> 'a" >> etc/prim.ml
        done
        echo " = \"$prim\"" >> etc/prim.ml
    done
    echo >> etc/prim.ml
done

echo "\

let find_prim arity name =
  match arity, name with" >> etc/prim.ml

for prim in $PRIMS; do
    for ((i=1; i <= MAX_PRIM_ARITY; i++)); do
        echo "  | $i, \"$prim\" -> Obj.repr ${prim}_$i" >> etc/prim.ml
    done
    echo >> etc/prim.ml
done

echo "\
  | 1, \"caml_ensure_stack_capacity\" -> Obj.repr (fun _ -> ())
  | 1, \"caml_is_printable\" -> Obj.repr (fun _ -> true)

  | _ -> Tools.fail \"external function %S of arity %d not found\" name arity

let apply narg cfun arg0 stack =
  let open Astack in
  let rec f narg cfun arg0 stack ofs =
    match narg with
    | 1 ->
      (Obj.obj cfun : Obj.t -> Obj.t) arg0
    | 2 ->
      (Obj.obj cfun : Obj.t -> Obj.t -> Obj.t) arg0 (acc stack ofs)
    | 3 ->
      (Obj.obj cfun : Obj.t -> Obj.t -> Obj.t -> Obj.t) arg0 (acc stack ofs)
        (acc stack (ofs + 1))
    | 4 ->
      (Obj.obj cfun : Obj.t -> Obj.t -> Obj.t -> Obj.t -> Obj.t) arg0
        (acc stack ofs) (acc stack (ofs + 1)) (acc stack (ofs + 2))
    | 5 ->
      (Obj.obj cfun : Obj.t -> Obj.t -> Obj.t -> Obj.t -> Obj.t -> Obj.t)
        arg0 (acc stack ofs) (acc stack (ofs + 1)) (acc stack (ofs + 2))
        (acc stack (ofs + 3))
    | _ ->
      f (narg - 5) (f 5 cfun arg0 stack ofs) (acc stack (ofs + 4)) stack
        (ofs + 5) in
  f narg cfun arg0 stack 0" >> etc/prim.ml

###

echo "** OByteLib configuration completed successfully **"
