-module(basedb).
-export([start/0,start_base/1,awake/2,asleep/2]).

-record(state,{data=dict:new(),proto='SELF'}).

-define(dir,"disk/").

start() ->
    register(?MODULE,spawn(fun() -> init() end)).

init() ->
    Tab = ets:new(?MODULE,[set]),
    read_dir(?dir,Tab),
    loop(?dir,Tab).

start_base(Id) ->
    Pid = start_lookup(Id),
    awake(Id,Pid),
    Pid.

start_lookup(Id) ->
    case lookup(Id) of
	{pid,Pid}  -> Pid;
	{data,Data} ->  spawn(fun() -> baselib:loop(Data) end);
	not_found -> spawn(fun() -> baselib:loop(#state{}) end)
    end.

lookup(Id) ->
    Ref = make_ref(),
    ?MODULE ! {lookup,Id,Ref,self()},
    receive
	{Ref,Reply} -> Reply
    end.

awake(Id,Pid) ->
    ?MODULE ! {awake,Id,Pid},
    ok.

asleep(Id,Data) ->
    ?MODULE ! {asleep,Id,Data},
    ok.

loop(Dir,Tab) ->
    receive
	{lookup,Id,Ref,From} ->
	    Reply = 
		case ets:lookup(Tab,Id) of
		    [] -> not_found;
		    [{Id,Entry}] -> Entry
		end,
	    From ! {Ref,Reply},
	    loop(Dir,Tab);
	{awake,Id,Pid} ->
	    ets:insert(Tab,{Id,{pid,Pid}}),
	    loop(Dir,Tab);
	{asleep,Id,Data} ->
	    ets:insert(Tab,{Id,{data,Data}}),
	    loop(Dir,Tab);
	flush_to_disk ->
	    spawn(fun() -> flush_to_disk(Dir,ets:tab2list(Tab)) end),
	    loop(Dir,Tab)
    end.

flush_to_disk(_Dir,[]) -> ok;
flush_to_disk(Dir,[{Id,{data,Data}}|More]) ->
    file:write_file(Dir++Id++".ob",term_to_binary(Data)),
    flush_to_disk(Dir,More);
flush_to_disk(Dir,[_|More]) ->
    flush_to_disk(Dir,More).		    


read_dir(Dir,Tab) ->
    {ok,List} =  file:list_dir(Dir),
    load_dir(List,Dir,Tab).

load_dir([],_,_Tab) -> ok;
load_dir([F|Files],Dir,Tab) ->
    case string:tokens(F,".") of
	[Name,"ob"] ->
	    {ok,Bytes} = file:read_file(Dir++F),
	    Dict = binary_to_term(Bytes),
	    ets:insert(Tab,{Name,{data,Dict}});
	_ -> skip
    end,
    load_dir(Files,Dir,Tab).
		    
