aboutsummaryrefslogblamecommitdiff
path: root/lib/app/chat.ex
blob: f16551464d84c35b3ff0d82ca99cc14a4b345bf0 (plain) (tree)
1
                      































                                                                                    


                                             




                                                   
 
                     

     





































                                                                                                             






                                                           
defmodule SApp.Chat do
  use GenServer

  def start_link(channel) do
    GenServer.start_link(__MODULE__, channel)
  end

  def init(channel) do
    {:ok, store} = SData.MerkleList.start_link(&msg_cmp/2)
    manifest = {:chat, channel}
    id = :crypto.hash(:sha256, :erlang.term_to_binary(manifest))

    GenServer.cast(Shard.Manager, {:register, id, self()})
    GenServer.cast(self(), :init_pull)

    {:ok, %{channel: channel, id: id, manifest: manifest, store: store, peers: %{}}}
  end

  def handle_call(:manifest, _from, state) do
    {:reply, state.manifest, state}
  end

  def handle_cast({:redundant, _}, _state) do
    exit :normal
  end

  def handle_cast(:init_pull, state) do
    GenServer.call(SNet.Manager, :get_all)
    |> Enum.each(&(GenServer.cast(&1, {:send_msg, {:interested, [state.id]}})))
    {:noreply, state}
  end

  def handle_cast({:chat_send, msg}, state) do
    msgitem = {(System.os_time :seconds),
               Shard.Identity.get_nickname(),
               msg}
    GenServer.cast(state.store, {:insert, msgitem})

    for {_, pid} <- state.peers do
      push_messages(state, pid, nil, 5)
    end

    {:noreply, state}
  end

  def handle_cast({:interested, peer_id, peer_pid}, state) do
    push_messages(state, peer_pid, nil, 10)
    new_peers = Map.put(state.peers, peer_id, peer_pid)
    {:noreply, %{ state | peers: new_peers }}
  end

  def handle_cast({:msg, peer_id, peer_pid, msg}, state) do
    case msg do
      :get_top -> push_messages(peer_id, state, nil, 10)
      {:get, start} -> push_messages(peer_id, state, start, 20)
      {:info, _start, list, rest} ->
        if rest != nil and not GenServer.call(state.store, {:has, rest}) do
          GenServer.cast(peer_pid, {:send_msg, {state.id, {:get, rest}}})
        end
        spawn_link(fn ->
          Process.sleep 1000
          GenServer.cast(state.store, {:insert_many, list, (fn msg -> msg_callback(state.channel, msg) end)})
        end)
    end

    if Map.has_key?(state.peers, peer_id) do
      {:noreply, state}
    else
      handle_cast({:interested, peer_id, peer_pid}, state)
    end
  end

  defp push_messages(state, to, start, num) do
    case GenServer.call(state.store, {:read, start, num}) do
      {:ok, list, rest} ->
        GenServer.cast(to, {:send_msg, {state.id, {:info, start, list, rest}}})
      _ -> nil
    end
  end


  def msg_callback(chan, {ts, nick, msg}) do
    IO.puts "#{ts |> DateTime.from_unix! |> DateTime.to_iso8601}  ##{chan}  <#{nick}> #{msg}"
  end

  def msg_cmp({ts1, nick1, msg1}, {ts2, nick2, msg2}) do
    SData.MerkleList.cmp_ts_str({ts1, nick1<>"|"<>msg1}, 
                                   {ts2, nick2<>"|"<>msg2})
  end 
end