package com.jscape.filetransfer;

import java.util.Arrays;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;

import static com.jscape.util.Parameters.assertNotNull;

/**
 * @author Alex Usun
 */
public interface FileTransferOperation {

    FileTransferOperation NULL = new FileTransferOperation() {
        @Override
        public FileTransferOperation applyTo(FileTransfer transfer) {
            return this;
        }
    };

    FileTransferOperation applyTo(FileTransfer transfer)
            throws FileTransferException;

    class ComponentOperation
            implements FileTransferOperation {

        private final FileTransferOperation[] operations;

        public ComponentOperation(FileTransferOperation... operations) {
            assertNotNull(operations);
            this.operations = operations;
        }

        @Override
        public FileTransferOperation applyTo(FileTransfer transfer)
                throws FileTransferException {
            for (FileTransferOperation operation : this.operations) {
                operation.applyTo(transfer);
            }

            return this;
        }

        @Override
        public String toString() {
            return "ComponentOperation {"
                    + "operations=" + Arrays.toString(this.operations)
                    + '}';
        }
    }

    class CallableOperation
            implements Callable<CallableOperation> {

        public final FileTransferOperation operation;
        public final FileTransfer transfer;

        public CallableOperation(FileTransferOperation operation, FileTransfer transfer) {
            assertNotNull(operation);
            this.operation = operation;

            assertNotNull(transfer);
            this.transfer = transfer;
        }

        @Override
        public CallableOperation call()
                throws Exception {
            this.operation.applyTo(this.transfer);
            return this;
        }

        @Override
        public String toString() {
            return "CallableOperation {"
                    + "operation=" + this.operation
                    + ", transfer=" + this.transfer
                    + '}';
        }
    }
    
    class CallableQueuedTransferOperation
            implements Callable<CallableQueuedTransferOperation> {
        
        public final FileTransferOperation operation;
        public final BlockingQueue<FileTransfer> transferQueue;
        
        public CallableQueuedTransferOperation(FileTransferOperation operation, BlockingQueue<FileTransfer> transferQueue) {
            assertNotNull(operation);
            this.operation = operation;
            
            assertNotNull(transferQueue);
            this.transferQueue = transferQueue;
        }
        
        @Override
        public CallableQueuedTransferOperation call()
                throws Exception {
            FileTransfer transfer = this.transferQueue.take();
            
            try {
                this.operation.applyTo(transfer);
                return this;
            } finally {
                this.transferQueue.put(transfer);
            }
        }
    
        @Override
        public String toString() {
            return "PoolOperation {"
                   + "operation=" + this.operation
                   + ", transferQueue=" + this.transferQueue
                   + '}';
        }
    }
}
