open Unix;;

let quit = "q"

type application_state = {
  mutable port : int;
  mutable host : string;
  mutable user : string;
  mutable password : string option;
  mutable use_stdin : bool;
  mutable quiet : bool;
  mutable verbose : bool;
  }
  
let create_state () = {
  port = 4000;
  host = "localhost";
  user = "admin";
  password = None;
  quiet = false;
  verbose = false;
  use_stdin = false;
  }

let commands_from_stdin =
  let one_command _ = 
    try
      Some (read_line ())
    with _ ->
      None
  in
  Stream.from one_command

type server_state = S_1 | S_2 | S_3 | End

let mldonkey_result (from_server,to_client) =
  let rec process_server state =
    try
      let new_char = input_char from_server
      in
      Buffer.add_char to_client new_char;
      match (new_char, state) with
      ('m',S_1) ->
        process_server S_2
      | ('\n',S_2) ->
        process_server S_3
      | ('>', S_3) ->
        process_server End
      | (' ',End) ->
        ()
      | _ ->
        process_server S_1
    with End_of_file ->
      ()
  in
  process_server S_1;
  Buffer.add_char to_client '\n'
    

let mldonkey_login (from_server,to_server) user password =
  (* Approximate size of the login panel *)
  let to_client = Buffer.create 600
  in
  let auth =
    let real_password = 
      match password with 
      Some(x) -> x
      | None ->
        begin
        prerr_string "Password: ";
        flush_all ();
        read_line ()
        end
    in
    "auth \""^user^"\" \""^real_password^"\"\n"
  in
  mldonkey_result (from_server,to_client);
  output_string to_server auth;
  flush to_server;
  mldonkey_result (from_server,to_client);
  to_client
  
let mldonkey_command (from_server,to_server) strm_commands = 
  (* Arbitrary size *)
  let to_client = Buffer.create 1000
  in
  let one_command cmd  =
    output_string to_server (cmd ^ "\n");
    flush to_server;
    mldonkey_result (from_server,to_client)
  in
  Stream.iter one_command strm_commands;
  to_client

let mldonkey_logout (from_server,to_server) = 
  (* Approximate size of the quit command *)
  let to_client = Buffer.create 30
  in
  output_string to_server "q\n";
  flush to_server;
  mldonkey_result (from_server,to_client);
  to_client

let _ = 
  let commands = ref []
  in
  let state = create_state ()
  in
  let _ = Arg.parse [
    ("-P", Arg.Int ( fun x -> state.port <- x ), "Port of the telnet mldonkey server");
    ("-h", Arg.String ( fun x -> state.host <- x ), "Host of the telnet mldonkey server");
    ("-u", Arg.String ( fun x -> state.user <- x ), "Username to use while connecting");
    ("-p", Arg.String ( fun x -> state.password <- Some x ), "Password to use while connecting");
    ("-q", Arg.Unit ( fun () -> state.quiet <- true ), "Run without displaying command result");
    ("-s", Arg.Unit ( fun () -> state.use_stdin <- true ), "Use stdin to enter command");
    ("-v", Arg.Unit ( fun () -> state.verbose <- true ), "Display login information") ]
    ( fun x -> commands := x :: !commands )
    "Usage mldonkey_command [options] list_of_command \n where options are:"
  in
  let _ = 
    if state.verbose then 
    begin
      print_string "MLDonkey command sender";
      print_newline ();
      Array.iter ( fun x -> print_string (x^" ") ) Sys.argv;
      print_newline ()
    end
    else
      ()
  in
  let hostent = gethostbyname state.host
  in
  let inet_host = Array.get hostent.h_addr_list 0
  in
  let strm_commands =
    if state.use_stdin then
      commands_from_stdin
    else
      Stream.of_list !commands
  in
  let (from_server, to_server) = 
    open_connection (ADDR_INET (inet_host, state.port))
  in
  let result_login = mldonkey_login (from_server,to_server) 
    state.user state.password
  in
  let result_commands = mldonkey_command (from_server,to_server) 
    strm_commands
  in
  let result_logout = mldonkey_logout (from_server,to_server)
  in
  shutdown_connection from_server;
  begin
  if state.verbose then
    Buffer.output_buffer Pervasives.stdout result_login
  else
    ()
  end;
  begin
  if state.quiet then
    ()
  else
    Buffer.output_buffer Pervasives.stdout result_commands
  end;
  begin
  if state.verbose then
    Buffer.output_buffer Pervasives.stdout result_logout
  else
    ()
  end;
  ()

