-module(polyline).
-export([decode/1, eight_bit_chunks/1, six_bit_chunks/1, six_bit_chunk/1, split_up_six_bits/1, five_bit_chunks/1, bin_flip/1]).

%See https://developers.google.com/maps/documentation/utilities/polylinealgorithm


decode(Encoded_polyline) ->
	%Steps 11 back to 8
	Six_bit_chunks = six_bit_chunks(Encoded_polyline),
	%Step 8
	Groups_of_chunks_list = split_up_six_bits(Six_bit_chunks),
	%Step 8 back to 6
	Five_bit_chunks = five_bit_chunks(Groups_of_chunks_list),
	%---TODO
	%Maybe some more of the below need splitting out into different functions or nesting in a map?
	%Which option to go for, a function that maps as per five_bit_chunks, or mapping functions as per below?
	%I don't think I can map all functions in one go because ultimately need to change number of members in groups.
	%I.e. following will go from groups of five to eight.
	%---
	%Step 5
	Eight_bit_chunks = lists:map(
		fun(Group_of_chunks) ->
			eight_bit_chunks(Group_of_chunks)
		end,
		Five_bit_chunks),
	Results = lists:map(
		fun(Group_of_chunks) ->
			%TODO These should probably be separate functions, rather than one long mess inside a map
			Last_bit = [hd(lists:reverse(hd(lists:reverse(Group_of_chunks))))],
			Flipped_chunks = lists:map(
				fun(Chunk) ->
					bin_flip_(Chunk, [])
				end,
				Group_of_chunks),
			%Step 5
			Chunks = if Last_bit =:= "1" ->
				Flipped_chunks;
			true ->
				Group_of_chunks
			end,
			{ok, [Flattened_binary], []} = io_lib:fread("~2u", lists:flatten(Chunks)),
			%Step 4
			Shifted_binary = Flattened_binary bsr 1,
			%Since bin_flip returns a string need to then change back to a number
			{ok, [Shifted_binary_], []} = io_lib:fread("~2u", bin_flip(Shifted_binary - 1)),
			%Step 3
			Final_binary = if Last_bit =:= "1" ->
				Shifted_binary_;
			true ->
				Shifted_binary
			end,
			%Step 2 back to 1
			Decoded = if Last_bit =:= "1" ->
				-1 * Final_binary/100000;
			true ->
				Final_binary/100000
			end,
			Decoded
		end,
		Eight_bit_chunks),
	Results.
	

%Step 8 - Split up six bit chunks, per the 0x20 bit
split_up_six_bits(Bit_chunks_list) ->
	split_up_six_bits_(Bit_chunks_list, [], []).
split_up_six_bits_([Head | Tail], Group_of_bit_chunks, Groups_of_bit_chunks_list) when [hd(Head)] == "1" ->
	split_up_six_bits_(Tail, [Head]++Group_of_bit_chunks, Groups_of_bit_chunks_list);
split_up_six_bits_([Head | Tail], Group_of_bit_chunks, Groups_of_bit_chunks_list) when [hd(Head)] == "0" ->
	%Then need to start a new list, but after this 0 one!
	split_up_six_bits_(Tail, [], [lists:reverse([Head]++Group_of_bit_chunks)]++Groups_of_bit_chunks_list);
split_up_six_bits_([], Group_of_bit_chunks, Groups_of_bit_chunks_list) when length(Group_of_bit_chunks) > 0 ->
	split_up_six_bits_([], [], [lists:reverse(Group_of_bit_chunks)]++Groups_of_bit_chunks_list);
split_up_six_bits_([], [], Groups_of_bit_chunks_list) ->
	%TODO Might be neater to map lists:reverse over the list instead of doing above and here.
	lists:reverse(Groups_of_bit_chunks_list).


%Step 5
%TODO See if better way of doing this
eight_bit_chunks(Five_bit_chunks_list) ->
	Five_bit_chunk_string = lists:reverse(lists:flatten(Five_bit_chunks_list)),
	eight_bit_chunks_(Five_bit_chunk_string, []).
