#!/usr/bin/env escript

%% needs:
%% - line parsing
%% - keyboard input
%% - drawing to screen

-mode(compile).

% john state
-record(js,
        {rcv   = <<>> :: binary(),
         kbdq  = []   :: iolist(),
         ibuf  = []   :: string(),
         tsock = none :: none | gen_tcp:socket(),
         nsock = none :: none | enoise:noise_socket()}).


main([])        -> main_ii(6969);
main([PortNum]) -> main_ii(list_to_integer(PortNum)).

main_ii(PortNum) ->
    TcpOpts = [binary, {packet, 0}, {active, true}],
    {ok, TSock} = gen_tcp:connect("localhost", PortNum, TcpOpts),
    %ok = inet:setopts(TSock, [{active, once}]),
    {ok, NSock, _HandshakeState} = enoise:connect(TSock, noise_options()),
    % need keyboard process
    Self = self(),
    spawn_link(fun() -> kbd_loop(Self) end),
    j_loop(#js{tsock = TSock, nsock = NSock}).

noise_options() ->
    % totally safe crypto
    KP = enoise_keypair:new(dh25519),
    [{noise, "Noise_NN_25519_ChaChaPoly_BLAKE2b"},
     {e, KP}].


j_loop(JS = #js{rcv = Rcv, tsock = TSock, nsock = NSock}) ->
    %ok = inet:setopts(TSock, [{active, once}]),
    receive
        {noise, NSock, Bytes} ->
            NewRcv = <<Rcv/binary, Bytes/binary>>,
            io:format("NewRcv = ~p~n", [NewRcv]),
            NewJS = j_received(NewRcv, JS),
            j_loop(NewJS);
        {kbd, line, Line} ->
            NewJS = j_kbdln(Line, JS),
            j_loop(NewJS);
        {tcp_closed, TSock} ->
            io:format("tcp socket closed unexpectedly, exiting~n"),
            exit(tcp_closed);
        Unknown ->
            ok = io:format("unknown message ~tp~n", [Unknown]),
            j_loop(JS)
    end.

j_kbdln(Line, JS = #js{nsock = NS}) ->
    FinalMsg = unicode:characters_to_binary([string:chomp(Line), "\r\n"]),
    ok = enoise:send(NS, FinalMsg),
    JS.

% not the time for readline
% % user hit return, flush queue
% j_kbd("\r", JS = #js{kbdq = KQ, nsock = NS}) ->
%     FinalMsg = unicode:characters_to_binary([KQ, "\r\n"]),
%     ok = enoise:send(NS, FinalMsg),
%     NewJS = JS#js{kbdq = []}
%     NewJS;
% % user hit something else, add to queue
% % fixme: parse ANSI escape sequences here, maybe xterm
% j_kbd(Str1, JS = #js{kbdq = KQ}) ->
%     JS#js{kbdq = KQ ++ Str1}.


j_received(Rcv, JS) ->
    case takeln(Rcv) of
        none ->
            JS#js{rcv = Rcv};
        {line, Line, Rcv2} ->
            ok = j_rln(Line),
            j_received(Rcv2, JS)
    end.


j_rln(Line) ->
    io:format("< ~ts~n", [string:chomp(Line)]).

takeln(X) ->
    takeln(<<>>, X).

takeln(Acc, <<"\r\n", Rest/binary>>) ->
    {line, <<Acc/binary, "\r\n">>, Rest};
takeln(Acc, <<B:8, Rest/binary>>) ->
    takeln(<<Acc/binary, B:8>>, Rest);
takeln(_Acc, <<>>) ->
    none.


kbd_loop(Parent) ->
    Line = io:get_line("> "),
    Parent ! {kbd, line, Line},
    kbd_loop(Parent).

%kbd_loop(Parent) ->
%    Char = io:get_chars("", 1),
%    Parent ! {kbd, Char},
%    kbd_loop(Parent).
