#!/usr/bin/env escript
-mode(compile).
% -include_lib("wx/include/wx.hrl").


cmds1() ->
  "Supported commands: init, updateschema (-us), updatenodes (-un), stat (-s), help (-h)~n".
cmds() ->
  cmds1()++
  "Stat columns:~n"++
  "allreads - total number of reads since node started~n"++
  "readsnow - number of reads in the past second~n"++
  "allwrites - total number of writes since node started~n"++
  "writesnow - number of writes in the past second~n"++
  "nactors - total number of actors active~n"++
  "nactive - number of actors that have been active in the past second~n"++
  "tmngrs - num_busy_transaction_managers/total_transaction_managers~n".

wxev(_Dlg) -> ok.
%   receive
%     #wx{id=Id, event=#wxCommand{type=command_button_clicked}} ->
%       case Id >= 1 andalso Id =< 3 of
%         true ->
%           % Txt = wx:typeCast(wxWindow:findWindowById(4, [{parent, Dlg}]),wxTextCtrl),
%           % Btn = wx:typeCast(wxWindow:findWindowById(Id, [{parent, Dlg}]),wxButton),
%           % wxTextCtrl:setValue(Txt,"you pressed "++wxButton:getLabel(Btn)),
%           case Id of
%             1 ->
%               doit(["init"],Dlg);
%             2 ->
%               doit(["updateschema"],Dlg);
%             3 ->
%               doit(["updatenodes"],Dlg)
%           end;
%           % wxev(Dlg);
%         false ->
%           ok
%       end
%   end.

main([]) ->
  case os:type() of
    {win32,_} ->
      %
      % To gui or not to gui?
      %
      io:format("Command list:~n"++
                "init - for initializing cluster~n"++
                "us - update schema (from etc/schema.yaml)~n"++
                "un - update nodes (from etc/nodes.yaml)~n"++
                "s - print stats~n"++
                "q or exit - for exit~n"),
      Read = io:fread("Command: ", "~s"),
      io:format("~n"),
      case Read of
        {ok,["q"]} ->
          ok;
        {ok,["exit"]} ->
          ok;
        {ok,["init"]} ->
          doit(["init"],winconsole);
        {ok,[Cmd]} ->
          case ok of
            ok when Cmd == "us"; Cmd == "un"; Cmd == "s" ->
              doit(["-"++Cmd],winconsole);
            _ ->
              io:format("Invalid command: ~p~n",[Cmd]),
              main([])
          end;
        _ ->
          ok
      end;

      % Wx = wx:new(),
      % Dlg = wxDialog:new(Wx,-1,"Pick command"),
      % Sizer = wxBoxSizer:new(?wxVERTICAL),
      % BtnSizer = wxStaticBoxSizer:new(?wxHORIZONTAL, Dlg, [{label, "Commands"}]),
      % BtnInit = wxButton:new(Dlg, 1, [{label,"Init"}]),
      % BtnSchema = wxButton:new(Dlg, 2, [{label,"Update schema"}]),
      % BtnNds = wxButton:new(Dlg, 3, [{label,"Update nodes"}]),
      % Text = wxTextCtrl:new(Dlg,4,[{style, ?wxDEFAULT bor ?wxTE_MULTILINE bor ?wxTE_READONLY}]),
      % SzFlags = [{proportion, 0}, {border, 4}, {flag, ?wxALL}],
      % [wxSizer:add(BtnSizer,B,SzFlags) || B <- [BtnInit,BtnSchema,BtnNds]],
      % wxSizer:add(Sizer,BtnSizer,SzFlags),
      % wxSizer:add(Sizer,Text,[{flag, ?wxEXPAND},{proportion, 1}|SzFlags]),
      % wxDialog:setSizer(Dlg,Sizer),
      % wxDialog:connect(Dlg,command_button_clicked),
      % wxDialog:show(Dlg),
      % wxev(Dlg);
    _ ->
      main(["nocmd"])
  end;
main(Args) ->
  doit(Args,console).

