package com.github.rschmitt.dynamicobject;

import clojure.java.api.Clojure;
import clojure.lang.IPersistentMap;
import clojure.lang.Keyword;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

import static org.junit.Assert.assertEquals;

public class TaggedReaderTest {
    @Before
    public void setup() {
        DynamicObject.registerType(DumbClass.class, new DumbClassTranslator());
    }

    @After
    public void teardown() {
        DynamicObject.deregisterType(DumbClass.class);
    }

    @Test
    public void roundTrip() {
        String edn = "{:dumb [#MyDumbClass{:version 1, :str \"str\"}]}";

        DumbClassHolder deserialized = DynamicObject.deserialize(edn, DumbClassHolder.class);
        String serialized = DynamicObject.serialize(deserialized);

        assertEquals(edn, serialized);
        assertEquals(new DumbClass(1, "str"), deserialized.dumb().get(0));
    }
}

// This is a DynamicObject that contains a regular POJO.
interface DumbClassHolder extends DynamicObject<DumbClassHolder> {
    List<DumbClass> dumb();
}

// This is a translation class that functions as an Edn reader/writer for its associated POJO.
class DumbClassTranslator extends EdnTranslator<DumbClass> {
    @Override
    public DumbClass read(Object obj) {
        IPersistentMap map = (IPersistentMap) obj;
        long version = (Long) map.valAt(Clojure.read(":version"));
        String str = (String) map.valAt(Clojure.read(":str"));
        return new DumbClass(version, str);
    }

    @Override
    public String write(DumbClass obj) {
        return String.format("{:version %d, :str \"%s\"}", obj.getVersion(), obj.getStr());
    }

    @Override
    public String getTag() {
        return "MyDumbClass"; // This is deliberately different from the class name.
    }
}

// This is a POJO that has no knowledge of Edn.
class DumbClass {
    private final long version;
    private final String str;

    DumbClass(long version, String str) {
        this.version = version;
        this.str = str;
    }

    public long getVersion() {
        return version;
    }

    public String getStr() {
        return str;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        DumbClass dumbClass = (DumbClass) o;

        return version == dumbClass.version && str.equals(dumbClass.str);
    }

    @Override
    public int hashCode() {
        int result = (int) (version ^ (version >>> 32));
        result = 31 * result + str.hashCode();
        return result;
    }

    @Override
    public String toString() {
        throw new UnsupportedOperationException("I'm a useless legacy toString() method that doesn't produce Edn!");
    }
}

@@ -41,106 +41,8 @@
 t;%0A%0A
-import clojure.java.api.Clojure;%0Aimport clojure.lang.IPersistentMap;%0Aimport clojure.lang.Keyword;%0A
 impo
@@ -1215,178 +1215,75 @@
     
-IPersistentMap map = (IPersistentMap) obj;%0A        long version = (Long) map.valAt(Clojure.read(%22:version%22));%0A        String str = (String) map.valAt(Clojure.read(%22:str%22)
+DumbClassProxy proxy = DynamicObject.wrap(obj, DumbClassProxy.class
 );%0A 
@@ -1314,20 +1314,36 @@
 ass(
+proxy.
 version
-, 
+(), proxy.
 str
+()
 );%0A 
@@ -1631,24 +1631,146 @@
  name.%0A    %7D
+%0A%0A    interface DumbClassProxy extends DynamicObject%3CDumbClassProxy%3E %7B%0A        long version();%0A        String str();%0A    %7D
 %0A%7D%0A%0A// This 

Use a smarter approach to implementing an EdnTranslator