eight_bit_chunks_(Five_bit_chunk_string, Eight_bit_chunks_list) when length(Five_bit_chunk_string) > 8 ->
	Eight_bit_chunk = lists:reverse(lists:sublist(Five_bit_chunk_string,1,8)),
	Rest_of_five_bit_chunk_string = lists:nthtail(8,Five_bit_chunk_string),
	eight_bit_chunks_(Rest_of_five_bit_chunk_string, [Eight_bit_chunk]++Eight_bit_chunks_list);
eight_bit_chunks_(Five_bit_chunk_string, Eight_bit_chunks_list) when length(Five_bit_chunk_string) =< 8, Five_bit_chunk_string /= [] ->
	Padded_bit_string = pad_to(8, lists:reverse(Five_bit_chunk_string)),
	eight_bit_chunks_([], [Padded_bit_string]++Eight_bit_chunks_list);
eight_bit_chunks_([], Eight_bit_chunks_list) ->
	Eight_bit_chunks_list.


six_bit_chunks(Encoded_polyline) ->
	six_bit_chunks_(Encoded_polyline, []).
six_bit_chunks_([Head | Rest], Chunks_list) ->
	Six_bit_chunk = six_bit_chunk(Head),
	%Add to Reversed_chunks
	six_bit_chunks_(Rest, [Six_bit_chunk]++Chunks_list);
six_bit_chunks_([], Chunks_list) ->
	lists:reverse(Chunks_list).


six_bit_chunk(Ascii_bit) ->
	%Step 10
	Shifted_bit = Ascii_bit - 63,
	%Step 9
	%From http://erlangcentral.org/wiki/index.php/Converting_Between_Binary_and_Decimal
	Binary_chunk = hd(io_lib:format("~.2B", [Shifted_bit])),
	%---TODO
	%What if Binary_chunk is shorter than 6?
	%Well in that case, I guess that means we'd want to split, but for now pad to six and check for 0x20 elsewhere.
	%---
	pad_to(6, Binary_chunk).


five_bit_chunks(Groups_of_chunks_list) ->
	lists:map(
		fun(Group_of_chunks) ->
			%Step 7 - Un-reverse the five bit chunks
			lists:reverse(lists:map(
				fun(Chunk) ->
					%Step 8 - "Un-or" the 0x20 bit
					lists:sublist(Chunk,2,6)
				end,
				Group_of_chunks))
		end,
		Groups_of_chunks_list).


%I can't figure out padding with io:format etc when printing binary numbers
pad_to(Length, Binary_string) when length(Binary_string) < Length ->
	Padded_binary_string = "0"++Binary_string,
	pad_to(Length, Padded_binary_string);
pad_to(Length, Binary_string) when length(Binary_string) == Length ->
	Binary_string.


%bnot doesn't seem to work as I thought it would so do it very inelegantly by switching each "bit" in a string.
bin_flip(Binary_number) ->
	Binary_string = hd(io_lib:format("~.2B", [Binary_number])),
	bin_flip_(Binary_string, []).
bin_flip_([Head | Rest], Flipped_string) ->
	Head_bit = hd(io_lib:format("~c",[Head])),
	Flipped_bit = if Head_bit =:= "0" ->
		"1";
	true ->
		"0"
	end,
	bin_flip_(Rest, Flipped_bit++Flipped_string);
bin_flip_([], Flipped_string) ->
	lists:reverse(Flipped_string).


%Notes
	%To enter numbers of a different base 2#101010
	%0x1f is 31, 0x20 is 32
	%Need to ensure functions are tail recursive
	%To convert to integer, just need to do $<char>
	%just do hd(String) will come as a number
	%Last = hd(io_lib:format("~c", [hd(lists:reverse(hd(io_lib:format("~.2B", [Binary_number]))))])),
	%{ok, Almost_flipped, _} = io_lib:fread("~2u",Almost_flipped_string),