doit([_,_] = Args,Ctx) ->
  [] = os:cmd(epmd_path(Ctx) ++ " -daemon"),
  case Args of
    [EtcPath1x,Cmd1] ->
      EtcPath1 = EtcPath1x ++ "/etc"
  end,
  case Cmd1 of
    "-us" ->
      Cmd = "updateschema";
    "-un" ->
      Cmd = "updatenodes";
    "-s" ->
      Cmd = "stat";
    "updateschema" = Cmd ->
      ok;
    "updatenodes" = Cmd ->
      ok;
    "stat" = Cmd ->
      ok;
    "stats" ->
      Cmd = "stat";
    "init" = Cmd ->
      ok;
    _ ->
      Cmd = "",
      case Cmd1 of
        "-h" ->
          io:format(cmds());
        "help" ->
          io:format(cmds());
        _ ->
          print(Ctx,"~nInvalid command.~n~n"++cmds())
      end,
      done(Ctx)
  end,
  EtcPath2 = "{{platform_etc_dir}}",
  case filelib:is_file(EtcPath2++"/app.config") of
    true ->
      EtcPath = EtcPath2;
    false ->
      EtcPath = EtcPath1
  end,
  {ok,VmBin} = file:read_file(EtcPath++"/vm.args"),
  Lines = string:tokens(binary_to_list(VmBin),"\r\n"),
  [{node,Node},{myname,Myname}] = parse_args(Lines,[]),

  case net_kernel:hidden_connect_node(Node) of
    true ->
      case net_adm:ping(Node) of
        pang ->
          print(Ctx,"ActorDB not responding~n"),
          done(Ctx);
        pong ->
          ok
      end;
    _X ->
      print(Ctx,"ActorDB not running ~n"),
      done(Ctx)
  end,
  case Cmd of
    "stat" ->
      Ref = make_ref(),
      {ok,Cols} = rpc:call(Node,actordb_cmd,cmd,[stats,describe,ok]),
      [ok = print(Ctx,"~s",[string:right(Val,15,$\s)]) || Val <- tuple_to_list(Cols)],
      print(Ctx,"~n"),
      ok = rpc:call(Node,actordb_cmd,cmd,[stats,stats,{Myname,self(),Ref}]),
      rec_print_stats(Ctx,Ref,Cols);
    _ ->
      case rpc:call(Node,actordb_cmd,cmd,[list_to_atom(Cmd),parse,EtcPath]) of
        {ok,nochange} ->
          print(Ctx,"No changes detected~n");
        {ok,Out} ->
          print(Ctx,"~s~n",[Out]),
          case do_confirmation(Ctx) of
            yes ->
              Result = rpc:call(Node,actordb_cmd,cmd,[list_to_atom(Cmd),commit,EtcPath]),
              print(Ctx,"~s~n",[Result]),
              done(Ctx);
            no ->
              ok
          end;
        {_,E} ->
          print(Ctx,"Error: ~s~n",[E]);
        E ->
          print(Ctx,"Error: ~s~n",[E])
      end
  end;
doit([Cmd],Ctx) ->
  doit([".",Cmd],Ctx).

done(console) ->
  halt(1);
done(winconsole) ->
  main([]),
  halt(1);
done(D) ->
  wxev(D),
  halt(1).

print(C,F) ->
  print(C,F,[]).
print(console,F,A) ->
  ok = io:format(F,A);
print(winconsole,F,A) ->
  ok = io:format(F,A);
print(Dlg,F,A) ->
  Txt = wx:typeCast(wxWindow:findWindowById(4, [{parent, Dlg}]),wxTextCtrl),
  wxTextCtrl:appendText(Txt,io_lib:format(F,A)),
  ok.

rec_print_stats(Ctx,Ref,Cols) ->
  receive
    {Ref,Vals} ->
      [begin
          case is_integer(Val) of
            true ->
              ok = print(Ctx,"~s",[string:right(integer_to_list(Val),15,$\s)]);
            false ->
              ok = print(Ctx,"~s",[string:right(Val,15,$\s)])
          end
        end || Val <- tuple_to_list(Vals)],
      print(Ctx,"~n"),
      rec_print_stats(Ctx,Ref,Cols)
    after 3000 ->
      ok
  end.

do_confirmation(X) when X == console; X == winconsole ->
	case io:fread("Confirm change? (Y/N): ", "~a") of
		{ok,[y]} ->
			yes;
		{ok,[n]} ->
			no;
		_ ->
			do_confirmation(X)
	end.
% do_confirmation(Dlg) ->
%   MD = wxMessageDialog:new(Dlg,"Confirm change?",[{style,?wxYES_NO}]),
%   case wxMessageDialog:showModal(MD) of
%     ?wxID_YES ->
%       yes;
%     ?wxID_NO ->
%       no
%   end.



parse_args([" "++Rem|T],L) ->
	parse_args([Rem|T],L);
parse_args(["#"++_|T],L) ->
	parse_args(T,L);
parse_args(["-name " ++ Namestr|T],L) ->
	Curname = rem_spaces(Namestr),
	Myname = setname(Curname),
  case node() == 'nonode@nohost' of
    true ->
      {ok, _} = net_kernel:start([Myname, longnames]);
    _ ->
      ok
  end,
	parse_args(T,[{node,list_to_atom(Curname)},{myname,Myname}|L]);
parse_args(["-sname " ++ Namestr|T],L) ->
	Curname = rem_spaces(Namestr),
  Myname = setname(Curname),
  case node() == 'nonode@nohost' of
    true ->
      {ok, _} = net_kernel:start([Myname, shortnames]);
    _ ->
      ok
  end,
	parse_args(T,[{node,list_to_atom(Curname)},{myname,Myname}|L]);
parse_args(["-setcookie "++N|T],L) ->
	erlang:set_cookie(node(), list_to_atom(rem_spaces(N))),
	parse_args(T,L);
parse_args([_|T],L) ->
  parse_args(T,L);
parse_args([],L) ->
	L.


setname(Namestr) ->
	{MS,S,MiS} = now(),
	Nm = integer_to_list(MS*1000000000000 + S*1000000 + MiS),
	case string:tokens(rem_spaces(Namestr),"@") of
		[_Name,Addr] ->
			list_to_atom(Nm++"@"++Addr);
		[_Name] ->
			list_to_atom(Nm)
	end.

rem_spaces(Str) ->
	lists:filter(fun(X) -> X /= $\s end,Str).

epmd_path(Ctx) ->
  ErtsBinDir = filename:dirname(escript:script_name()),
  Name = "epmd",
  case os:find_executable(Name, ErtsBinDir) of
    false ->
      case os:find_executable(Name) of
        false ->
          print(Ctx,"Could not find epmd.~n"),
          done(Ctx);
        GlobalEpmd ->
          GlobalEpmd
      end;
    Epmd ->
      Epmd
  end.

