-module(dynamic_kvs).

-define(boot_type_supervisor, supervisor).
-define(boot_type_worker, worker).
-define(boot_type_simple_worker, simple_worker).
-type boot_type() :: ?boot_type_simple_worker | ?boot_type_supervisor | ?boot_type_worker.
-define(MATH_INT32_MAX, 4294967296).

-record(boot, {module :: atom(), type :: boot_type(), hasname = true :: boolean(), params :: any()}).

-export([start/1, start/0, get_name/1, init/1, handle_call/3, new/1, new/2]).
%% API
-export([set/3, gen/2]).

-record(dynamic_kvs, {
   modules = [] :: [{Mod :: module(), [{Key :: atom(), Val :: any()}]}]
}).

start(FatherPID) ->
   boot:start_child(FatherPID, #boot{module = ?MODULE, params = {}, type = ?boot_type_worker}).

start() ->
   boot:start(#boot{module = ?MODULE, params = {}, type = ?boot_type_worker}).

get_name(_) ->
   ?MODULE.

init(_) ->
   {ok, #dynamic_kvs{modules = []}}.

new(ModuleName) ->
   new(ModuleName, []).

new(ModuleName, Kvs) ->
   boot:call(?MODULE, {new, ModuleName, Kvs}).

set(ModuleName, Key, Val) ->
   boot:call(?MODULE, {set, ModuleName, Key, Val}).

handle_call({new, ModuleName, Kvs}, _, DynamicKvs) ->
   gen(ModuleName, Kvs),
   NewDynamicKvs =
      case lists:keyfind(ModuleName, 1, DynamicKvs#dynamic_kvs.modules) of
         false ->
            DynamicKvs#dynamic_kvs{modules = [{ModuleName, Kvs} | DynamicKvs#dynamic_kvs.modules]};
         _ ->
            DynamicKvs
      end,
   {reply, ok, NewDynamicKvs};

handle_call({set, ModuleName, Key, Val}, _, DynamicKvs) ->
   case lists:keytake(ModuleName, 1, DynamicKvs#dynamic_kvs.modules) of
      false ->
         {reply, {error, not_find_module}, DynamicKvs};
      {value, {_, Kvs}, RemainMods} ->
         NewKvs = lists:keyreplace(Key, 1, Kvs, {Key, Val}),
         gen(ModuleName, NewKvs),
         {reply, ok, DynamicKvs#dynamic_kvs{modules = [{ModuleName, NewKvs} | RemainMods]}}
   end.


concat(List) ->
   lists:concat(List).

gen(ModuleName, Kvs) ->
   Meta = meta:new(ModuleName),
   Fun =
      fun({K, V}, MetaTemp) ->
         FunStr = concat([K, "() ->\n\t", utTypeCast:term_to_string(V), ".\n"]),
         case meta:add_func(MetaTemp, FunStr) of
            {ok, MetaTemp2} -> MetaTemp2;
            Error ->
               io:format("module(~p) define func(~s) error ~p~n", [ModuleName, FunStr, Error]),
               exit(Error)
         end
      end,
   FinalMeta = lists:foldl(Fun, Meta, Kvs),
   FinalMeta2 = meta:add_func(FinalMeta, concat(["set(Key, Val) ->\n\tdynamic_kvs:set(", ModuleName, ", Key, Val).\n"])),
   case meta:compile(FinalMeta2) of
      {error, Error} ->
         io:format("error ~p", [Error]),
         ok;
      ok ->
         ok
   end.