-module(ts_entry).
-export([create_table/1, new/1, update/1, lookup/3, list_asc/3, list_desc/3]).

-include("ts_db_records.hrl").
-include_lib("stdlib/include/qlc.hrl").

create_table(TableOpts) ->
    mnesia:create_table(ts_entry,
        TableOpts ++ [{attributes, record_info(fields, ts_entry)},
                      {type, ordered_set}, {index, [timestamp]}]).

new(ER = #ts_entry{}) ->
    {atmoic, NewRow} = mnesia:transaction(fun() ->
        {Username, TimelineId, _} = ER#ts_entry.ref,
        NextId = id_counter:next_counter(ts_entry_id),
        NewRow = ER#ts_entry{ref = {Username, TimelineId, NextId}},
        ok = mnesia:write(NewRow),
        NewRow end),
    {ok, NewRow}.

update(ER = #ts_entry{}) ->
    
    % look for existing record
    case mnesia:dirty_read(ts_entry, ER#ts_entry.ref) of
        % record does not exist
        [] -> no_record;
        % record exists, update it
        [_Record] -> mnesia:dirty_write(ER)
    end.

lookup(Username, TimelineId, EntryId) ->
    case mnesia:dirty_read(ts_entry, {Username, TimelineId, EntryId}) of
        [] -> no_record;
        [Entry] -> Entry
    end.

list({Username, Timeline}, Start, Length, OrderFun)
when is_integer(Start) and is_integer(Length) ->

    {atomic, Entries} = mnesia:transaction(fun() ->
        % match the username and timeline
        MatchHead = #ts_entry{ref = {Username, Timeline, '_'}, _='_'},

        % select all records that match
        mnesia:select(ts_entry, [{MatchHead, [], ['$_']}]
    end),

    % sort
    SortedEntries = lists:sort(OrderFun, Entries),

    % return only the range selected.
    % TODO: can we do this without selecting all entries?
    lists:sublist(SortedEntries, Start, Length);

list({Username, Timeline}, StartDateTime, EndDateTime, OrderFun) ->

    % compute the seconds from datetimes
    StartSeconds = calendar:datetime_to_gregorian_seconds(StartDateTime),
    EndSeconds = calendar:datetime_to_gregorian_seconds(EndDateTime),

    % select all entries from the timeline that are within the time range
    {atomic, Entries} = mnesia:transaction(fun() ->

        % match the username and timeline id
        MatchHead = #ts_entry{ref = {Username, Timeline, '_'}, timestamp='$1', _='_'},

        % guards for the time range
        StartGuard = {'>=', '$1', StartSeconds},
        EndGuard = {'<', '$1', EndSeconds},

        mnesia:select(ts_entry, [{MatchHead, [StartGuard, EndGuard], ['$_']}])
    end,

    % sort
    lists:sort(OrderFun, Entries).

list_asc(TimelineRef, Start, Length)
when is_integer(Start) and is_integer(Length) ->
    list(TimelineRef, Start, Length, fun timestamp_asc/2);

list_asc(TimelineRef, StartDateTime, EndDateTime) ->
    list(TimelineRef, StartDateTime, EndDateTime, fun timestamp_asc/2).

list_desc(TimelineRef, Start, Length)
when is_integer(Start) and is_integer(Length) ->
    list(TimelineRef, Start, Length, fun timestamp_desc/2);

list_desc(TimelineRef, StartDateTime, EndDateTime) ->
    list(TimelineRef, StartDateTime, EndDateTime, fun timestamp_desc/2).

timestamp_asc(E1, E2) -> E1#ts_entry.timestamp > E2#ts_entry.timestamp.

timestamp_desc(E1, E2) -> E1#ts_entry.timestamp < E2#ts_entry.timestamp.