// Path: src/main/java/org/nextrtc/signalingserver/cases/ExchangeSignalsBetweenMembers.java
// @Component
// @Scope("prototype")
// public class ExchangeSignalsBetweenMembers {
// 
//     private RTCConnections connections;
//     private ConnectionContextFactory factory;
// 
//     @Inject
//     public ExchangeSignalsBetweenMembers(RTCConnections connections, ConnectionContextFactory factory) {
//         this.connections = connections;
//         this.factory = factory;
//     }
// 
//     public synchronized void begin(Member from, Member to) {
//         connections.put(from, to, factory.create(from, to));
//         connections.get(from, to).ifPresent(ConnectionContext::begin);
//     }
// 
//     public synchronized void execute(InternalMessage message) {
//         connections.get(message.getFrom(), message.getTo()).ifPresent(context -> context.process(message));
//     }
// }
// 
// Path: src/main/java/org/nextrtc/signalingserver/domain/DefaultMessageSender.java
// @Component
// @Scope("singleton")
// @Slf4j
// public class DefaultMessageSender implements MessageSender {
// 
//     private MemberRepository members;
// 
//     @Inject
//     public DefaultMessageSender(MemberRepository members) {
//         this.members = members;
//     }
// 
//     @Override
//     public void send(InternalMessage message) {
//         send(message, 3);
//     }
// 
//     private void send(InternalMessage message, int retry) {
//         if (message.getSignal() != Signal.PING) {
//             log.debug("Outgoing: " + message.transformToExternalMessage());
//         }
//         if (message.getSignal() == Signal.ERROR) {
//             tryToSendErrorMessage(message);
//             return;
//         }
//         Member destination = message.getTo();
//         if (destination == null || !destination.getConnection().isOpen()) {
//             log.warn("Destination member is not set or session is closed! Message will not be send: " + message.transformToExternalMessage());
//             return;
//         }
//         members.findBy(destination.getId()).ifPresent(member ->
//                 lockAndRun(message, member, retry)
//         );
//     }
// 
//     private void tryToSendErrorMessage(InternalMessage message) {
//         try {
//             Connection connection = message.getTo().getConnection();
//             synchronized (connection) {
//                 connection.sendObject(message.transformToExternalMessage());
//             }
//         } catch (Exception e) {
//             throw new RuntimeException("Unable to send message: " + message.transformToExternalMessage(), e);
//         }
//     }
// 
//     private void lockAndRun(InternalMessage message, Member destination, int retry) {
//         try {
//             Connection connection = destination.getConnection();
//             synchronized (destination) {
//                 connection.sendObject(message.transformToExternalMessage());
//             }
//         } catch (Exception e) {
//             if (retry >= 0) {
//                 log.warn("Retrying... " + message.transformToExternalMessage());
//                 send(message, --retry);
//             }
//             log.error("Unable to send message: " + message.transformToExternalMessage() + " error during sending!");
//             throw new RuntimeException("Unable to send message: " + message.transformToExternalMessage(), e);
//         }
//     }
// }
// 
// Path: src/main/java/org/nextrtc/signalingserver/domain/RTCConnections.java
// @Component
// @Scope("singleton")
// public class RTCConnections implements Closeable{
//     private static Table<Member, Member, ConnectionContext> connections = HashBasedTable.create();
// 
//     private ScheduledExecutorService scheduler;
//     private NextRTCProperties properties;
// 
//     @Inject
//     public RTCConnections(ScheduledExecutorService scheduler, NextRTCProperties properties) {
//         this.scheduler = scheduler;
//         this.properties = properties;
//     }
// 
//     @PostConstruct
//     void cleanOldConnections() {
//         scheduler.scheduleWithFixedDelay(this::removeOldConnections,
//                 properties.getMaxConnectionSetupTime(),
//                 properties.getMaxConnectionSetupTime(),
//                 TimeUnit.SECONDS);
//     }
// 
//     void removeOldConnections() {
//         List<ConnectionContext> oldConnections = connections.values().stream()
//                 .filter(context -> !context.isCurrent())
//                 .collect(Collectors.toList());
//         oldConnections.forEach(c -> connections.remove(c.getMaster(), c.getSlave()));
//     }
// 
//     public void put(Member from, Member to, ConnectionContext ctx) {
//         connections.put(from, to, ctx);
//         connections.put(to, from, ctx);
//     }
// 
//     public Optional<ConnectionContext> get(Member from, Member to) {
//         return Optional.ofNullable(connections.get(from, to));
//     }
// 
// 
//     @Override
//     public void close() throws IOException {
//         connections.clear();
//     }
// }
// 
// Path: src/main/java/org/nextrtc/signalingserver/factory/ConnectionContextFactory.java
// public interface ConnectionContextFactory {
//     ConnectionContext create(Member from, Member to);
// }
// 
// Path: src/main/java/org/nextrtc/signalingserver/property/NextRTCProperties.java
// public interface NextRTCProperties {
//     int getMaxConnectionSetupTime();
// 
//     int getPingPeriod();
// 
//     int getSchedulerPoolSize();
// 
//     boolean isJoinOnlyToExisting();
// 
//     String getDefaultConversationType();
// }
// 
// Path: src/main/java/org/nextrtc/signalingserver/repository/MemberRepository.java
// public interface MemberRepository extends Closeable{
//     Collection<String> getAllIds();
// 
//     Optional<Member> findBy(String id);
// 
//     Member register(Member member);
// 
//     void unregister(String id);
// 
// }


