open Printf
open Parseopt

(** This file is a detailed, somewhat contrived example to show various ways of using pa_arg.

To see how the options work in this file, look at this transcript of interactions with this program.

If pa_arg is installed with findlib, you can compile this file with

ocamlfind ocamlc -package pa_arg -syntax camlp4o -linkpkg example.ml -o example

(or likewise with ocamlopt for a binary version)

*)



type option myOptions = {

  
  (** These fields have only the default behavior *)


  alpha               : int; 
  bravo               : string;
  charlie             : float;
  delta               : bool; (* 3 possible vals: Some true, Some false, None *)
  echo                : unit; (* 2 vals: Some (), None (effectively boolean) *)

  
  (** These fields are slightly more complicated *)


  foxtrot             : [ `Ecks | `Why | `ReallyLongConstructorName "zee" ];
                        (* symbol: provided value must be ecks | why | zee *)
  golf = 3            : action = store 6; int;
  india = true        : action = clear; bool; (* -i sets india to false; 
                                                 else it's true *)

  illinois            : key = ["-j"]; string; (* set key to -j (otherwise 
                                                 would conflict with india) *)


  kilo                : str = (Printf.sprintf "%.5f"); float; (* change print 
                                                                 behavior *)

  mutable lima = false: action = set; bool;

  (* some yet more complex behavior *)

  (* a silly example of how a more complex type can be used.  *)
  mike = (0,0)        : str = (fun (i0,i1) -> Printf.sprintf "%d,%d" i0 i1); 
                        conversion = 
                          (fun _ s -> Scanf.sscanf s "%d,%d" (fun a b ->a,b));
                        (int * int);

  (* re-use one of the variants from above *)
  november = `Ecks    : [`Ecks | `You | `Vee];

  oscar = `Off        : action = store `On; [ `On | `Off];

  
  (** every appearance of -v increases the verbosity level by one *)

  verbosity = 0       : action = (fun o -> succ o.options.verbosity); 
                        help = "increase verbosity level"; int;

  print = false       : action = set; help = "display received opts, args";bool;

  
  (** these options have no corresponding field *)

  _                   : handler = 
                        (fun _ -> print_string "$Revision: 1.10 $\n"; exit 0);
                        key = ["-V"]; help = "print version and exit"; unit;

  _                   : handler = (fun _ -> print_string 
                         "Available commands:\ntag\nlog\n"; exit 0);
                        key = ["-L"]; help = "display available commands"; unit;

  _                   : handler = (fun ap -> 
                        printf "Default values:\n%s\n" 
                        (ap.option_parameters.to_string
                        ap.option_parameters.default); exit 0);
                        key = ["-D"]; help = "display default values"; unit;

  
  (** This option passes off parsing duties to the standard Arg module *)

  
  (** I'm not sure why you'd want to do this, but if you want to... *)

  tango               : handler = (fun ap -> 
    let anonargs = ref [] in 
    let alpha = ref "default-string" in
    let beta = ref 0xBADBEEF in
    Arg.parse_argv (Array.of_list ("progname"::ap.args))
    ["-a",Arg.Set_string alpha,"set alpha";"-beta",Arg.Set_int beta,"set beta"
    (fun anon -> anonargs:= anon::!anonargs) "example -A [OPTS] args";
    {ap.options with tango=Some (!alpha,!beta)},(List.rev !anonargs)); 
    help = "an Arg parser"; str = (fun (s,i) -> sprintf "%s/%08X" s i);
                        (string * int);


(* override some of the default option-parsing behavior *)
let myOptions = {myOptions with
    usage_suffix = "command [sub-options]" ;
    parsetype = Gnu ; (* allow -ane 'arg' instead of -a -n -e 'arg' *)
    keyspecs  = [Short;Long2]; (* field foo generates keys -f,--foo *)
    keepgoing = false (* This allows processing of subcommands' options *)
}

type option logOptions = {
  date       : string;
  log = false: action = set; bool;
}
let logOptions = {logOptions with usage_prefix = "log "}

type option tagOptions = {
  all_users = false: action = set; bool;
  tag              : string;
}
let tagOptions = {tagOptions with usage_prefix = "tag "}

let () = 
  let options, args = parse_argv myOptions in

  (* This just shows that this field is indeed mutable *)
  options.lima <- false;

  (* Accessing option fields is easy *)
  if options.verbosity > 1 then Printf.printf "really verbose!\n";

  (* Here's a way to process subcommands, like ocamlfind or cvs *)
  (* Note that these subcommands have the default option parameters:
     parsetype = Caml; keyspecs = [Short | Long1]; keepgoing = true; *)


  if options.print then begin
    printf "Received options:\n%s\n" (myOptions.to_string options); 
    printf "Received anonymous arguments: %s\n" (String.concat " " (args));
  end;

  match args with
  | "tag" :: args -> 
      let o,args = parse tagOptions args in
      Printf.printf "Received suboptions:\n%s\n" (tagOptions.to_string o);
      Printf.printf "Received subarguments %s\n" (String.concat " " args);
  | "log" :: args -> 
      let o,args = parse logOptions args in
      Printf.printf "Received suboptions:\n%s\n" (logOptions.to_string o);
      Printf.printf "Received subarguments %s\n" (String.concat " " args);
  | _ ->
    Printf.printf "no command specified\n";
    Printf.printf "Use -h option for detailed help\n";
    usage myOptions