/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.lexer;

import net.ranides.assira.events.CoreEvent;
import net.ranides.assira.events.EventListener;
import net.ranides.assira.trace.AdvLogger;
import net.ranides.assira.trace.LoggerUtils;

/**
 *
 * @author ranides
 */
public final class SafeConsumer {

    private SafeConsumer() {
        // utility class
    }

    public static <UserType> ByteConsumer<UserType> make(ByteConsumer<UserType> consumer) {
        return new SafeBC<>(consumer, new LogListener(consumer.getClass()));
    }

    public static <UserType> CharConsumer<UserType> make(CharConsumer<UserType> consumer) {
        return new SafeCC<>(consumer, new LogListener(consumer.getClass()));
    }

    public static <UserType> ByteConsumer<UserType> make(ByteConsumer<UserType> consumer, EventListener<? super CoreEvent.Failure> handler) {
        return new SafeBC<>(consumer, handler);
    }

    public static <UserType> CharConsumer<UserType> make(CharConsumer<UserType> consumer, EventListener<? super CoreEvent.Failure> handler) {
        return new SafeCC<>(consumer, handler);
    }

    private static final class SafeBC<UserType> implements ByteConsumer<UserType> {

        private final EventListener<? super CoreEvent.Failure> handler;
        private final ByteConsumer<UserType> consumer;

        public SafeBC(ByteConsumer<UserType> consumer, EventListener<? super CoreEvent.Failure> handler) {
            this.handler = handler;
            this.consumer = consumer;
        }

        @Override
        public void consume(UserType type, byte[] data) {
            try {
                consumer.consume(type, data);
            } catch(Throwable cause) { //NOPMD - jesteśmy świadomi, że errors też tu lecą...
                handler.handleEvent(CoreEvent.failure(handler, cause));
            }
        }

    }

    private static final class SafeCC<UserType> implements CharConsumer<UserType> {

        private final EventListener<? super CoreEvent.Failure> handler;
        private final CharConsumer<UserType> consumer;

        public SafeCC(CharConsumer<UserType> consumer, EventListener<? super CoreEvent.Failure> handler) {
            this.handler = handler;
            this.consumer = consumer;
        }

        @Override
        public void consume(UserType type, char[] data) {
            try {
                consumer.consume(type, data);
            } catch(Throwable cause) { //NOPMD - jesteśmy świadomi, że errors też tu lecą...
                handler.handleEvent(CoreEvent.failure(handler, cause));
            }
        }

    }

    private static final class LogListener implements EventListener<CoreEvent.Failure> {
        private final AdvLogger logger;

        public LogListener(Class<?> clazz) {
            this.logger = LoggerUtils.getLogger(clazz);
        }

        @Override
        public void handleEvent(CoreEvent.Failure event) {
            logger.xerror("Unexpected exception: %s", event.cause(), event.cause());
        }

    }

}
