-module(utGenTerm).

-export([
   any/0,
   any/1,
   genAtom/1,
   genInteger/1,
   genFloat/1,
   genReference/1,
   genPort/1,
   genPid/1,
   genTuple/1,
   genList/1,
   genShortString/1,
   genString/0,
   genString/1,
   genBinary/1,
   genBitstring/1,
   genBigNum/1,
   genFunction/1
]).

any() ->
   any(16).

any(MaxSize) when MaxSize =< 0 ->
   Fun = choice(valueTypes()),
   ?MODULE:Fun(MaxSize);
any(MaxSize) ->
   Fun = choice(allTypes()),
   ?MODULE:Fun(MaxSize).

genAtom(MaxSize) ->
   list_to_atom(genShortString(MaxSize)).

genInteger(_) ->
   Value = case rand:uniform() < 0.5 of
              true -> rand:uniform(127);
              false -> rand:uniform(16#FFFFFFFF)
           end,
   case rand:uniform() < 0.5 of
      true -> -1 * Value;
      false -> Value
   end.

genFloat(_) ->
   rand:uniform() * float(16#FFFFFFFF).

genReference(_) ->
   erlang:make_ref().

genPort(_) ->
   Ports = erlang:ports(),
   lists:nth(rand:uniform(length(Ports)), Ports).

genPid(_) ->
   Pids = erlang:processes(),
   lists:nth(rand:uniform(length(Pids)), Pids).

genTuple(MaxSize) ->
   list_to_tuple(genList(MaxSize)).

genList(MaxSize) ->
   Width = rand:uniform(MaxSize),
   [any(MaxSize - Width) || _ <- lists:seq(1, Width)].

genShortString(_) ->
   Size = rand:uniform(255),
   [rand:uniform(127) || _ <- lists:seq(1, Size)].

genString() ->
   Size = rand:uniform(4096),
   [rand:uniform(127) || _ <- lists:seq(1, Size)].

genString(MaxSize) ->
   [rand:uniform(255) || _ <- lists:seq(1, MaxSize)].

genBinary(MaxSize) ->
   list_to_binary(genString(MaxSize)).

genBitstring(MaxSize) ->
   B = genBinary(MaxSize),
   <<2:4/integer, B/binary>>.

genBigNum(_) ->
   16#FFFFFFFFFFFFFFFF + rand:uniform(16#FFFFFFFF).

genFunction(_) ->
   choice(allTypes()).

choice(Options) ->
   lists:nth(rand:uniform(length(Options)), Options).

valueTypes() ->
   [
      genAtom,
      genInteger,
      genFloat,
      genReference,
      genPort,
      genPid,
      genShortString,
      genString,
      genBinary,
      genBitstring,
      genBigNum,
      genFunction
   ].

allTypes() ->
   valueTypes() ++ [genTuple, genList].