%% to compile: erlc day3A.erl
%% to run: erl -noshell -s day3 solve
%%
-module(day10).

-export ([solve/0, solve/1, solve/2]).
-export ([read_input/0]).

solve() ->
    solve(['1']),
    solve(['2']),
    init:stop().

solve(A) ->
    solve(A, read_input()).

solve(['1'], D) ->
    io:format("The solution to ~p puzzle1 is: ~p~n", [?MODULE, solve(1, D)]);
solve(1, D) ->
    solution1(D);
solve(['2'], D) ->
    io:format("The solution to ~p puzzle2 is: ~p~n", [?MODULE, solve(2, D)]);
solve(2, D) ->
    solution2(D).

read_input() ->
	{ok, IO} = file:open("input.txt", 'read'),
	Data = read_input(IO),
	file:close(IO),
        Data.

read_input(IO) ->
	read_input(IO, []).

read_input(IO, Input) ->
	case read_line(IO) of
		'eof' -> Input;
		Line -> read_input(IO, Input ++ [Line])
	end.

read_line(IO) ->
	case file:read_line(IO) of
		'eof' -> 'eof';
		{ok, Line} -> [ X || X <- Line, [X] /= "\n"]
	end.

solution1(Input) ->
    lists:sum([parse(X) || X <- Input]).

solution2(Input) ->
    List = [incomplete(X) || X <- Input],
    Filter_fail = lists:filter(fun(X) -> X /= 'failed' end, List),
    Scores = lists:sort([lists:foldl(fun(X, Acc) -> (Acc * 5) + value(X) end, 0, F) || F <- Filter_fail]),
    lists:nth(length(Scores) div 2 + 1, Scores).

incomplete([H|T] = Input) ->
    io:format("~p~n", [Input]),
    try
        incomplete(T, [partner(H)])
    of
	ok -> 'ok';
	{incomplete, Rest} -> Rest
    catch
        _:_ -> 'failed' 
    end.

incomplete([], Expected) -> {incomplete, Expected};
incomplete(Rest, []) ->  {incomplete, incomplete(Rest)};

incomplete([H|T], [H|Rest]) -> incomplete(T, Rest);
incomplete([H|T], Expected) -> incomplete(T, [partner(H)|Expected]).

parse([H|T] = Input) ->
    io:format("~p~n", [Input]),
    try
        parse(T, [partner(H)])
    of
	_ -> 0
    catch
        _:{invalid_partner,")"} -> 3;
        _:{invalid_partner,"]"} -> 57;
        _:{invalid_partner,"}"} -> 1197;
        _:{invalid_partner,">"} -> 25137
    end.

parse([], Expected) -> {parsed, Expected};
parse(Rest, []) ->  {incomplete, parse(Rest)};

parse([H|T], [H|Rest]) -> parse(T, Rest);
parse([H|T], Expected) -> parse(T, [partner(H)|Expected]).

partner($() -> $);
partner($[) -> $];
partner(${) -> $};
partner($<) -> $>;
partner(X) -> throw({invalid_partner, [X]}).

value($)) -> 1;
value($]) -> 2;
value($}) -> 3;
value($>) -> 4.
