defmodule Ibex.Message do
  require Logger

  def make_field(f) do
    f <> <<0>>
  end

  def make_msg(fields, l) do
    <<l::size(32)>> <> fields <> <<0>>
  end

  def get_msg_length(msg) do
    byte_size(msg) + 1
  end

  def message_version() do
    <<2>>
  end
end

defprotocol IBMessage do
  def to_ib_api(msg)
end

defimpl IBMessage, for: List do
  require Logger

  def to_ib_api(msg) do
    fields =
      Enum.reduce(msg, <<>>, fn f, acc ->
        acc <> Ibex.Message.make_field(f)
      end)

    Logger.debug("Print: #{inspect(fields)}")
    length = Ibex.Message.get_msg_length(to_string(fields))
    Ibex.Message.make_msg(fields, length)
  end
end

defmodule Ibex.Messages.Inbound do
  require Logger

  def parse(m) do
    <<l::size(32), rest::binary>> = m
    fields = String.split(rest, <<0>>)
    code = Enum.at(fields, 0)
    code = Ibex.Messages.Types.Inbound.parse(String.to_integer(code))
    {l, code, fields}
  end
end

defmodule Ibex.Messages.Outbound do
  require Logger

  defmodule ReqIds do
    alias Ibex.Messages.Types.Outbound, as: Outbound

    def t() do
      code = to_string(Outbound.parse(:req_ids))

      [
        code,
        <<"1">>,
        <<"1">>
      ]
    end
  end

  defmodule ReqContractDetails do
    alias Ibex.Messages.Types.Outbound, as: Outbound

    def t(req_id, symbol, sec_type) do
      code = to_string(Outbound.parse(:req_contract_data))

      [
        code,
        <<"8">>,
        req_id,
        symbol,
        sec_type
      ]
    end
  end

  defmodule ReqMatchingSymbols do
    alias Ibex.Messages.Types.Outbound, as: Outbound

    def t(reqId, symbol) do
      code = to_string(Outbound.parse(:req_matching_symbols))

      [
        code,
        reqId,
        symbol
      ]
    end
  end

  defmodule ReqHistoricalData do
    alias Ibex.Messages.Types.Outbound, as: Outbound
    @version <<"6">>

    def t(
          req_id,
          contract,
          end_date_time,
          duration_str,
          bar_size_setting,
          what_to_show,
          useRTH,
          formatDate,
          keepUpToDate,
          chartOption
        ) do
      code = to_string(Outbound.parse(:req_historical_data))
      contract_fields = contract.to_fields()

      [
        code,
        @version,
        req_id,
        contract[:con_id]
      ]
    end
  end
end