(* Time-stamp: <modified the 10/07/2017 (at 13:12) by Erwan Jahier> *)


(* let put (os: out_channel) (fmt:('a, unit, string, unit) format4) : 'a = *)
(* 	Printf.kprintf (fun t -> output_string os t) fmt *)

open Printf
open Soc2cIdent
open Data


let rec (type_to_string2 : Data.t -> string) = 
  fun v -> 
    let str =
      match v with
        | Bool -> "bool"
        | Int  -> "int"
        | Real -> "real"
        | Extern s -> id2s s
        | Enum  (s, sl) -> id2s s
        | Struct (sid,_) -> (id2s sid)
        | Array (ty, sz) -> Printf.sprintf "%s_%d" (type_to_string2 ty) sz 
        | Alpha nb -> "alpha_"^(string_of_int nb) 
        | Alias(n,_) -> n
    in
    str

let inlined_soc = Soc2cDep.inlined_soc

(****************************************************************************)

(* Soc printer *)
type 'a soc_pp = {
  hfmt:  ('a, unit, string, unit) format4 -> 'a;
  cfmt:  ('a, unit, string, unit) format4 -> 'a;
  cput : string -> unit;
  hput : string -> unit;
  soc: Soc.t
}


let (string_of_soc_key : Soc.key -> string) = Soc2cIdent.get_soc_name


let string_of_var_expr = Soc2cDep.string_of_var_expr
open Soc

(* when an error occur, remove the generated c file (for the nonreg tests) *)
exception Delete_C_files

let var_expr_is_not_a_slice = function Slice _ -> false | _ -> true

let (gao2c : Soc.tbl -> 'a soc_pp -> Soc.gao -> unit) =
  fun stbl sp gao -> 
    let rec gao2str gao = 
      match gao with
        | Case(id, id_gao_l,_) -> ( 
          let to_case_str (v,gaol) =
            let gaol_str = (List.map gao2str gaol) in
            let gaol_block = String.concat "" gaol_str in
            (id2s v), gaol_block
          in
          let cases = List.map to_case_str id_gao_l in
          let ctx_opt = 
            let il,ol = sp.soc.profile in
            if List.mem_assoc id il || List.mem_assoc id ol then
              (if SocUtils.ctx_is_global sp.soc 
               then Soc2cUtil.ML_IO sp.soc.key 
               else Soc2cUtil.M_IO)
            else Soc2cUtil.Local 
          in
          let str = Soc2cUtil.gen_c_switch (Soc2cDep.ctx_var ctx_opt sp.soc id) cases in
          str
        )
        | Call(vel_out, Assign, vel_in,_) -> (
          let l = List.map2 (Soc2cDep.gen_assign_var_expr sp.soc) vel_out vel_in in
          String.concat "" l 
        )
        | Call(vel_out, Method((inst_name,sk),sname), vel_in,lxm) -> ( 
          let called_soc = SocUtils.find lxm sk stbl in
          let _, get_index = Soc2cInstances.to_array (sp.soc).instances in
          let index = get_index (inst_name,sk) in
          let step_arg = Printf.sprintf "ctx->%s_tab[%d]" (get_ctx_name sk) index in
          let ctx = step_arg in
          let step_arg = "&"^step_arg in
          List.iter (fun ve  -> assert(var_expr_is_not_a_slice ve)) vel_in;
          List.iter (fun ve  -> assert(var_expr_is_not_a_slice ve)) vel_out;
          Soc2cDep.gen_step_call sp.soc called_soc vel_out vel_in ctx sname step_arg
        )
        | Call(vel_out, Procedure sk, vel_in, lxm) -> (
          let called_soc = SocUtils.find lxm sk stbl in
          let ctx = get_ctx_name called_soc.key in
          (try
            List.iter (fun ve  -> assert(var_expr_is_not_a_slice ve)) vel_in;
            List.iter (fun ve  -> assert(var_expr_is_not_a_slice ve)) vel_out;
          with _ -> 
            print_string
              "*** Error. Slices in left part not yet supported in the C code generator, sorry\n";
            flush stdout;
            raise Delete_C_files
          ); 
          Soc2cDep.gen_step_call sp.soc called_soc vel_out vel_in ctx "step" ""
        )
    in
    sp.cput (gao2str gao)

let (step2c : Soc.tbl -> 'a soc_pp -> Soc.step_method -> unit) =
  fun stbl sp sm -> 
    if inlined_soc sp.soc.key then () (* don't generate code if inlined *) else
    (*     let sname = Soc2cDep.step_name sp.soc.key sm.name in *)
      let sname = Soc2cDep.step_name sp.soc.key sm.name in
      if sm.impl<>Extern then (
        let decl, def, ctype = Soc2cDep.get_step_prototype sm sp.soc in
        sp.hput (Printf.sprintf "%s\n" decl);
        sp.cput (Printf.sprintf "%s" def);
        (match sm.impl with
          | Extern -> ()
          | Predef ->
             (match sp.soc.key with
              | ("Lustre::eq",(Array _)::_,_) ->  
                 let str = Printf.sprintf
             "  *out = memcmp((const void *) i1, (const void *) i2, %s)==0;\n" ctype in
                 sp.cput str  
              | ("Lustre::eq",(Struct _)::_,_) -> 
                 let str = Printf.sprintf
             "  *out = memcmp((const void *) &i1, (const void *) &i2, %s)==0;\n" ctype in
                 sp.cput str  
              | ("Lustre::neq",(Array _)::_,_)  ->
                 let str = Printf.sprintf
             "  *out = !memcmp((const void *) i1, (const void *) i2, %s)==0;\n" ctype in
                 sp.cput str  
              | ("Lustre::neq",(Struct _)::_,_)  ->
                 let str = Printf.sprintf
             "  *out = !memcmp((const void *) &i1, (const void *) &i2, %s)==0;\n" ctype in
                 sp.cput str  
              | n -> sp.cput (Soc2cDep.get_predef_op n)
             )
          | Gaol(vl, gaol) -> (
            if Lv6MainArgs.global_opt.Lv6MainArgs.gen_wcet then 
              List.iter
                (fun v -> sp.cput (Soc2cUtil.string_of_flow_decl_w7annot gaol v))
                vl
            else
              List.iter (fun v -> sp.cput (Soc2cUtil.string_of_flow_decl v)) vl;
            sp.cput "\n"; 
            List.iter (gao2c stbl sp) gaol
          )
          | Iterator(it,it_soc_key,s) ->     
            let it_soc = SocUtils.find sm.lxm it_soc_key stbl in
            sp.cput (Soc2cDep.get_iterator sp.soc it it_soc s)
          | Boolred(i,j,k) -> 
            sp.cput (Soc2cDep.get_boolred sp.soc i j k)
          | Condact(k,el) -> 
            sp.cput (Soc2cDep.get_condact sp.soc (SocUtils.find sm.lxm k stbl) el)
        );
        sp.cput (sprintf "\n} // End of %s\n\n" sname)
      )
let (gen_instance_init_call : 'a soc_pp -> Soc.key * int -> unit) =
  fun sp (key,i) -> 
    let ctx_name = get_ctx_name key in
    if Lv6MainArgs.global_opt.Lv6MainArgs.soc2c_inline_loops || i<4 then
      for k=0 to i-1 do
        sp.cfmt "\n    %s_reset(&ctx->%s_tab[%d]);" ctx_name ctx_name k
      done
    else (
      sp.cput (Printf.sprintf "  for (_i=0 ; _i<%d ; _i+=1){" i);
      sp.cput (Printf.sprintf "\n    %s_reset(&ctx->%s_tab[_i]);" ctx_name ctx_name);
      sp.cput "\n }"
    )

module KeySet = Set.Make(struct type t = Soc.key let compare = compare end)
           
let (get_used_soc : Soc.t -> KeySet.t) =
  fun soc -> (* dig into the soc for the list of socs it uses *)
  let rec get_soc_of_gao acc = function
    | Case(_,l,_) ->
       List.fold_left
         (fun acc (_,gaol) -> List.fold_left get_soc_of_gao acc gaol) acc l         
    | Call(_,Assign,_,_) -> acc
    | Call(_,Method((_,sk),_),_,_) 
    | Call(_,Procedure sk,_,_) -> KeySet.add sk acc
  in
  let get_soc_of_step acc sm =
    match sm.impl with
    | Gaol(_, gaol) -> List.fold_left get_soc_of_gao acc gaol
    | Iterator(_,sk,_)
    | Condact(sk,_) -> KeySet.add sk acc
    | _ -> acc
  in
  List.fold_left get_soc_of_step KeySet.empty soc.step
           
let one_file() = Lv6MainArgs.global_opt.Lv6MainArgs.soc2c_one_file

(* soc2c
- creates c and h file(s) 
- updates/returns the list of created C files *)
let (soc2c : int -> out_channel -> out_channel -> Soc.tbl -> Soc.key ->
             string list -> Soc.t -> string list) = 
  fun pass hfile cfile stbl msoc_key cfiles_acc soc -> 
  if inlined_soc soc.key then cfiles_acc (* don't generate code if inlined *) else
    let ctx_name = get_ctx_name soc.key in
    let ctx_name_type = ctx_name^"_type" in
    if pass=1 then (
      (* Only for ctx of memoryless nodes + main node *)
      if SocUtils.ctx_is_global soc then
        Printf.kprintf (fun t -> output_string cfile t) "%s %s;\n" ctx_name_type ctx_name;
        cfiles_acc
    ) else (
      let base = string_of_soc_key soc.key  in
      let cfile,hfile,cfiles_acc =
        if one_file() || msoc_key = soc.key then cfile, hfile, cfiles_acc else
          let cfile =  (base^".c") in
          let hfile =  (base^".h") in
          let cfile_oc = open_out cfile in
          let hfile_oc = open_out hfile in
          (*open_out (base^".h"), *)
          Lv6util.entete cfile_oc "/*" "*/" ;
          Lv6util.entete hfile_oc "/*" "*/" ;
          Printf.fprintf cfile_oc "#include \"%s\"\n" hfile;
          Printf.fprintf hfile_oc "#include \"lustre_types.h\"\n";
          Printf.fprintf hfile_oc "#include \"lustre_consts.h\"\n";
          Printf.fprintf hfile_oc "#ifndef _%s_H_FILE \n" base;
          Printf.fprintf hfile_oc "#define _%s_H_FILE \n" base;
          flush cfile_oc;
          flush hfile_oc;
          cfile_oc, hfile_oc,
          (if List.mem cfile cfiles_acc then cfiles_acc else cfile::cfiles_acc)
      in
      let hfmt fmt = Printf.kprintf (fun t -> output_string hfile t) fmt in
      let cfmt fmt = Printf.kprintf (fun t -> output_string cfile t) fmt in
      let hput str = output_string hfile str in
      let cput str = output_string cfile str in
      let sp = { hfmt = hfmt; cfmt=cfmt; hput = hput; cput = cput; soc = soc } in
      (* include the header files that define the step functions used by the soc *)
      if (one_file()) then () else (
        let (used_soc:Soc.key list) = KeySet.elements (get_used_soc soc) in
        List.iter
          (fun sk -> if inlined_soc sk then () else
                       hfmt "#include \"%s.h\"\n" (string_of_soc_key sk)
          )
          used_soc;
        if msoc_key <> soc.key then hfmt "%s\n" (Soc2cDep.typedef_of_soc soc);
      );
      
      if SocUtils.is_memory_less soc then () else (
        cfmt "// Memory initialisation for %s\n" ctx_name;
        hfmt "void %s_reset(%s_type* ctx);\n" ctx_name ctx_name;
        cfmt "void %s_reset(%s_type* ctx){" ctx_name ctx_name;
        (* Call the reset_ctx functions of the soc instances *)
        if Lv6MainArgs.global_opt.Lv6MainArgs.soc2c_inline_loops then () else
          sp.cput "\n  int _i;\n";
        List.iter (gen_instance_init_call sp)
                  (fst (Soc2cInstances.to_array soc.instances));
        (match soc.key with
         (* set the parameter fields that have a default value (arrow,fby) *)
         | (_,_,MemInit (ve)) ->
            assert(var_expr_is_not_a_slice ve);
            cfmt "  ctx->_memory = %s;" (string_of_var_expr soc ve)
         | _ -> ()
        );
        cfmt "\n}\n";
        if
          SocUtils.is_memory_less soc
        then () (*no ctx at all in this case ! *) 
        else if
          Lv6MainArgs.global_opt.Lv6MainArgs.soc2c_global_ctx
        then
          (
            cfmt "\n// Initialisation of the  internal structure of %s\n" ctx_name;
            hfmt "void %s_init(%s_type* ctx);\n" ctx_name ctx_name;
            cfmt "void %s_init(%s_type* ctx){" ctx_name ctx_name;
            cfmt "
  // ctx->client_data = cdata;
  %s_reset(ctx);
 }
" ctx_name 
          )   
        else
          (
            cfmt "// Memory allocation for %s\n" ctx_name;
            hfmt "%s_type* %s_new_ctx();\n" ctx_name ctx_name;
            cfmt "%s_type* %s_new_ctx(){" ctx_name ctx_name;
            cfmt "

   %s_type* ctx = (%s_type*)calloc(1, sizeof(%s_type));
   // ctx->client_data = cdata;
   %s_reset(ctx);
  return ctx;
}
" ctx_name ctx_name ctx_name ctx_name) 
      );
      cfmt "// Step function(s) for %s\n" ctx_name;
      List.iter (step2c stbl sp) soc.step;
      ();
      if not (one_file() || msoc_key = soc.key) then (
        hfmt "#endif /* _%s_H_FILE */" base;
        close_out hfile;
        close_out cfile;
        Printf.printf "%s.h has been generated.\n" base;
        Printf.printf "%s.c has been generated.\n" base;
        flush stdout
      );
      cfiles_acc
      )

(****************************************************************************)
let (type_to_format_string : Data.t -> string) =
  function
    | Bool -> "%d"
    | Int -> "%d"
    | Real-> "%f"
    | Extern s -> "%d"
    | Enum  (s, sl) -> "%d" 
    | Struct (sid,_) -> "%s"
    | Array (ty, sz) -> "%s"
    | Alpha nb -> assert false 
    | Alias _ -> assert false 


(****************************************************************************)


module ItemKeySet = Set.Make(struct type t = Lic.item_key let compare = compare end)

(* To perform the topological sort of typedef. nf stands for "no
   fixpoint", that should be done by the caller. it is recursive just
   to deal with array of arrays *)
let  (find_typedep_nf : Lic.type_ -> Lic.item_key list) =
  fun t -> 
    let rec aux top = function
      | Lic.Bool_type_eff | Lic.Int_type_eff | Lic.Real_type_eff
      | Lic.External_type_eff _   | Lic.TypeVar _  
        ->  []
      | Lic.Abstract_type_eff(name,_) 
      | Lic.Enum_type_eff(name,_) -> if top then [] (* avoid self dep *) else [name] 
      | Lic.Array_type_eff(t,_) -> aux false t
      | Lic.Struct_type_eff(name, fl) -> 
        if not top then [name] else List.flatten(List.map (fun (_,(t,_)) -> aux false t) fl)
    in
    aux true t

let (is_extern_type: Lic.type_ -> bool) =
  function 
    | Lic.External_type_eff _ -> true
    | _  -> false


(* returns the typedef *)
let user_typedef licprg = 
  let to_c k t =
    Printf.sprintf "typedef %s;\n" (Soc2cUtil.lic_type_to_c t (long2s k))
  in
  let rec (typedef_to_string : Lic.item_key -> Lic.type_ -> string * ItemKeySet.t -> 
                               string * ItemKeySet.t) =
    fun k t acc ->
    (* topological sort according to type dep *)
    if is_extern_type t then acc else
      if ItemKeySet.mem k (snd acc) then acc else
        let type_list = find_typedep_nf t in
        let acc = List.fold_left 
                    (fun acc k -> 
                     match LicPrg.find_type licprg k with
                     | Some t -> typedef_to_string k t acc
                     | None -> acc (* occurs ? *)
                    ) 
                    acc type_list 
        in
        ((fst acc)^(to_c k t), ItemKeySet.add k (snd acc))
  in
  fst (LicPrg.fold_types typedef_to_string licprg ("",ItemKeySet.empty))
    
let (typedef_all : LicPrg.t -> Soc.tbl -> Soc.t -> string) =
  fun licprg soc_tbl main_soc ->
  (* We need to print the ctx typedef in a good order
       (w.r.t. typedef dependencies).  To do that, we traverse
       the tree of soc instances which root is the main soc. 
   *)

  (* Soc with memory can be used several times; hence we mark via this
       set the ones that have already been visited. *)
  let visited = KeySet.empty in
  let rec (soc_with_mem : string * KeySet.t -> Soc.t -> string * KeySet.t) =
    (* recursively traverse the soc dependancies to define the typedef
       in the good order (i.e., define before use) *)
    fun (acc,visited) soc ->
    if KeySet.mem soc.key visited then (acc,visited) else
      let visited = KeySet.add soc.key visited in
      let acc,visited =
        List.fold_left
          (fun (acc,visited) (iname, sk) ->
           let soc = SocUtils.find_no_exc sk soc_tbl in
           soc_with_mem (acc,visited) soc
          )
          (acc,visited) soc.instances
      in
      let acc = acc ^ (
          if one_file() || soc.key = main_soc.key then
            Soc2cDep.typedef_of_soc soc
          else
            (Printf.sprintf "#include \"%s.h\"\n" (string_of_soc_key soc.key))
        )
      in
      acc,visited
  in
  let soc_ctx_typedef_with_mem = 
    if SocUtils.ctx_is_global main_soc then "" else
      fst (soc_with_mem ("",visited) main_soc)
  in
  (* Then we still have to print memoryless soc that can not appear
       as a soc instance *)
  let soc_ctx_typedef_without_mem =
    let socs = Soc.SocMap.bindings soc_tbl in
    let socs = snd (List.split socs) in
    let memless_soc_to_string acc soc =
      if SocUtils.is_memory_less soc then acc^(Soc2cDep.typedef_of_soc soc) else acc
    in 
    List.fold_left  memless_soc_to_string "" socs
  in 
"// Memoryless soc ctx typedef \n"^soc_ctx_typedef_without_mem
  ^"// Memoryfull soc ctx typedef \n"^soc_ctx_typedef_with_mem
    



(****************************************************************************)
let rec (const_to_c: Lic.const -> string) =
  function
    | Lic.Bool_const_eff true -> "1"
    | Lic.Bool_const_eff false -> "0"
    | Lic.Int_const_eff i -> (sprintf "%s" i)
    | Lic.Real_const_eff r -> r
    | Lic.Extern_const_eff (s,t) -> (long2s s)
    | Lic.Abstract_const_eff (s,t,v,_) -> const_to_c v
    | Lic.Enum_const_eff   (s,Lic.Enum_type_eff(_,ll)) -> Lic.enum_to_string s ll
    | Lic.Enum_const_eff   _ -> assert false (* SNO *)
    | Lic.Struct_const_eff (fl, t) -> (
      let string_of_field = 
        function (id, veff) -> 
          (Lv6Id.to_string id)^" = "^ (const_to_c veff) 
      in
      let flst = List.map string_of_field fl in
(*       (string_of_type_eff t)^ *)
        "{"^(String.concat "; " flst)^"}"
    )
    | Lic.Array_const_eff (ctab, t) -> (
      let vl = List.map const_to_c ctab in
      "{"^(String.concat ", " vl)^"}"
    )
    | Lic.Tuple_const_eff   cl -> assert false

(* returns a pair: the lhs for the .h, the rhs for the .c
Indeed, arrays constant need to be defined in a .c
*)
let (constdef : LicPrg.t -> string*string) = 
  fun licprg -> 
    let to_c k = function
      | Lic.Extern_const_eff _ -> "",""
      (*       | Lic.Array_const_eff (ctab, Array_type_eff(_t,s)) -> ( *)
      | Lic.Array_const_eff (ctab, t) -> (
        let vl = List.map const_to_c ctab in
        let s = List.length vl in
        let tab_exp = "{"^(String.concat ", " vl)^"}" in
         Printf.sprintf "const %s [%i];\n" (long2s k) s, 
         Printf.sprintf "const %s [%i] = %s;\n" (long2s k) s tab_exp 
      )       
      | c  -> 
      Printf.sprintf "#define %s %s\n"
        (long2s k)
        (const_to_c c),""
    in
    let strh,strc = LicPrg.fold_consts
                (fun k t (acc_h,acc_c) ->
                 let h,c = to_c k t in
                 (acc_h^h,acc_c^c)) licprg  ("","")
    in
    (if strh = "" then "" else
       "\n// Constant definitions \n" ^ strh),
    (if strc = "" then "" else
       "\n// Constant definitions \n" ^ strc)

(****************************************************************************)

let (gen_memoryless_ctx : Soc.tbl -> string) =
  fun stbl -> 
    let do_soc sk soc acc =
      if (SocUtils.ctx_is_global soc) && not (inlined_soc soc.key) then 
        let ctx_name = get_ctx_name soc.key in
        let ctx_name_type = ctx_name^"_type" in    
        Printf.sprintf "%sextern %s %s;\n" acc ctx_name_type ctx_name 
      else
        acc
    in
    let acc = Soc.SocMap.fold do_soc stbl "" in
    if acc = "" then "" else 
      Printf.sprintf "\n// Allocation of memoryless ctx\n%s" acc

(* a shortcut *)
let io_transmit_mode () = Lv6MainArgs.global_opt.Lv6MainArgs.io_transmit_mode

(****************************************************************************)
let gen_main_loop_body inputs outputs soc ctx =
  if not Lv6MainArgs.global_opt.Lv6MainArgs.soc2c_global_ctx then
    (match io_transmit_mode () with
     | Lv6MainArgs.Stack ->
        let to_c_decl (n,t) = ((Soc2cUtil.data_type_to_c t n)^ ";\n  ") in
        let inputs_t  = List.map to_c_decl inputs in
        let outputs_t = List.map to_c_decl outputs in
        let inputs_decl = Printf.sprintf "\n  %s" (String.concat "" inputs_t) in
        let outputs_decl = Printf.sprintf "%s" (String.concat "" outputs_t) in
        let ctx_decl = if SocUtils.is_memory_less soc then "" else 
                         "  "^ctx^"_type* ctx = "^ ctx^"_new_ctx(NULL);\n"
        in
        inputs_decl ^ outputs_decl ^ ctx_decl
     | Lv6MainArgs.Heap -> ("
/* Context allocation */
" ^ (if SocUtils.is_memory_less soc then ctx^"_type* ctx = &"^ctx^";\n"
     else ctx^"_type* ctx = "^ ctx^"_new_ctx(NULL);")
                              )
        | Lv6MainArgs.HeapStack -> ("
/* Context allocation */
" ^ (if SocUtils.is_memory_less soc then ctx^""
     else ctx^"_type* ctx ;\n"^ ctx^"_reset(ctx);")
                                   )
    ) 
  else
    (match io_transmit_mode () with
     | Lv6MainArgs.Stack ->
        let to_c_decl (n,t) = ((Soc2cUtil.data_type_to_c t n)^ ";\n  ") in
        let inputs_t  = List.map to_c_decl inputs in
        let outputs_t = List.map to_c_decl outputs in
        let inputs_decl = Printf.sprintf "\n  %s" (String.concat "" inputs_t) in
        let outputs_decl = Printf.sprintf "%s" (String.concat "" outputs_t) in
        let ctx_decl = if SocUtils.is_memory_less soc then "" else 
                         ctx^"_type ctx_struct;\n  "^
                           ctx^"_type* ctx = &ctx_struct;\n  "^
                             ctx^"_init(ctx);"
        in
        inputs_decl ^ outputs_decl ^ ctx_decl
     | Lv6MainArgs.Heap -> ("
/* Context allocation */
" ^ (if SocUtils.is_memory_less soc then 
       ctx^"_type* ctx = &"^ctx^";\n"
     else (
       ctx^"_type ctx_struct;
    "^ctx^"_type* ctx = & ctx_struct;
    "^ctx^"_init(ctx);    
")
    )
                           )
     | Lv6MainArgs.HeapStack -> ("
/* Context allocation */
" ^ (if SocUtils.is_memory_less soc then ctx^""
     else ctx^"_type* ctx ;\n"^ ctx^"_reset(ctx);")
                                )
    )
      
(****************************************************************************)
let (gen_main_wcet_file : Soc.t -> string -> Soc.tbl -> unit) =
  fun soc base stbl -> 
    let mainfile = base^"_main.c" in
    let oc = open_out mainfile in

    let putc s = output_string oc s in
    let ctx = get_ctx_name soc.key in
    let step = Soc2cDep.step_name soc.key "step" in
    let inputs,outputs = soc.profile in
    Lv6util.entete oc "/*" "*/";
    putc  ("
#include <stdlib.h>
#include \""^base ^".h\" 
int main(){" ^ (gen_main_loop_body inputs outputs soc ctx));
    (match io_transmit_mode () with
      | Lv6MainArgs.Stack ->
        let i =  fst (List.split inputs) in
        let o = List.map (fun (n,t) -> match t with Data.Array(_,_) -> n | _ ->"&"^n) outputs in
        let io = String.concat "," (i@o) in
        let io = if SocUtils.is_memory_less soc then io else if io = "" then "ctx" else io^",ctx" in
        putc ("  " ^ step^"("^io^");
  return 0; 
}
"
        );
      | Lv6MainArgs.HeapStack -> assert false
      | Lv6MainArgs.Heap ->
        let io = if SocUtils.is_memory_less soc then  "" else "ctx"  in
        putc ("  " ^ step^"("^io^");
  return 0; 
}
"
        );
    );
    Printf.printf "%s has been generated.\n" mainfile; flush stdout;
    close_out oc


let (gen_loop_file : LicPrg.t -> Soc.t -> string -> out_channel -> Soc.tbl -> unit) =
  fun licprg soc base oc stbl -> 
    let putc s = output_string oc s in
    let ctx = get_ctx_name soc.key in
    let step = Soc2cDep.step_name soc.key "step" in
    let (n,_,_) = soc.key in
    let n = id2s n in
    let inputs,outputs = soc.profile in
    let inputs_io  = SocVar.expand_profile true false inputs  in
    let outputs_io = SocVar.expand_profile true false outputs in
    let inputs_exp = SocVar.expand_profile true true  inputs  in
    let outputs_exp= SocVar.expand_profile true true  outputs in
    Lv6util.entete oc "/*" "*/";
    putc  ("
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include \""^base ^".h\" 
/* Print a promt ? ************************/
static int ISATTY;
/* MACROS DEFINITIONS ****************/
#ifndef TT
#define TT \"1\"
#endif
#ifndef FF
#define FF \"0\"
#endif
#ifndef BB
#define BB \"bottom\"
#endif
#ifdef CKCHECK
/* set this macro for testing output clocks */
#endif

/* Standard Input procedures **************/
_boolean _get_bool(char* n){
   char b[512];
   _boolean r = 0;
   int s = 1;
   char c;
   do {
      if(ISATTY) {
         if((s != 1)||(r == -1)) printf(\"\\a\");
         // printf(\"%s (1,t,T/0,f,F) ? \", n);
      }
      if(scanf(\"%s\", b)==EOF) exit(0);
      if (*b == 'q') exit(0);
      s = sscanf(b, \"%c\", &c);
      r = -1;
      if((c == '0') || (c == 'f') || (c == 'F')) r = 0;
      if((c == '1') || (c == 't') || (c == 'T')) r = 1;
   } while((s != 1) || (r == -1));
   return r;
}
_integer _get_int(char* n){
   char b[512];
   _integer r;
   int s = 1;
   do {
      if(ISATTY) {
         if(s != 1) printf(\"\\a\");
         //printf(\"%s (integer) ? \", n);
      }
      if(scanf(\"%s\", b)==EOF) exit(0);
      if (*b == 'q') exit(0);
      s = sscanf(b, \"%d\", &r);
   } while(s != 1);
   return r;
}
#define REALFORMAT ((sizeof(_real)==8)?\"%lf\":\"%f\")
_real _get_real(char* n){
   char b[512];
   _real r;
   int s = 1;
   do {
      if(ISATTY) {
         if(s != 1) printf(\"\\a\");
         //printf(\"%s (real) ? \", n);
      }
      if(scanf(\"%s\", b)==EOF) exit(0);
      if (*b == 'q') exit(0);
      s = sscanf(b, REALFORMAT, &r);
   } while(s != 1);
   return r;
}
/* Standard Output procedures **************/
void _put_bottom(char* n){
   if(ISATTY) printf(\"%s = \", n);
   printf(\"%s \", BB);
   if(ISATTY) printf(\"\\n\");
}
void _put_bool(char* n, _boolean _V){
   if(ISATTY) printf(\"%s = \", n);
   printf(\"%s \", (_V)? TT : FF);
   if(ISATTY) printf(\"\\n\");
}
void _put_int(char* n, _integer _V){
   if(ISATTY) printf(\"%s = \", n);
   printf(\"%d \", _V);
   if(ISATTY) printf(\"\\n\");
}
void _put_real(char* n, _real _V){
   if(ISATTY) printf(\"%s = \", n);
   printf(\"%f \", _V);
   if(ISATTY) printf(\"\\n\");
}"^(Soc2cExtern.gen_getters licprg)^"
/* Output procedures **********************/
#ifdef CKCHECK
void %s_BOT_n(void* cdata){
   _put_bottom(\"n\");
}
#endif
/* Output procedures **********************/
void "^n^"_O_n(void* cdata, _integer _V) {
   _put_int(\"n\", _V);
}"^ (gen_memoryless_ctx stbl) ^
"
/* Main procedure *************************/
int main(){
  int _s = 0;" ^ (
  (gen_main_loop_body inputs outputs soc ctx)
           )
    );
    let to_rif_decl (n,t) = ("\\\""^n^"\\\":" ^(type_to_string t)) in
    let inputs_t  = List.map to_rif_decl inputs_io in
    let outputs_t = List.map to_rif_decl outputs_io in
    let inputs_decl = Printf.sprintf "#inputs %s" (String.concat " " inputs_t) in
    let outputs_decl = Printf.sprintf "#outputs %s" (String.concat " " outputs_t) in
    
    putc  ("
  printf(\""^inputs_decl^"\\n\");
  printf(\""^outputs_decl^"\\n\");

  /* Main loop */
  ISATTY = isatty(0);
  while(1){
    if (ISATTY) printf(\"#step %d \\n\", _s+1);
    else if(_s) printf(\"\\n\");
    fflush(stdout);
    ++_s;
");
    List.iter (fun (id,t) -> 
      let t = type_to_string2 t in
      let str = 
        if io_transmit_mode () = Lv6MainArgs.Stack 
        then Printf.sprintf "    %s = _get_%s(\"%s\");\n" id t id 
        else Printf.sprintf "    ctx->%s = _get_%s(\"%s\");\n" id t id 
      in
      putc str
    ) 
      inputs_exp;
    let inputs_fmt  = List.map (fun (_,t) -> type_to_format_string t) inputs_io in
    let outputs_fmt = List.map (fun (_,t) -> type_to_format_string t) outputs_io in
    if io_transmit_mode () = Lv6MainArgs.Stack 
    then
      let i =  fst (List.split inputs) in
      let o = List.map
                (fun (n,t) -> match t with Data.Array(_,_) -> n | _ ->"&"^n)
                outputs
      in
      let io = String.concat "," (i@o) in
      let io = if SocUtils.is_memory_less soc then io
               else if io = "" then "ctx" else io^",ctx"
      in
      putc ("    " ^ step^"("^io^");
    // printf(\"" ^ (String.concat " " inputs_fmt)^ " #outs " ^ 
               (String.concat " " outputs_fmt)^ "\\n\"," ^
                 (String.concat "," (List.map (fun (id,_) -> ""^id )
                                              (inputs_exp@outputs_exp)))^ 
               ");
    printf(\"" ^ 
               (String.concat " " outputs_fmt)^ "\\n\"," ^
               (String.concat "," (List.map (fun (id,_) -> ""^id ) (outputs_exp)))^ 
               ");
  }"
    ) else (
      putc ("    " ^ step^"(ctx);
    // printf(\"" ^ (String.concat " " inputs_fmt)^ " #outs " ^ 
               (String.concat " " outputs_fmt)^ "\\n\"," ^
                 (String.concat "," (List.map (fun (id,_) -> "ctx->"^id )
                                              (inputs_exp@outputs_exp)))^ 
               ");
    printf(\"" ^ 
               (String.concat " " outputs_fmt)^ "\\n\"," ^
             (String.concat "," (List.map (fun (id,_) -> "ctx->"^id ) (outputs_exp)))^ 
             ");
  }"));
    putc "\n  return 1;
   
}
"


let (gen_loop_file4ogensim : Soc.t -> string -> out_channel -> Soc.tbl -> unit) =
  fun soc base oc stbl -> 
  let putc s = output_string oc s in
  let ctx = get_ctx_name soc.key in
  let step = Soc2cDep.step_name soc.key "step" in
  let inputs,outputs = soc.profile in
  let inputs_io  = SocVar.expand_profile true false inputs  in
  let outputs_io = SocVar.expand_profile true false outputs in
  let inputs_exp = SocVar.expand_profile true true  inputs  in
  let outputs_exp= SocVar.expand_profile true true  outputs in
  let define_define i (var_name,_) =
    putc (Printf.sprintf "#define _%s\t 0xe%07X\n" var_name ((i+1)*8));
  in
  Lv6util.entete oc "/*" "*/";
  
  putc  ("#include \""^base ^".h\"
                              
                              #define tickBegin  0xe0000000
                              ");
  List.iteri define_define (inputs_io@ outputs_io);
  
  putc ("\n int main(){\n" ^ gen_main_loop_body inputs outputs soc ctx);
  putc  ("
          /* Main loop */
          while(1){
          // notify the simulator that it is time to load the inputs from Lurette
          // to the specified address
          *((unsigned int*)tickBegin) = 0;
          // load inputs from the memory locations
          ");
  List.iter2 
    (fun (id,t)  (id_flat,_) -> 
     let t = Soc2cUtil.data_type_to_c t "" in
     let str = 
       Printf.sprintf "    %s = *((%s*)_%s);\n" id t id_flat
     in
     putc str
    ) 
    inputs_exp inputs_io;

  assert (io_transmit_mode () = Lv6MainArgs.Stack);

  let i =  fst (List.split inputs) in
  let o = List.map (fun (n,t) -> match t with Data.Array(_,_) -> n | _ ->"&"^n) outputs in
  let io = String.concat "," (i@o) in
  let io = if SocUtils.is_memory_less soc then io else if io = "" then "ctx" else io^",ctx" in
  putc ("    " ^ step^"("^io^");

// now write the output to the memory which will be output to Lurette
");
  List.iter2
    (fun (id,t) (id_flat,_) -> 
     let t = Soc2cUtil.data_type_to_c t "" in
     let str = Printf.sprintf "	 *((%s*)_%s) = %s;\n" t id id_flat in
     putc str
    ) 
    outputs_io outputs_exp;

  putc "}\n  return 1;       
        }        
        "

(****************************************************************************)
(* The entry point for lus2lic --to-c *)
let (f : Lv6MainArgs.t -> Soc.key -> Soc.tbl -> LicPrg.t -> unit) =
  fun args msoc stbl licprg -> 
  let socs = Soc.SocMap.bindings stbl in
  let socs = snd (List.split socs) in 
  (* XXX que fait-on pour les soc predef ? *)
  (*     let _, socs = List.partition is_predef socs in *)
  let base = 
    if args.Lv6MainArgs.outfile = "" then
      string_of_soc_key msoc 
    else
      Filename.basename (
          try Filename.chop_extension args.Lv6MainArgs.outfile
          with Invalid_argument _ -> args.Lv6MainArgs.outfile)
  in
  let hfile = base ^ ".h" in
  let cfile = base ^ ".c" in
  let ext_cfile = Printf.sprintf "%s_ext.c" base in
  let ext_hfile = Printf.sprintf "%s_ext.h" base in
  let loopfile = base^"_loop.c" in
  let occ = open_out cfile in
  let och = open_out hfile in
  let ocl = open_out loopfile in
  let types_h_oc = open_out "lustre_types.h" in
  let consts_h_oc = open_out "lustre_consts.h" in
  let consts_c_oc = open_out "lustre_consts.c" in
  let cfiles_acc = ["lustre_consts.c"; cfile] in
  let const_def_h, const_def_c = constdef licprg in
  let assign_ext_types_list = (Soc2cGenAssign.gen_used_types socs) in
  let main_soc = SocUtils.find_no_exc msoc stbl in
    (* Generate ext files if necessary  *)
  let needs_cfile, needs_hfile = 
    Soc2cExtern.gen_files main_soc stbl licprg ext_cfile ext_hfile hfile
  in
  Lv6util.entete consts_h_oc "/*" "*/" ;
  output_string consts_h_oc const_def_h;
  Lv6util.entete consts_c_oc "/*" "*/" ;
  output_string consts_c_oc "#include \"lustre_consts.h\"";
  output_string consts_c_oc const_def_c;
  Lv6util.entete types_h_oc "/*" "*/" ;
  output_string types_h_oc ("
#ifndef _SOC2C_PREDEF_TYPES
#define _SOC2C_PREDEF_TYPES
typedef int _boolean;
typedef int _integer;
typedef char* _string;
typedef double _real;
typedef double _double;
typedef float _float;
#define _false 0
#define _true 1
#endif
// end of _SOC2C_PREDEF_TYPES
// User typedef 
#ifndef _"^base^"_TYPES
#define _"^base^"_TYPES\n");
    output_string types_h_oc (user_typedef licprg);
    output_string types_h_oc ("#endif // enf of  _"^base^"_TYPES
"
  ^ (typedef_all licprg stbl main_soc )
  ^ (if needs_hfile then "#include \""^ base ^"_ext.h\"" else ""));
   
    try
    let putc s = output_string occ s in
    let puth s = output_string och s in
    Lv6util.entete occ "/*" "*/" ;
    Lv6util.entete och "/*" "*/";
    if Lv6MainArgs.global_opt.Lv6MainArgs.gen_wcet then (
      gen_loop_file4ogensim main_soc base ocl stbl;
      gen_main_wcet_file main_soc base stbl
    )
    else
      gen_loop_file licprg main_soc base ocl stbl;
    
    output_string och "
#include <stdlib.h>
#include <string.h>

#include \"lustre_types.h\"
#include \"lustre_consts.h\"

";

    if needs_hfile then puth (Printf.sprintf "#include \"%s\"\n" ext_hfile);
    puth (Printf.sprintf "#ifndef _%s_H_FILE\n" base);
    puth (Printf.sprintf "#define _%s_H_FILE\n" base);

    putc (Printf.sprintf "#include \"%s\"\n" hfile);
(*     putc (Soc2cExtern.cpy_declaration licprg); *)
    putc (Soc2cExtern.const_declaration licprg);
    let cfiles_acc = 
    if io_transmit_mode () = Lv6MainArgs.Heap then (
      puth "/////////////////////////////////////////////////\n";
      puth "//// Static allocation of memoryless soc ctx\n";
      let cfiles_acc = List.fold_left (soc2c 1 och occ stbl msoc) cfiles_acc socs in
      puth "/////////////////////////////////////////////////\n";
      cfiles_acc
    ) else cfiles_acc
    in
    putc "//// Defining step functions\n";
    let cfiles_acc = List.fold_left (soc2c 2 och occ stbl msoc) cfiles_acc socs in
    
    puth "/////////////////////////////////////////////////\n";
    if assign_ext_types_list <> [] then (
      output_string types_h_oc "// Defining array and extern types assignments \n";
      if Lv6MainArgs.global_opt.Lv6MainArgs.gen_wcet then 
        List.iter (fun t -> output_string types_h_oc (Soc2cGenAssign.f_forloop t))
                  assign_ext_types_list
      else
        List.iter (fun t -> output_string types_h_oc (Soc2cGenAssign.f t))
                  assign_ext_types_list
    );
    puth "#endif\n";
    flush occ; close_out occ;
    flush och; close_out och;
    flush ocl; close_out ocl;
    flush consts_h_oc; close_out consts_h_oc;
    flush consts_c_oc; close_out consts_c_oc;
    Printf.printf "%s has been generated.\n" loopfile;
    Printf.printf "%s has been generated.\n" hfile;
    Printf.printf "%s has been generated.\n" cfile;
    flush stdout;
    let node = args.Lv6MainArgs.main_node in
    let execfile = if args.Lv6MainArgs.outfile = "" then (node^".exec")
      else args.Lv6MainArgs.outfile 
    in
    let cflags = try Sys.getenv "CFLAGS" with Not_found -> "" in
    let ocsh = open_out (node ^".sh") in
    let main_file, ogensim_main_file, gcc = 
      if Lv6MainArgs.global_opt.Lv6MainArgs.gen_wcet then 
        base^"_main.c",base^"_loop.c","$gcc --specs=linux.specs -g" 
      else 
        loopfile, "I am a dead string...", "gcc"
    in
    let ogensim_exe = node^"4ogensim.exec" in
    let cfiles_acc = if needs_cfile then ext_cfile::cfiles_acc else cfiles_acc in
    let cfiles = String.concat " " cfiles_acc in
    let gcc, gcc_ogensim =
        Printf.sprintf "%s -o %s \\\n\t%s %s %s"
                       gcc execfile cfiles cflags main_file,
        Printf.sprintf "%s -o %s \\\n\t%s %s %s"
                       gcc ogensim_exe cfiles cflags ogensim_main_file
    in
    let main_step = (string_of_soc_key msoc)^"_step" in
    let gcc = if Lv6MainArgs.global_opt.Lv6MainArgs.gen_wcet then
        ("#!/bin/bash
set -x

otawa=\"true\"
ogensim=\"true\"
xpdf=\"true\"

OGENSIM=${OGENSIM:-\"osim.arm\"}
OTAWA=${OTAWA:-\"owcet.arm\"}
ORANGE=${ORANGE:-\"orange\"}
LUSTREV6=${LUSTREV6:-\"lus2lic\"}
fixffx=${fixffx:-\"fixffx\"}
mkff=${mkff:-\"mkff\"}
lutin=${lutin:-\"lutin\"}
rdbg=${rdbg:-\"rdbg-batch\"}
getstat=${getstat:-\"getstat.r\"}
gcc=${gcc:-\"arm-elf-gcc\"}


if [ $# -gt 0 ]
    then
    case \"$1\" in
        \"otawa\")
            otawa=\"true\"
            ogensim=\"false\"
            xpdf=\"false\"
            ;;
        \"ogensim\")
            otawa=\"false\"
            ogensim=\"true\"
            xpdf=\"false\"
            ;;
        \"both\")
            otawa=\"true\"
            ogensim=\"true\"
            xpdf=\"false\"
            ;;
    esac
fi
cfile="^cfile^ "
execfile="^execfile^ "
main_step="^main_step^ "
n="^node^ "
n_n="^base ^ "
freeport=`python -c 'import socket; s=socket.socket(); s.bind((\"\", 0)); print(s.getsockname()[1]); s.close()'`
if [ \"$otawa\" = \"true\" ]
then
# ZZZ otawa won't work with programs that use division because of orange (!?)
# Let's compile the c files for otawa\n" ^
          gcc ^ " > \\\n\t$n_n.owcet.log 2>&1 &&\n\n"^
            "$ORANGE $cfile ${n_n}_step -o $n_n.ffx > $n_n.orange.log  2>&1 &&\n" ^

"
IDIR=`readlink -f fixffx`
IDIR=`dirname \"$IDIR\"`
ARM_LOOPLOC=\"$IDIR/arm.looploc\"

$mkff -x $execfile > $n_n.ff
$fixffx $ARM_LOOPLOC -i $n_n.ff >  $n_n.fixed.ffx
# Let's  run otawa (owcet.arm)\n" ^
            "$OTAWA $execfile $main_step  -f $n_n.fixed.ffx -f $n_n.ffx --add-prop otawa::ilp::OUTPUT_PATH=$main_step.lp \\\n\t>"^
              "$n_n.owcet.arm.log 2>&1 && \n"^
              "grep WCET $n_n.owcet.arm.log | cut -d \"=\" -f 2 > "^
              "$n.wcet &&\n\n" ^ 
            "WCET=`cat $n.wcet` \n\n" ^ 
              "# Let's compile the c files for ogensim \n" ^
              "
fi
if [ \"$ogensim\" = \"true\" ]
then
# Now let's run ogensim \n(" ^
           gcc_ogensim ^ " >>$n_n.owcet.log  2>&1 &&\n"^
           "$LUSTREV6 "^(String.concat " " args.Lv6MainArgs.infiles)^" -n $n"^
           " -interface > $n.io &&\n"^
           "$LUSTREV6 "^(String.concat " " args.Lv6MainArgs.infiles)^" -n $n --gen-autotest &&\n\n"^
             "# Now let's run ogensim \n" ^
           "($OGENSIM "^ogensim_exe^" -ul 1 \\\n\t-e $main_step"^
           " -cl $n.cycles -lp $freeport \\\n\t-iol $n.io > $n_n"^
             ".ogensim.log  2>&1&) && \n\nsleep 1 &&\n"^
           "($rdbg -lurette -l 1000 -o "^node^".rif \\\n\t \
            --sut-socket \"127.0.0.1:$freeport\"  \\\n\t \
            --env-stdio \"$lutin -boot -rif _${n}_env.lut -n ${n}_env\" || true)) &&\n\n"^
             "$getstat $n.cycles $WCET > $n.stat
fi
if [ \"$xpdf\" = \"true\" ]
then
           xpdf $n.cycles.pdf &
fi
"
        )
      else gcc
    in
    output_string ocsh (gcc^"\n\n"); 
    flush ocsh; 
    close_out types_h_oc;
    close_out ocsh;
    let call_script = "sh "^node^".sh" in
    let call_exec = "./"^node^".exec" in
    if args.Lv6MainArgs.launch_cc then (
      if (Sys.command call_script)=0 then (
        if args.Lv6MainArgs.launch_exec then (
          if (Sys.command call_exec)=0 then (
          ) else
            print_string ("sys call: '"^call_exec^"' failed\n")
        )
      ) else
        print_string ("sys call: '"^call_script^"' failed\n")
    ) else
      print_string ("you can compile those files calling:  "^call_script^"\n");
    flush stdout
    with
      | Delete_C_files -> 
         close_out types_h_oc;
         close_out occ;
         close_out och;
         close_out ocl;
         if Sys.file_exists hfile then Sys.remove hfile;
         if Sys.file_exists cfile then Sys.remove cfile;
         if Sys.file_exists ext_cfile then Sys.remove ext_cfile;
         if Sys.file_exists ext_hfile then Sys.remove ext_hfile;
         if Sys.file_exists loopfile then Sys.remove loopfile;
         exit 2