package io.undertow.websockets.extensions;

import io.undertow.connector.PooledByteBuffer;
import io.undertow.websockets.core.StreamSinkFrameChannel;
import io.undertow.websockets.core.StreamSourceFrameChannel;

import java.io.IOException;
import java.util.List;

public class CompositeExtensionFunction implements ExtensionFunction {

    private final ExtensionFunction[] delegates;

    private CompositeExtensionFunction(ExtensionFunction... delegates) {
        this.delegates = delegates;
    }

    public static ExtensionFunction compose(List<ExtensionFunction> functions) {
        if (null == functions) {
            return NoopExtensionFunction.INSTANCE;
        }
        return compose(functions.toArray(new ExtensionFunction[functions.size()]));
    }

    public static ExtensionFunction compose(ExtensionFunction... functions) {
        if (functions == null || functions.length == 0) {
            return NoopExtensionFunction.INSTANCE;
        } else if (functions.length == 1) {
            return functions[0];
        }

        return new CompositeExtensionFunction(functions);
    }

    @Override
    public boolean hasExtensionOpCode() {
        for (ExtensionFunction delegate : delegates) {
            if (delegate.hasExtensionOpCode()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public int writeRsv(int rsv) {
        for (ExtensionFunction ext : delegates) {
            rsv = ext.writeRsv(rsv);
        }

        return rsv;
    }

    @Override
    public PooledByteBuffer transformForWrite(PooledByteBuffer pooledBuffer, StreamSinkFrameChannel channel, boolean lastFrame) throws IOException {
        PooledByteBuffer result = pooledBuffer;
        for (ExtensionFunction delegate : delegates) {
            result = delegate.transformForWrite(result, channel, lastFrame);
        }
        return result;
    }

    @Override
    public PooledByteBuffer transformForRead(PooledByteBuffer pooledBuffer, StreamSourceFrameChannel channel, boolean lastFragementOfMessage) throws IOException {
        PooledByteBuffer result = pooledBuffer;
        // TODO do we iterate over functions in the opposite order when reading vs writing?
        for (ExtensionFunction delegate : delegates) {
            result = delegate.transformForRead(result, channel, lastFragementOfMessage);
        }
        return result;
    }

    @Override
    public void dispose() {
        for (ExtensionFunction delegate : delegates) {
            delegate.dispose();
        }
    }
}