package net.ranides.assira.generic;

import lombok.Data;
import lombok.EqualsAndHashCode;
import org.junit.Test;

import net.ranides.assira.io.ResourceUtils;
import net.ranides.assira.text.FormatHex;

import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;

import static org.junit.Assert.*;

public class SerializableCodeTest {

//    @Ignore("manual")
//    @Test
//    public void demo() throws IOException {
//        System.out.println(FormatHex.format(SerializationUtils.write(SerializableCode.wrap(new Adder1(7)))));
//
//        System.out.println(FormatHex.format(SerializationUtils.write(SerializableCode.wrap(new Multiplier1(5)))));
//
//        System.out.println(FormatHex.format(SerializationUtils.write(new Divider1(3))));
//
//    }

    @Test
    public void demo() throws IOException {
        byte[] add = SerializationUtils.write(SerializableCode.wrap(new Adder2(7)));
        byte[] mul = SerializationUtils.write(SerializableCode.wrap(new Multiplier2(5)));
        byte[] div = SerializationUtils.write(SerializableCode.wrap(new Divider2(5)));

        assertNotNull(add);
        assertNotNull(mul);
        assertNotNull(div);
    }

    @Test
    public void demo2() throws IOException {
        byte[] div1 = SerializationUtils.write(new Divider2(5));
        byte[] div2 = SerializationUtils.write(SerializableCode.wrap(new Divider2(5)));

        assertNotNull(div1);
        assertNotNull(div2);
        assertArrayEquals(div1, div2);
    }


    @Test
    public void fromBytes() throws IOException {
        Operator add = SerializationUtils.read(Operator.class, readFromFile("bytecode/add.txt"));
        Operator mul = SerializationUtils.read(Operator.class, readFromFile("bytecode/mul.txt"));
        Operator div1 = SerializationUtils.read(Operator.class, readFromFile("bytecode/div.txt"));
        Operator div2 = SerializationUtils.read(Operator.class, readFromFile("bytecode/div.txt"));

        assertEquals("net.ranides.assira.generic.SerializableCodeTest$Adder1", add.getClass().getName());
        assertEquals("net.ranides.assira.generic.SerializableCodeTest$Multiplier1", mul.getClass().getName());
        assertEquals("net.ranides.assira.generic.SerializableCodeTest$Divider1", div1.getClass().getName());
        assertEquals("net.ranides.assira.generic.SerializableCodeTest$Divider1", div2.getClass().getName());

        assertEquals(22, add.apply(15));
        assertEquals(75, mul.apply(15));
        assertEquals(5, div1.apply(15));
        assertEquals(5, div2.apply(15));
    }

    private static byte[] readFromFile(String file) throws IOException {
        String text = ResourceUtils.text(file, StandardCharsets.US_ASCII);
        return FormatHex.parse(text.replaceAll("//.*[\\n\\r]+", "").replaceAll("[\\n\\r]+", " "));
    }

    public interface Operator {

        int apply(int x);

    }

    @Data
    @EqualsAndHashCode(callSuper = false)
    public static class Divider2 extends SerializableCode implements Operator {

        private final int a;

        @Override
        public int apply(int x) {
            return x / a;
        }

    }

    @Data
    public static class Adder2 implements Operator, Serializable {

        private final int a;

        @Override
        public int apply(int x) {
            return x + a;
        }

    }

    @Data
    public static class Multiplier2 implements Operator, Serializable {

        private final int a;

        @Override
        public int apply(int x) {
            return x * a;
        }
    }

}