require "socket"
require_relative "tattler"

module Buschtelefon
  class NetTattler < Tattler
    attr_accessor :host, :port, :socket

    def initialize(host: "127.0.0.1", port: 0)
      super()
      @socket = UDPSocket.new
      @socket.bind(host, port)
      @host = @socket.local_address.ip_address
      @port = @socket.local_address.ip_port
    end

    def listen(&_callback)
      puts "Started UDP server on #{@host}:#{@port}..."

      Socket.udp_server_loop_on([@socket]) do |message, message_source|
        remote_tattler = find_or_build_remote_tattler(
          host: message_source.remote_address.ip_address,
          port: message_source.remote_address.ip_port
        )

        if message == "\x05"
          # puts "#{@port} got inquiry from #{remote_tattler}. Is connected to #{@connections.inspect}"
          handle_knowledge_inquiry(remote_tattler)
        else
          gossip = Gossip.new(message)
          handle_incoming_gossip(gossip)
          yield(gossip, remote_tattler) if block_given?
        end
      end
    end

    def connect_remote(host:, port:)
      find_or_build_remote_tattler(host: host, port: port).tap do |remote_tattler|
        connect(remote_tattler)
      end
    end

    def inquire_remote_neighbors
      remote_connections.each { |remote_tattler| remote_tattler.inquire }
    end

    def remote_connections
      @connections.select { |tattler| tattler.is_a?(RemoteTattler) }
    end

    def to_s
      "#{@host}:#{@port}"
    end

    private

    def handle_incoming_gossip(gossip)
      feed(gossip)
    end

    # We just give out everything to the outbound port of the inquiry source
    def handle_knowledge_inquiry(remote_tattler)
      transfer_knowledge(remote_tattler)
    end

    def find_or_build_remote_tattler(host:, port:)
      remote_connections.find { |t| t.host == host && t.port == port } || build_remote_tattler(host: host, port: port)
    end

    def build_remote_tattler(host:, port:)
      RemoteTattler.new(host: host, port: port, outbound_socket: @socket)
    end
  end
end