import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.MalformedInputException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;

public class ContrattiIO {
    static void verifica(boolean condizione) {
        if (!condizione) throw new AssertionError("Contratto non rispettato");
    }

    public static void main(String[] args) throws Exception {
        byte[] dati = "caffè-Java".getBytes(StandardCharsets.UTF_8);
        try (var ingresso = new ByteArrayInputStream(dati) {
            @Override public synchronized int read(byte[] b, int offset, int lunghezza) {
                return super.read(b, offset, Math.min(3, lunghezza));
            }
        }; var uscita = new ByteArrayOutputStream()) {
            byte[] blocco = new byte[8];
            int letti;
            while ((letti = ingresso.read(blocco)) != -1) {
                uscita.write(blocco, 0, letti);
            }
            verifica(Arrays.equals(dati, uscita.toByteArray()));
        }
        var decoder = StandardCharsets.UTF_8.newDecoder()
                .onMalformedInput(CodingErrorAction.REPORT)
                .onUnmappableCharacter(CodingErrorAction.REPORT);
        try (var lettore = new InputStreamReader(
                new ByteArrayInputStream(new byte[]{(byte) 0xc3, 0x28}), decoder)) {
            while (lettore.read() != -1) { }
            throw new AssertionError("UTF-8 malformato accettato");
        } catch (MalformedInputException attesa) {
            System.out.println("Letture parziali e UTF-8 malformato: OK");
        }
        var cartella = Files.createTempDirectory("contratti-io-");
        var ingresso = cartella.resolve("ingresso.bin");
        var uscita = cartella.resolve("uscita.bin");
        try {
            Files.write(ingresso, dati, StandardOpenOption.CREATE_NEW);
            try {
                Files.write(ingresso, new byte[]{0}, StandardOpenOption.CREATE_NEW);
                throw new AssertionError("File esistente sovrascritto");
            } catch (FileAlreadyExistsException attesa) {
                verifica(Arrays.equals(dati, Files.readAllBytes(ingresso)));
            }
            try (var origine = FileChannel.open(ingresso, StandardOpenOption.READ);
                 var destinazione = FileChannel.open(uscita,
                         StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW)) {
                ByteBuffer buffer = ByteBuffer.allocate(4);
                while (origine.read(buffer) != -1) {
                    buffer.flip();
                    while (buffer.hasRemaining()) destinazione.write(buffer);
                    buffer.clear();
                }
            }
            verifica(Arrays.equals(dati, Files.readAllBytes(uscita)));
            System.out.println("CREATE_NEW e copia con blocco finale parziale: OK");
        } finally {
            Files.deleteIfExists(uscita);
            Files.deleteIfExists(ingresso);
            Files.deleteIfExists(cartella);
        }
    }
}
