-module(binary).

-export([split/2, match/2, match/3, to_upper/1, to_lower/1, 
	 nth/2, nth/3, last/1, last/2, first/1, first/2, duplicate/2, 
	 strip/1, strip/2, strip/3, bin_to_unsigned/1, unsigned_to_bin/1,
         regex_first/2, regex_match/2]).


regex_first(Bin, List)->
    erlang:regex_binary(Bin, regexp:parse(List), {1, size(Bin)}).

regex_match(Bin, List)->
    not_implemented.

match(<<>>, _With)-> 0;

match(Bin, With)-> 
    erlang:match_binary(Bin, With, {1, size(Bin)}).
match(Bin, With, {Start, End})-> 
    erlang:match_binary(Bin, With, {Start, End}).



split(Bin, With) when is_binary(With)->
    dosplit(Bin, 0, match(Bin, With, {1, size(Bin)}), [], With);
split(Bin, With) ->
    domultisplit(Bin, 0, match(Bin, With, {1, size(Bin)}), [], With).

dosplit(Bin, Start, 0, Acc, _With)->
    MatchSize = size(Bin)-Start,
    <<_:Start/binary, Match:MatchSize/binary, _/binary>> = Bin,  
    lists:reverse([ Match | Acc]);

dosplit(Bin, Start, End, Acc, With) ->
    MatchSize = End-1 - Start,
    <<_:Start/binary, Match:MatchSize/binary, _/binary>> = Bin,  
    case size(Bin)-size(With) == End-1 of
	true -> 
	    lists:reverse([<<>> | [ Match | Acc]]);
	false -> 
	    dosplit(Bin, End-1 + size(With), 
		    match(Bin, With, {End-1 + size(With)+1, size(Bin)}), 
		    [ Match | Acc ], With)
    end.

domultisplit(Bin, Start, 0, Acc, _With)->
    MatchSize = size(Bin)-Start,
    <<_:Start/binary, Match:MatchSize/binary, _/binary>> = Bin,  
    lists:reverse([ Match | Acc]);

domultisplit(Bin, Start, {Key, End}, Acc, With) -> 
    MatchSize = End-1 - Start,
    <<_:Start/binary, Match:MatchSize/binary, _/binary>> = Bin,  
    case End-1 + len(Key, With)==size(Bin) of 
	true ->
	    lists:reverse([<<>> | [ Match | Acc]]);
	false ->
	    domultisplit(Bin, End - 1 + len(Key, With), 
			 match(Bin, With, {End + len(Key, With), size(Bin)}), 
			 [ Match | Acc ], With)
    end.

len(Key, With)-> size(lists:nth(Key, With)).



to_lower_char(C) when is_integer(C),  C >= $A, C =< $Z -> C + 32;
to_lower_char(C) when is_integer(C),  C >= 16#C1, C =< 16#D6 -> C + 32;
to_lower_char(C) when is_integer(C),  C >= 16#D8, C =< 16#DE -> C + 32;
to_lower_char(C) -> C.

to_upper_char(C) when is_integer(C),  C >= $a, C =< $z -> C - 32;
to_upper_char(C) when is_integer(C),  C >= 16#E1, C =< 16#F6 -> C - 32;
to_upper_char(C) when is_integer(C),  C >= 16#F8, C =< 16#FE -> C - 32;
to_upper_char(C) -> C.

to_lower(Bin) when is_binary(Bin) ->
    << << (to_lower_char(C)) >> || <<C>> <= Bin >>.

to_upper(Bin) when is_binary(Bin) ->
    << << (to_upper_char(C)) >> || <<C>> <= Bin >>.


nth(N, Bin)->
    T = N-1,
    <<_:T/binary, Value:1/binary, _/binary>> = Bin, 
    Value.
nth(N, SizeBytes, Bin)->
    T = N-1,
    <<_:T/binary, Value:SizeBytes/binary, _/binary>> = Bin, 
    Value.

last(Bin)-> nth(size(Bin), 1, Bin).
last(SizeBytes, Bin)-> nth(size(Bin)-SizeBytes+1, SizeBytes, Bin).

first(<<Byte, _/binary>>)-> <<Byte>>.
first(SizeBytes, Bin)-> 
    <<Bytes:SizeBytes/binary, _/binary>> = Bin, 
    Bytes.

strip(Bin) -> strip(Bin, both).


strip(Bin, left) -> strip_left(Bin, $\s);
strip(Bin, right) -> strip_right(Bin, $\s);
strip(Bin, both) -> strip_right(strip_left(Bin, $\s), $\s).


strip(Bin, right, Char) -> strip_right(Bin, Char);
strip(Bin, left, Char) -> strip_left(Bin, Char);
strip(Bin, both, Char) -> strip_right(strip_left(Bin, Char), Char).

strip_left(<<Sc, Bin/binary>>, Sc) when is_integer(Sc)->strip_left(Bin, Sc);
strip_left(<<A, B/binary>>, Sc) when is_integer(Sc) -> <<A, B/binary>>;
strip_left(<<>>, Sc) when is_integer(Sc) -> <<>>.

strip_right(<<>>, Sc) when is_integer(Sc) -> <<>>;
strip_right(Bin, Sc) ->
    case last(Bin) of
        <<Sc>> -> strip_right(first(size(Bin)-1, Bin), Sc);
        _  -> Bin
    end.


unsigned_to_bin(I) when is_integer(I), I >= 0 ->
    unsigned_to_bin(I, []).

unsigned_to_bin(0, Acc) ->
    list_to_binary(Acc);
unsigned_to_bin(N, Acc) ->
    unsigned_to_bin(N bsr 8, [N band 16#ff|Acc]).

bin_to_unsigned(Bin) when is_binary(Bin)->
    Size=size(Bin)*8,
    <<Integer:Size>> = Bin,
    Integer.

duplicate(N, Byte)->
    << <<Byte:8>> || _ <- lists:seq(1,N) >>. 


