-module(zntrees).
-export([root/1, value/1,
replace/2, insert/2, delete/1,
left/1, right/1, children/1, parent/1, rparent/1]).
-type zlist(A) :: {Left::list(A), Right::list(A)}.
-type znode() :: zlist({term(), zlist(_)}). % znode is a zlist of nodes
-type thread() :: [znode()].
-type zntree() :: {thread(), znode()}.
%% creates a tree with an empty thread and Val as the root.
-spec root(term()) -> zntree().
root(Val) -> {[], {[], [{Val, {[], []}}]}}.
%% Extracts the node's value from the current tree position.
-spec value(zntree()) -> term().
value({_Thread, {_Left, [{Val, _Children} | _Right]}}) -> Val.
%% Replaces the value from at the current tree position, without touching
%% the children nodes.
-spec replace(term(), zntree()) -> zntree().
replace(Val, {T, {L, [{_Val, Children}|R]}}) ->
{T, {L, [{Val,Children}|R]}}.
%% Add a new node at the current position with the value Val.
-spec insert(term(), zntree()) -> zntree().
insert(Val, {Thread, {L, R}}) ->
{Thread, {L, [{Val, {[], []}} | R]}}.
%% Deletes the node at the current position and its children.
%% The next one on the right becomes the current position.
-spec delete(zntree()) -> zntree().
delete({Thread, {L, [_|R]}}) ->
{Thread, {L, R}}.
%% Moves to the left of the current level.
-spec left(zntree()) -> zntree().
left({Thread, {[H|T], R}}) ->
{Thread, {T, [H|R]}}.
%% Moves to the right of the current level
-spec right(zntree()) -> zntree().
right({Thread, {L, [H|T]}}) ->
{Thread, {[H|L], T}}.
%% Goes down one level to the children of the current node.
%% Note that in order for this to work, the {Val, Children} tuple
%% needs to be broken in two: the value goes in the Thread's zlist
%% while the Children become the current level.
-spec children(zntree()) -> zntree().
children({Thread, {L, [{Val, Children}|R]}}) ->
{[{L,[Val|R]}|Thread], Children}.
%% Moves up to the direct parent level. Doesn't rewind the current
%% level's zlist. This means that if you have a tree, go to the
%% children, browse to the right 2-3 times, then go back up and
%% down to the children again, you'll be at the same position you were
%% before.
%% If you prefer the children to be 'rewinded', use rparent/1.
-spec parent(zntree()) -> zntree().
parent({[{L, [Val|R]}|Thread], Children}) ->
{Thread, {L, [{Val, Children}|R]}}.
%% Moves up to the direct parent level, much like parent/1. However,
%% it rewinds the current level's zlist before doing so. This allows
%% the programmer to access children as if it were the first time,
%% all the time.
-spec rparent(zntree()) -> zntree().
rparent({[{ParentL, [Val|ParentR]}|Thread], {L, R}}) ->
{Thread, {ParentL, [{Val, {[], lists:reverse(L)++R}}|ParentR]}}.