import dagger.Module;
import dagger.Provides;
import org.nextrtc.signalingserver.cases.ExchangeSignalsBetweenMembers;
import org.nextrtc.signalingserver.domain.DefaultMessageSender;
import org.nextrtc.signalingserver.domain.RTCConnections;
import org.nextrtc.signalingserver.factory.ConnectionContextFactory;
import org.nextrtc.signalingserver.property.NextRTCProperties;
import org.nextrtc.signalingserver.repository.MemberRepository;
import javax.inject.Singleton;
import java.util.concurrent.ScheduledExecutorService;
package org.nextrtc.signalingserver.modules;



@Module
public abstract class NextRTCMedia {

    @Provides
    static DefaultMessageSender defaultMessageSender(MemberRepository repo) {
        return new DefaultMessageSender(repo);
    }

    @Provides
// Path: src/main/java/org/nextrtc/signalingserver/cases/ExchangeSignalsBetweenMembers.java
// @Component
// @Scope("prototype")
// public class ExchangeSignalsBetweenMembers {
// 
//     private RTCConnections connections;
//     private ConnectionContextFactory factory;
// 
//     @Inject
//     public ExchangeSignalsBetweenMembers(RTCConnections connections, ConnectionContextFactory factory) {
//         this.connections = connections;
//         this.factory = factory;
//     }
// 
//     public synchronized void begin(Member from, Member to) {
//         connections.put(from, to, factory.create(from, to));
//         connections.get(from, to).ifPresent(ConnectionContext::begin);
//     }
// 
//     public synchronized void execute(InternalMessage message) {
//         connections.get(message.getFrom(), message.getTo()).ifPresent(context -> context.process(message));
//     }
// }
// 
// Path: src/main/java/org/nextrtc/signalingserver/domain/DefaultMessageSender.java
// @Component
// @Scope("singleton")
// @Slf4j
// public class DefaultMessageSender implements MessageSender {
// 
//     private MemberRepository members;
// 
//     @Inject
//     public DefaultMessageSender(MemberRepository members) {
//         this.members = members;
//     }
// 
//     @Override
//     public void send(InternalMessage message) {
//         send(message, 3);
//     }
// 
//     private void send(InternalMessage message, int retry) {
//         if (message.getSignal() != Signal.PING) {
//             log.debug("Outgoing: " + message.transformToExternalMessage());
//         }
//         if (message.getSignal() == Signal.ERROR) {
//             tryToSendErrorMessage(message);
//             return;
//         }
//         Member destination = message.getTo();
//         if (destination == null || !destination.getConnection().isOpen()) {
//             log.warn("Destination member is not set or session is closed! Message will not be send: " + message.transformToExternalMessage());
//             return;
//         }
//         members.findBy(destination.getId()).ifPresent(member ->
//                 lockAndRun(message, member, retry)
//         );
//     }
// 
//     private void tryToSendErrorMessage(InternalMessage message) {
//         try {
//             Connection connection = message.getTo().getConnection();
//             synchronized (connection) {
//                 connection.sendObject(message.transformToExternalMessage());
//             }
//         } catch (Exception e) {
//             throw new RuntimeException("Unable to send message: " + message.transformToExternalMessage(), e);
//         }
//     }
// 
//     private void lockAndRun(InternalMessage message, Member destination, int retry) {
//         try {
//             Connection connection = destination.getConnection();
//             synchronized (destination) {
//                 connection.sendObject(message.transformToExternalMessage());
//             }
//         } catch (Exception e) {
//             if (retry >= 0) {
//                 log.warn("Retrying... " + message.transformToExternalMessage());
//                 send(message, --retry);
//             }
//             log.error("Unable to send message: " + message.transformToExternalMessage() + " error during sending!");
//             throw new RuntimeException("Unable to send message: " + message.transformToExternalMessage(), e);
//         }
//     }
// }
// 
// Path: src/main/java/org/nextrtc/signalingserver/domain/RTCConnections.java
// @Component
// @Scope("singleton")
// public class RTCConnections implements Closeable{
//     private static Table<Member, Member, ConnectionContext> connections = HashBasedTable.create();
// 
//     private ScheduledExecutorService scheduler;
//     private NextRTCProperties properties;
// 
//     @Inject
//     public RTCConnections(ScheduledExecutorService scheduler, NextRTCProperties properties) {
//         this.scheduler = scheduler;
//         this.properties = properties;
//     }
// 
//     @PostConstruct
//     void cleanOldConnections() {
//         scheduler.scheduleWithFixedDelay(this::removeOldConnections,
//                 properties.getMaxConnectionSetupTime(),
//                 properties.getMaxConnectionSetupTime(),
//                 TimeUnit.SECONDS);
//     }
// 
//     void removeOldConnections() {
//         List<ConnectionContext> oldConnections = connections.values().stream()
//                 .filter(context -> !context.isCurrent())
//                 .collect(Collectors.toList());
//         oldConnections.forEach(c -> connections.remove(c.getMaster(), c.getSlave()));
//     }
// 
//     public void put(Member from, Member to, ConnectionContext ctx) {
//         connections.put(from, to, ctx);
//         connections.put(to, from, ctx);
//     }
// 
//     public Optional<ConnectionContext> get(Member from, Member to) {
//         return Optional.ofNullable(connections.get(from, to));
//     }
// 
// 
//     @Override
//     public void close() throws IOException {
//         connections.clear();
//     }
// }
// 
// Path: src/main/java/org/nextrtc/signalingserver/factory/ConnectionContextFactory.java
// public interface ConnectionContextFactory {
//     ConnectionContext create(Member from, Member to);
// }
// 
// Path: src/main/java/org/nextrtc/signalingserver/property/NextRTCProperties.java
// public interface NextRTCProperties {
//     int getMaxConnectionSetupTime();
// 
//     int getPingPeriod();
// 
//     int getSchedulerPoolSize();
// 
//     boolean isJoinOnlyToExisting();
// 
//     String getDefaultConversationType();
// }
// 
// Path: src/main/java/org/nextrtc/signalingserver/repository/MemberRepository.java
// public interface MemberRepository extends Closeable{
//     Collection<String> getAllIds();
// 
//     Optional<Member> findBy(String id);
// 
//     Member register(Member member);
// 
//     void unregister(String id);
// 
// }


// Path: src/main/java/org/nextrtc/signalingserver/modules/NextRTCMedia.java
import dagger.Module;
import dagger.Provides;
import org.nextrtc.signalingserver.cases.ExchangeSignalsBetweenMembers;
import org.nextrtc.signalingserver.domain.DefaultMessageSender;
import org.nextrtc.signalingserver.domain.RTCConnections;
import org.nextrtc.signalingserver.factory.ConnectionContextFactory;
import org.nextrtc.signalingserver.property.NextRTCProperties;
import org.nextrtc.signalingserver.repository.MemberRepository;
import javax.inject.Singleton;
import java.util.concurrent.ScheduledExecutorService;

package org.nextrtc.signalingserver.modules;



@Module
public abstract class NextRTCMedia {

    @Provides
    static DefaultMessageSender defaultMessageSender(MemberRepository repo) {
        return new DefaultMessageSender(repo);
    }

    @Provides
    @Singleton