package won.node.service.persistence;

import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import won.node.service.linkeddata.lookup.SocketLookup;
import won.protocol.exception.NoAtomForSocketFoundException;
import won.protocol.exception.NoDefaultSocketException;
import won.protocol.exception.NoSuchAtomException;
import won.protocol.exception.NoSuchSocketException;
import won.protocol.model.Socket;
import won.protocol.model.SocketDefinition;
import won.protocol.repository.AtomRepository;
import won.protocol.repository.SocketRepository;

/**
 * Service that is informed of a state change of a connection and performs data
 * derivation work, changing the data of the atom owning the connection.
 */
@Component
public class SocketService {
    Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    AtomRepository atomRepository;
    @Autowired
    SocketRepository socketRepository;
    Map<URI, SocketDefinition> knownSockets = new HashMap<>();
    @Autowired
    SocketLookup socketLookup;

    public SocketService() {
    }

    public Optional<Socket> getDefaultSocket(URI atomUri) throws NoSuchAtomException {
        List<Socket> sockets = socketRepository.findByAtomURI(atomUri);
        for (Socket socket : sockets) {
            if (socket.isDefaultSocket())
                return Optional.of(socket);
        }
        return sockets.stream().findFirst();
    }

    public Socket getSocket(URI atomUri, Optional<URI> socketUri) throws IllegalArgumentException, NoSuchAtomException {
        if (socketUri.isPresent()) {
            return socketRepository.findOneBySocketURI(socketUri.get())
                            .orElseThrow(() -> new NoSuchSocketException(socketUri.get()));
        }
        return getDefaultSocket(atomUri)
                        .orElseThrow(() -> new NoDefaultSocketException(atomUri));
    }

    public Optional<URI> getAtomOfSocket(URI socketURI) {
        if (socketURI == null) {
            return Optional.empty();
        }
        String uri = socketURI.toString();
        String fragment = socketURI.getRawFragment();
        if (fragment == null) {
            return Optional.empty();
        }
        return Optional.of(URI.create(uri.substring(0, uri.length() - fragment.length() - 1)));
    }

    public URI getAtomOfSocketRequired(URI socketURI) {
        return getAtomOfSocket(socketURI).orElseThrow(() -> new NoAtomForSocketFoundException(socketURI));
    }

    public Optional<Socket> getSocket(URI socketURI) {
        return socketRepository.findOneBySocketURI(socketURI);
    }

    public Socket getSocketRequired(URI socketURI) {
        return getSocket(socketURI).orElseThrow(() -> new NoSuchSocketException(socketURI));
    }

    public Optional<SocketDefinition> getSocketConfig(URI socketType) {
        return socketLookup.getSocketConfig(socketType);
    }

    public Optional<Integer> getCapacity(URI localSocket) {
        Optional<Socket> socket = socketRepository.findOneBySocketURI(localSocket);
        if (!socket.isPresent()) {
            throw new NoSuchSocketException(localSocket);
        }
        return socketLookup.getCapacityOfType(socket.get().getTypeURI());
    }

    public boolean isCompatible(URI localSocket, URI targetSocket) {
        Optional<Socket> socket = socketRepository.findOneBySocketURI(localSocket);
        if (!socket.isPresent()) {
            throw new NoSuchSocketException(localSocket);
        }
        Optional<Socket> target = socketRepository.findOneBySocketURI(targetSocket);
        Optional<URI> targetType = Optional.empty();
        if (target.isPresent()) {
            targetType = Optional.of(target.get().getTypeURI());
        } else {
            targetType = socketLookup.getSocketType(targetSocket);
        }
        if (targetType.isPresent()) {
            return socketLookup.isCompatibleSocketTypes(socket.get().getTypeURI(), targetType.get());
        }
        return false;
    }

    public boolean isAutoOpen(URI localSocket) {
        Optional<Socket> socket = socketRepository.findOneBySocketURI(localSocket);
        if (!socket.isPresent()) {
            throw new NoSuchSocketException(localSocket);
        }
        return socketLookup.isAutoOpenSocketType(socket.get().getTypeURI());
    }

    public Optional<URI> getSocketType(URI socketURI) {
        return socketLookup.getSocketType(socketURI);
    }
}

@@ -36,128 +36,8 @@
 e;%0A%0A
-import java.net.URI;%0Aimport java.util.HashMap;%0Aimport java.util.List;%0Aimport java.util.Map;%0Aimport java.util.Optional;%0A%0A
 impo
@@ -57,16 +57,16 @@
 Logger;%0A
+
 import o
@@ -201,17 +201,16 @@
 ponent;%0A
-%0A
 import w
@@ -322,64 +322,8 @@
 on;%0A
-import won.protocol.exception.NoDefaultSocketException;%0A
 impo
@@ -597,16 +597,113 @@
 itory;%0A%0A
+import java.net.URI;%0Aimport java.util.HashMap;%0Aimport java.util.Map;%0Aimport java.util.Optional;%0A%0A
 /**%0A * S
@@ -1206,355 +1206,8 @@
  %7D%0A%0A
-    public Optional%3CSocket%3E getDefaultSocket(URI atomUri) throws NoSuchAtomException %7B%0A        List%3CSocket%3E sockets = socketRepository.findByAtomURI(atomUri);%0A        for (Socket socket : sockets) %7B%0A            if (socket.isDefaultSocket())%0A                return Optional.of(socket);%0A        %7D%0A        return sockets.stream().findFirst();%0A    %7D%0A%0A
     
@@ -1553,115 +1553,12 @@
 urn 
-getDefaultSocket(atomUri)%0A                        .orElseThrow(() -%3E new NoDefaultSocketException(atomUri))
+null
 ;%0A  

Remove obsolete default socket handling