%% @doc Special command. Executes Squery over pg_type table and updates codecs.
-module(cloudi_x_epgsql_cmd_update_type_cache).
-behaviour(cloudi_x_epgsql_command).
-export([init/1, execute/2, handle_message/4]).
-export_type([response/0]).

-type response() ::
        {ok, [cloudi_x_epgsql:type_name()]}
      | {error, cloudi_x_epgsql:query_error()}.

-include("cloudi_x_epgsql_protocol.hrl").

-record(upd,
        {codecs :: [{cloudi_x_epgsql_codec:codec_mod(), Opts :: any()}],
         codec_entries :: [cloudi_x_epgsql_codec:codec_entry()] | undefined,
         decoder :: cloudi_x_epgsql_wire:row_decoder() | undefined}).

init(Codecs) ->
    #upd{codecs = Codecs}.

execute(Sock, #upd{codecs = Codecs} = State) ->
    CodecEntries = cloudi_x_epgsql_codec:init_mods(Codecs, Sock),
    TypeNames = [element(1, Entry) || Entry <- CodecEntries],
    Query = cloudi_x_epgsql_oid_db:build_query(TypeNames),
    {PktType, PktData} = cloudi_x_epgsql_wire:encode_query(Query),
    {send, PktType, PktData, Sock, State#upd{codec_entries = CodecEntries}}.

handle_message(?ROW_DESCRIPTION, <<Count:?int16, Bin/binary>>, Sock, State) ->
    Codec = cloudi_x_epgsql_sock:get_codec(Sock),
    Columns = cloudi_x_epgsql_wire:decode_columns(Count, Bin, Codec),
    Decoder = cloudi_x_epgsql_wire:build_decoder(Columns, Codec),
    {noaction, Sock, State#upd{decoder = Decoder}};
handle_message(?DATA_ROW, <<_Count:?int16, Bin/binary>>,
               Sock, #upd{decoder = Decoder} = St) ->
    Row = cloudi_x_epgsql_wire:decode_data(Bin, Decoder),
    {add_row, Row, Sock, St};
handle_message(?COMMAND_COMPLETE, Bin, Sock, St) ->
    Complete = cloudi_x_epgsql_wire:decode_complete(Bin),
    Rows = cloudi_x_epgsql_sock:get_rows(Sock),
    {add_result, Rows, {complete, Complete}, Sock, St};
handle_message(?READY_FOR_QUERY, _Status, Sock, State) ->
    [Result] = cloudi_x_epgsql_sock:get_results(Sock),
    handle_result(Result, Sock, State);
handle_message(?ERROR, Error, Sock, St) ->
    Result = {error, Error},
    {add_result, Result, Result, Sock, St};
handle_message(_, _, _, _) ->
    unknown.

handle_result({error, _} = Err, Sock, _State) ->
    {finish, Err, done, Sock};
handle_result(Rows, Sock, #upd{codec_entries = CodecEntries} = _State) ->
    OidEntries = cloudi_x_epgsql_oid_db:parse_rows(Rows),
    Types = cloudi_x_epgsql_oid_db:join_codecs_oids(OidEntries, CodecEntries),

    Codec = cloudi_x_epgsql_sock:get_codec(Sock),
    Codec1 = cloudi_x_epgsql_binary:update_codec(Types, Codec),
    Sock1 = cloudi_x_epgsql_sock:set_attr(codec, Codec1, Sock),

    TypeNames = [element(1, Entry) || Entry <- CodecEntries],
    {finish, {ok, TypeNames}, done, Sock1}.
