package com.jscape.filetransfer;

import com.jscape.crypto.Hash;
import com.jscape.crypto.MD5;
import com.jscape.util.HexFormat;

import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.UUID;

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

/**
 * @author Alex Usun
 */
public class FindUniqueFilenameOperation
        implements FileTransferOperation {

    private static final int MAX_ATTEMPTS = 100;

    private final String prefix;
    private String filename;

    public FindUniqueFilenameOperation(String prefix) {
        assertNotNull(prefix);
        this.prefix = prefix;
    }

    public String filename() {
        return this.filename;
    }

    @Override
    public FindUniqueFilenameOperation applyTo(FileTransfer transfer)
            throws FileTransferException {
        this.filename = null;
        List<String> entries = currentEntriesOf(transfer);

        Hash hash = MD5.defaultHash();
        String filename;
        int attempt = 0;

        do {
            String randomPart = nextRandomPart(hash);
            filename = String.format("%s%s", this.prefix, randomPart);
        } while (entries.contains(filename) && ++attempt < MAX_ATTEMPTS);

        if (attempt == MAX_ATTEMPTS) {
            throw new FileTransferException("Error finding unique filename.");
        }

        this.filename = filename;

        return this;
    }

    @SuppressWarnings("unchecked")
    private List<String> currentEntriesOf(FileTransfer transfer)
            throws FileTransferException {
        Enumeration entries = transfer.getNameListing();
        return Collections.<String>list(entries);
    }

    private String nextRandomPart(Hash hash) {
        UUID uuid = UUID.randomUUID();
        return hashOf(uuid.toString().getBytes(StandardCharsets.US_ASCII), hash);
    }

    private String hashOf(byte[] data, Hash hash) {
        hash.reset();
        byte[] hashData = hash.compute(data);

        return HexFormat.formatData(hashData, "");
    }
}
