import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HexFormat;

public class SerializationDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Path file = Path.of("build", "demo", "lista.bin");
        Files.createDirectories(file.getParent());

        Playlist list = new Playlist("Criollas");
        list.add(new Song("Contigo Perú", "Arturo Cavero", 254));
        list.add(new Song("Cariñito", "Los Hijos del Sol", 187));
        IO.println("antes:   " + list.songs().size() + " canciones, " + list.totalSeconds() + " s");

        try (ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(file))) {
            out.writeObject(list);
        }
        byte[] bytes = Files.readAllBytes(file);
        IO.println("archivo: " + bytes.length + " bytes, empieza con "
                + HexFormat.ofDelimiter(" ").formatHex(bytes, 0, 4));

        try (ObjectInputStream in = new ObjectInputStream(Files.newInputStream(file))) {
            Playlist back = (Playlist) in.readObject();
            IO.println("después: " + back.songs().size() + " canciones, " + back.totalSeconds()
                    + " s (totalSeconds es transient)");
            IO.println("¿mismas canciones? " + back.songs().equals(list.songs()));
        }

        // Bytes alterados: la duración 254 (00 00 00 fe) pasa a -5 (ff ff ff fb).
        IO.println("clase común: " + tamper(new PlainSong("Contigo Perú", 254)));
        IO.println("record:      " + tamper(new Song("Contigo Perú", "Arturo Cavero", 254)));
    }

    /** Serializa, cambia el entero 254 por -5 en los bytes y deserializa. */
    static String tamper(Object obj) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        try (ObjectOutputStream out = new ObjectOutputStream(buffer)) {
            out.writeObject(obj);
        }
        byte[] b = buffer.toByteArray();
        for (int i = 0; i + 3 < b.length; i++) {
            if (b[i] == 0 && b[i + 1] == 0 && b[i + 2] == 0 && b[i + 3] == (byte) 0xfe) {
                b[i] = b[i + 1] = b[i + 2] = (byte) 0xff;
                b[i + 3] = (byte) 0xfb;
            }
        }
        try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(b))) {
            return String.valueOf(in.readObject());
        } catch (IOException e) {
            return e.toString();
        }
    }
}
