defmodule Bbox.Client do
use GenServer
alias Bbox.Peers
require Logger
@ip {127, 0, 0, 1}
def start_link(state) do
GenServer.start_link(__MODULE__, state, name: __MODULE__)
end
@impl true
def init(state) do
send(self(), :connect)
{:ok, state}
end
@impl true
def handle_info(:connect, state) do
port = Keyword.fetch!(state, :port)
peers =
Peers.list!()
|> Enum.map(&(&1.address |> String.split(":") |> Enum.at(1) |> String.to_integer()))
|> Enum.filter(&(&1 != port))
peers =
peers
|> Enum.map(fn peer ->
case :gen_tcp.connect(@ip, peer, [:binary, active: true]) do
{:ok, socket} ->
{:ok, socket}
{:error, reason} ->
disconnect(state, reason)
{:error, reason}
end
end)
|> Enum.filter(&match?({:ok, _}, &1))
|> Enum.map(fn {:ok, peer} -> peer end)
Logger.info("connected to #{length(peers)} peers")
{:noreply, state |> Keyword.put(:peers, peers)}
end
@impl true
def handle_info({:tcp, _, packet}, state) do
Logger.info("Received #{packet}")
{:noreply, state}
end
@impl true
def handle_info({:tcp_closed, _}, state) do
{:stop, :normal, state}
end
@impl true
def handle_info({:tcp_error, _}, state), do: {:stop, :normal, state}
@impl true
def handle_cast({:message_by_socket, {socket, message}}, state) do
Logger.info("Sending #{message}")
:ok = :gen_tcp.send(socket, message)
{:noreply, state}
end
@impl true
def handle_cast({:message_by_port, {port, message}}, state) do
Logger.info("Sending #{message}")
peer = get_peer(port)
:gen_tcp.send(peer, message)
{:noreply, state}
end
@impl true
def handle_cast({:broadcast_message, message}, state) do
list_peers()
|> Enum.each(fn peer ->
:gen_tcp.send(peer, message)
end)
{:noreply, state}
end
@impl true
def handle_call({:get_peer, port}, _from, state) do
peer =
list_peers()
|> Enum.find(fn peer ->
{:ok, {_, p}} = :inet.peername(peer)
p == port
end)
{:reply, peer, state}
end
@impl true
def handle_call(:list_peers, _from, state), do: {:reply, Keyword.fetch!(state, :peers), state}
def disconnect(state, reason) do
Logger.info("Disconnected: #{reason}")
{:stop, :normal, state}
end
def send_message_by_socket(params) do
GenServer.cast(__MODULE__, {:message_by_socket, params})
end
def send_message_by_port(params) do
GenServer.cast(__MODULE__, {:message_by_port, params})
end
def broadcast_message(message) do
GenServer.cast(__MODULE__, {:broadcast_message, message})
end
def get_peer(port) do
GenServer.call(__MODULE__, {:get_peer, port})
end
def list_peers() do
GenServer.call(__MODULE__, :list_peers)
end
end