%% -------------------------------------------------------------------
%%
%% Copyright (c) 2014 Basho Technologies, Inc.  All Rights Reserved.
%%
%%   This Source Code Form is subject to the terms of the Mozilla Public
%%   License, v. 2.0. If a copy of the MPL was not distributed with this
%%   file, You can obtain one at http://mozilla.org/MPL/2.0/.
%%
%% -------------------------------------------------------------------
%% @doc Accessor function for exometer data structures
%%
%% This module uses the exprecs transform (see <a href="https://github.com/uwiger/parse_trans/tree/master/doc/exprecs.md">exprecs</a>)
%% to generate accessor functions for exometer data structures.
%%
%% Note that the `value' attribute in `exometer_entry{}' records may not
%% represent the true value of the metric, since exometer entries often
%% have structured values, or are represented as CRDTs for update efficiency.
%%
%% @end
-module(exometer_info).

-export([status/1,
         pp/1,
         pp_lookup/1,
         pp_find/1,
         pp_select/1]).

-include("exometer.hrl").
-include_lib("parse_trans/include/exprecs.hrl").

-export_type([pp/0]).

-export_records([exometer_entry]).

-type pp() :: {atom(), [{atom(), any()}]}.

-spec status(exometer:entry()) -> enabled | disabled.
%% @doc Return the operational status of the given exometer entry.
%%
%% The `status' attribute is overloaded in the `#exometer_entry{}' record.
%% This function extracts the correct status (`enabled | disabled').
%% @end
status(#exometer_entry{status = St}) ->
    exometer_util:get_status(St).

-spec pp(tuple() | list()) -> pp() | [pp() | any()].
%% @doc Pretty-print a record, or list containing records.
%%
%% This function pretty-prints a record as `{RecordName, [{Attr,Value}]}',
%% or, if the input is a list, recognizes records and pretty-prints them,
%% leaving other data structures unchanged.
%% @end
pp(L) when is_list(L) ->
    [pp(X) || X <- L];
pp(X) ->
    case '#is_record-'(X) of
        true ->
            RecName = element(1,X),
            {RecName, lists:zip(
                        '#info-'(RecName,fields),
                        pp(tl(tuple_to_list(X))))};
        false ->
            if is_tuple(X) ->
                    list_to_tuple(pp(tuple_to_list(X)));
               true ->
                    X
            end
    end.

-spec pp_lookup(exometer:name()) -> pp() | undefined.
%% @doc Performs a lookup by name of entry and pretty-prints the result.
%%
%% This function returns `undefined' if the entry cannot be found.
%% @end
pp_lookup(Name) ->
    case exometer:info(Name, entry) of
        undefined ->
            undefined;
        Entry ->
            pp(Entry)
    end.

-spec pp_find(list()) -> [pp()].
%% @doc Performs `exometer:find_entries(Path) & returns pretty-printed result.
%%
%% This function calls `exometer:find_entries(Path)', retrieves the entry
%% for each matching metric, and calls `pp(Entry)' for each entry.
%% @end
pp_find(Path) ->
    pp([exometer:info(M, entry) || {M,_,_} <- exometer:find_entries(Path)]).

-spec pp_select(ets:match_spec()) -> [pp()].
%% @doc Performs `exometer:select(Pattern) & returns pretty-printed result.
%%
%% This function calls `exometer:select(Pattern)', retrieves the entry
%% for each matching metric, and calls `pp(Entry)' for each entry.
%%
%% Note that the match body of the select pattern must produce the full
%% `{Name, Type, Status}' object, e.g. by specifying <code>['$_']</code>.
%% @end
pp_select(Pat) ->
    pp([exometer:info(M, entry) || {M,_,_} <- exometer:select(Pat)]).
