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

-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} -> parse_line(Line)
	end.

parse_line(Line) ->
    [Cave1, Cave2] = string:tokens(Line, "-\n"),
    {Cave1, Cave2}.

solution1(Caves) ->
   CaveMap = insert(Caves),
   count_paths("start", #{}, 'some', CaveMap).

solution2(Caves) ->
   CaveMap = insert(Caves),
   count_paths("start", #{}, 'none', CaveMap).


insert(Caves) -> insert(Caves, maps:new()).

insert([], CaveMap) -> CaveMap;
insert([{Cave1, Cave2}|Rest], CaveMap) ->
	NewMap = 
	case maps:is_key(Cave1, CaveMap) of
		'false' -> maps:put(Cave1, [Cave2], CaveMap);
		'true' -> maps:put(Cave1, [Cave2|maps:get(Cave1, CaveMap)], CaveMap)
	end,
	NewMap2 = 
        case maps:is_key(Cave2, NewMap) of
                'false' -> maps:put(Cave2, [Cave1], NewMap);
                'true' -> maps:put(Cave2, [Cave1|maps:get(Cave2, CaveMap)], NewMap)
        end,
	insert(Rest, NewMap2).

visit_vertex(Name, Visited, VisitedTwice, Graph) ->
    NewVisited =
        case hd(Name) >= $a of
            true -> Visited#{Name => true};
            false -> Visited
        end,
    #{Name := Neighbours} = Graph,
    lists:sum([count_paths(N, NewVisited, VisitedTwice, Graph) || N <- Neighbours]).

count_paths("end", _, _, _) ->
    % We have found an entire path through the graph.
    1;
count_paths("start", Visited, _, _) when map_size(Visited) > 0 ->
    % We cannot visit the start cave more than once.
    0;
count_paths(Name, Visited, VisitedTwice, Graph) when hd(Name) >= $a andalso is_map_key(Name, Visited) ->
    % We can visit only one small cave twice.
    case VisitedTwice of
        'none' -> visit_vertex(Name, Visited, Name, Graph);
         _ -> 0
    end;
count_paths(Name, Visited, VisitedTwice, Graph) ->
    % Small caves can only be visited once, but large caves may be visited any
    % number of times.
    visit_vertex(Name, Visited, VisitedTwice, Graph).
