// Copyright (c) Cognitect, Inc.
// All rights reserved.

package com.cognitect.transit;

import com.cognitect.transit.impl.AbstractParser;
import com.cognitect.transit.impl.JsonParser;
import com.cognitect.transit.impl.ReadCache;
import com.cognitect.transit.impl.WriteCache;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.apache.commons.codec.binary.Base64;

import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.*;

public class TransitJSONMachineModeTest extends TestCase {

    public TransitJSONMachineModeTest(String testName) {
        super(testName);
    }

    public static Test suite() {
        return new TestSuite(TransitJSONMachineModeTest.class);
    }

    // Reading

    public Reader reader(String s) throws IOException {

        InputStream in = new ByteArrayInputStream(s.getBytes());
        return TransitFactory.reader(TransitFactory.Format.JSON, in, null);
    }

    public void testReadTime() throws Exception {
        Date d = new Date();
        long t = d.getTime();
        Date dt = ((Date)reader("\"~t" + t + "\"").read());
        assertEquals(t, dt.getTime());

        List l = ((List)reader("[\"~t" + t + "\"]").read());
        dt = (Date) l.get(0);

        assertEquals(t, dt.getTime());

        l = ((List)reader("[\"~t1776-07-04T12:00:00.000Z\",\"~t1970-01-01T00:00:00.000Z\",\"~t2000-01-01T12:00:00.000Z\",\"~t2014-04-07T22:17:17.000Z\"]").read());
        assertEquals(4, l.size());

//        for (Object o : l) {
//            Date d1
//        }

        l = ((List)reader("[\"~t-6106017600000\",\"~t0\",\"~t946728000000\",\"~t1396909037000\"]").read());
        assertEquals(4, l.size());
    }

    public void testReadMap() throws IOException {
        Map m = (Map)reader("[\"~^\",\"foo\",1,\"bar\",2]").read();

        assertTrue(m instanceof HashMap);
        assertTrue(m.containsKey("foo"));
        assertTrue(m.containsKey("bar"));
        assertEquals(1L, m.get("foo"));
        assertEquals(2L, m.get("bar"));
    }

    public void testReadMapWithNested() throws IOException {
        Map m = (Map)reader("[\"~^\",\"foo\",1,\"bar\",[\"~^\",\"baz\",3]]").read();

        assertTrue(m instanceof HashMap);
        assertTrue(m.get("bar") instanceof HashMap);
        assertTrue(((Map)m.get("bar")).containsKey("baz"));
        assertEquals(3L, ((Map)m.get("bar")).get("baz"));
    }

    // Writing

    public String write(Object o) throws Exception {

        OutputStream out = new ByteArrayOutputStream();
        Writer w = TransitFactory.writer(TransitFactory.Format.JSON_MACHINE, out, null);
        w.write(o);
        return out.toString();

    }

    public boolean isEqual(Object o1, Object o2) {
        if(o1 instanceof Boolean)
            return o1 == o2;
        else
            return false;
    }

    public void testRoundTrip() throws Exception {

        Object inObject = true;

        OutputStream out = new ByteArrayOutputStream();
        Writer w = TransitFactory.writer(TransitFactory.Format.JSON_MACHINE, out, null);
        w.write(inObject);
        String s = out.toString();
        InputStream in = new ByteArrayInputStream(s.getBytes());
        Reader reader = TransitFactory.reader(TransitFactory.Format.JSON, in, null);
        Object outObject = reader.read();

        assertTrue(isEqual(inObject, outObject));
    }

    public void testWriteMap() throws Exception {

        Map m = new HashMap();
        m.put("foo", 1);
        m.put("bar", 2);

        assertEquals("[\"~^\",\"foo\",1,\"bar\",2]", write(m));
    }

    public void testWriteEmptyMap() throws Exception {

        Map m = new HashMap();
        assertEquals("[\"~^\"]", write(m));
    }

    public String scalar(String value) {
        return "{\"~#'\":"+value+"}";
    }

    public void testWriteTime() throws Exception {

        final Date d = new Date();
        long tm = d.getTime();
        String t = write(d);
        List l = new ArrayList() {{ add(d); }};

        assertEquals(scalar("\"~t" + tm + "\""), t);

        t = write(l);
        assertEquals("[\"~t" + tm + "\"]", t);

        final Date da[] = {new Date(-6106017600000l),
                           new Date(0),
                           new Date(946728000000l),
                           new Date(1396909037000l)};

        l = Arrays.asList(da);
        t = write(l);
        assertEquals( "[\"~t" + da[0].getTime() + "\","
                     + "\"~t" + da[1].getTime() + "\","
                     + "\"~t" + da[2].getTime() + "\","
                     + "\"~t" + da[3].getTime() + "\"]", t);
    }

}

@@ -1418,33 +1418,42 @@
 me());%0A%0A        
-l
+List human
  = ((List)reader
@@ -1607,33 +1607,37 @@
 assertEquals(4, 
-l
+human
 .size());%0A%0A//   
@@ -1631,26 +1631,24 @@
 ze());%0A%0A
-//
         
 for (Obj
@@ -1643,73 +1643,20 @@
     
-for (Object o : l) %7B%0A//            Date d1%0A//        %7D%0A%0A        l
+List machine
  = (
@@ -1774,17 +1774,23 @@
 uals(4, 
-l
+machine
 .size())
@@ -1786,24 +1786,220 @@
 ine.size());
+%0A%0A        for (int i = 0; i %3C human.size(); i++) %7B%0A            Date dh = (Date) human.get(i);%0A            Date dm = (Date) machine.get(i);%0A%0A            assertEquals(dh.compareTo(dm), 0);%0A        %7D
 %0A    %7D%0A%0A    

Test that the dates constructed via JSON-H are the same as the dates constructed via JSON-M