package gossip

import (
    "github.com/iotaledger/goshimmer/packages/accountability"
    "github.com/iotaledger/goshimmer/packages/daemon"
    "github.com/iotaledger/goshimmer/packages/errors"
    "github.com/iotaledger/goshimmer/packages/events"
    "github.com/iotaledger/goshimmer/packages/identity"
    "github.com/iotaledger/goshimmer/packages/network"
    "github.com/iotaledger/goshimmer/packages/network/tcp"
    "github.com/iotaledger/goshimmer/packages/node"
    "strconv"
)

var TCPServer = tcp.NewServer()

func configureServer(plugin *node.Plugin) {
    TCPServer.Events.Connect.Attach(events.NewClosure(func(conn *network.ManagedConnection) {
        protocol := newProtocol(conn)

        // print protocol errors
        protocol.Events.Error.Attach(events.NewClosure(func(err errors.IdentifiableError) {
            plugin.LogFailure(err.Error())
        }))

        // store protocol in neighbor if its a neighbor calling
        protocol.Events.ReceiveIdentification.Attach(events.NewClosure(func(identity *identity.Identity) {
            if protocol.Neighbor != nil {
                protocol.Neighbor.acceptedProtocolMutex.Lock()
                if protocol.Neighbor.AcceptedProtocol == nil {
                    protocol.Neighbor.AcceptedProtocol = protocol

                    protocol.Conn.Events.Close.Attach(events.NewClosure(func() {
                        protocol.Neighbor.acceptedProtocolMutex.Lock()
                        defer protocol.Neighbor.acceptedProtocolMutex.Unlock()

                        protocol.Neighbor.AcceptedProtocol = nil
                    }))
                }
                protocol.Neighbor.acceptedProtocolMutex.Unlock()
            }
        }))

        // drop the "secondary" connection upon successful handshake
        protocol.Events.HandshakeCompleted.Attach(events.NewClosure(func() {
            if protocol.Neighbor.Identity.StringIdentifier <= accountability.OWN_ID.StringIdentifier {
                protocol.Neighbor.initiatedProtocolMutex.Lock()
                var initiatedProtocolConn *network.ManagedConnection
                if protocol.Neighbor.InitiatedProtocol != nil {
                    initiatedProtocolConn = protocol.Neighbor.InitiatedProtocol.Conn
                }
                protocol.Neighbor.initiatedProtocolMutex.Unlock()

                if initiatedProtocolConn != nil {
                    _ = initiatedProtocolConn.Close()
                }
            }

            protocol.Neighbor.Events.ProtocolConnectionEstablished.Trigger(protocol)
        }))

        go protocol.Init()
    }))

    daemon.Events.Shutdown.Attach(events.NewClosure(func() {
        plugin.LogInfo("Stopping TCP Server ...")

        TCPServer.Shutdown()
    }))
}

func runServer(plugin *node.Plugin) {
    plugin.LogInfo("Starting TCP Server (port " + strconv.Itoa(*PORT.Value) + ") ...")

    daemon.BackgroundWorker(func() {
        plugin.LogSuccess("Starting TCP Server (port " + strconv.Itoa(*PORT.Value) + ") ... done")

        TCPServer.Listen(*PORT.Value)

        plugin.LogSuccess("Stopping TCP Server ... done")
    })
}