-module(wiki_split).

%% File    : wiki_format_txt.erl
%% Author  : Joe Armstrong (joe@bluetail.com)
%%         : Johan Bevemyr, minor modifications
%% Purpose : Wiki formatting engine
%%
%% Split the text. Looking for blocks
%%      <         is represented as {open,Tag,string()}
%%        ...
%%      >
%%      <         is represented as {write_append,Tag,string()}
%%        ...
%%      >
%%
%%      Everything else is represented as {txt, Tag, string()}

%% +type str2wiki(string())                             -> wikiText().
%% +type wiki2str(wikiText())                           -> string().
%% +type getRegion(tag(), wikiText())                   -> string().
%% +type putRegion(tag(), wikiText(), string())         -> wikiText().
%% +type writeAppendRegion(tag(), wikiText(), string()) -> wikiText().

%% +deftype wikiText() = {wik, [{text,tag(),string()} |
%%                              {open,tag(),string()} |
%%                              {write_append, tag(), string()}]}
%% +deftype tag() = int().

-export([str2wiki/1, wiki2str/1,
         getRegion/2, putRegion/3, writeAppendRegion/3]).

str2wiki(Str) ->
    Blocks = str2wiki(Str, []),
    {wik, number_blocks(Blocks, 1)}.

number_blocks([{txt,[]}|T], N)  -> number_blocks(T, N);
number_blocks([{Tag,Str}|T], N) -> [{Tag,N,Str}|number_blocks(T, N+1)];
number_blocks([], _)            -> [].

str2wiki(Str, L) ->
    {Before, Stuff} = collect_str(Str),
    case Stuff of
        "<<\n" ++ T ->
            {In, Str3} = collect_write_append([$\n|T], []),
            str2wiki(Str3, [{write_append,In},{txt,Before}|L]);
        "<\n" ++ T ->
            {In, Str3} = collect_open_region([$\n|T], []),
            str2wiki(Str3, [{open,In},{txt,Before}|L]);
        [] ->
            lists:reverse([{txt,Before}|L])
    end.

%% collect_str(Str) -> {Str1, Str2}
%% where Str2 == [], "<" ++ _ | "<<" ++ _

collect_str(Str) -> collect_after_nl(Str, []).

collect_after_nl(S = "<<\n" ++ _, L) -> {lists:reverse(L), S};
collect_after_nl(S = "<\n" ++ _, L)  -> {lists:reverse(L), S};
collect_after_nl(X, L)               -> collect_str(X, L).

collect_str([$\n|T], L) -> collect_after_nl(T, [$\n|L]);
collect_str([H|T], L)   -> collect_str(T, [H|L]);
collect_str([], L)      -> {lists:reverse(L), []}.

collect_write_append("\n>>\n" ++ T, L) -> {lists:reverse([$\n|L]), [$\n|T]};
collect_write_append([H|T], L)         -> collect_write_append(T, [H|L]);
collect_write_append([], L)            -> {lists:reverse(L), []}.

collect_open_region("\n>\n" ++ T, L) -> {lists:reverse([$\n|L]), [$\n|T]};
collect_open_region([H|T], L)        -> collect_open_region(T, [H|L]);
collect_open_region([], L)           -> {lists:reverse(L), []}.

%% wiki2str.

wiki2str({wik,L})                  -> sneaky_flatten(wiki2str1(L)).

wiki2str1([{txt,_,Str}|T])          -> [Str|wiki2str1(T)];
wiki2str1([{open,_,Str}|T])         -> ["<\n",Str,"\n>"|wiki2str1(T)];
wiki2str1([{write_append,_,Str}|T]) -> ["<<\n",Str,"\n>>"|wiki2str1(T)];
wiki2str1([])                       -> [].

sneaky_flatten(L) ->
    binary_to_list(list_to_binary(L)).

getRegion(Tag, {wik, L}) -> getRegion1(Tag, L).

getRegion1(Tag, [{Type,Tag,Str}|_]) -> {Type, Str};
getRegion1(Tag, [_|T])              -> getRegion1(Tag, T).

putRegion(Tag, {wik, L}, Str1) -> {wik, putRegion1(Tag, L, Str1)}.

putRegion1(Tag, [{Type,Tag,_}|T], New) -> [{Type,Tag,New}|T];
putRegion1(Tag, [H|T], New)            -> [H|putRegion1(Tag, T, New)].

writeAppendRegion(Tag, Wik, Str) ->
    Str1 = getRegion(Tag, Wik),
    putRegion(Tag, Wik, Str ++ Str1).








