/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.io;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import net.ranides.assira.generic.CompareUtils;
import net.ranides.assira.text.Charsets;
import net.ranides.assira.text.StringIO;
import net.ranides.assira.text.StringUtils;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class IOStreamsTest {
    
    
    @Test
    public void testCopy() throws IOException {
        assertCopy(1);
        assertCopy(7);
        assertCopy(740);
        assertCopy(4096 + 0);
        assertCopy(4096 + 1);
        assertCopy(4096 + 740);
        assertCopy(7* 4096 + 0);
        assertCopy(7* 4096 + 1);
        assertCopy(7* 4096 + 740);
    }
    
    @Test
    public void testEquivalent() throws IOException {
        byte[] data1 = random(7*4096+33, 777L);
        byte[] data2 = random(7*4096+33, 777L);
        byte[] data3 = random(7*4096+33, 778L);
        byte[] data4 = random(7*4096+34, 777L);
        assertTrue(IOStreams.equivalent(new ByteArrayInputStream(data1), new ByteArrayInputStream(data1)));
        assertTrue(IOStreams.equivalent(new ByteArrayInputStream(data1), new ByteArrayInputStream(data2)));
        assertFalse(IOStreams.equivalent(new ByteArrayInputStream(data1), new ByteArrayInputStream(data3)));
        assertFalse(IOStreams.equivalent(new ByteArrayInputStream(data1), new ByteArrayInputStream(data4)));
    }
    
    private static void assertCopy(int size) throws IOException {
        byte[] data = random(size, 777L);
        ByteArrayOutputStream bos1 = new ByteArrayOutputStream();
        
        IOStreams.copy(new ByteArrayInputStream(data), bos1);
        assertArrayEquals(data, bos1.toByteArray());
        
        ByteArrayOutputStream bos2 = new ByteArrayOutputStream();
        List<Long> info = new ArrayList<>();
        IOStreams.copy(new ByteArrayInputStream(data), bos2, info::add);
        assertEquals( (int)Math.ceil(size/4096.0), info.size());
        assertEquals( size, info.get(info.size()-1).longValue() );
    }
    
    @SuppressFBWarnings
    private static byte[] random(int size, long seed) {
        byte[] ret = new byte[size];
        new Random(seed).nextBytes(ret);
        return ret;
    }
    
    private static String randomstr(int size, long seed) {
        Random rand = new Random(seed);
        char[] ret = new char[size];
        for(int i=0; i<ret.length; i++) {
            ret[i] = (char)rand.nextInt(0x7FFF);
        }
        return new String(ret);
    }
    
    @Test
    public void testStreamWriter() throws IOException {
        assertWrite(Charsets.UTF8, "Hello world", ". Text", ".");
        assertWrite(Charsets.UTF8, randomstr(256*1024, 777));
    }
    
    private void assertWrite(Charset cs, String... values) throws IOException {
        String expected = StringUtils.join(values, "");
        StringWriter out = new StringWriter();
        OutputStream ostream = IOStreams.stream(out, cs);
        for(String value : values) {
            ostream.write(value.getBytes(cs));
        }
        ostream.flush();
        assertEquals(expected, out.toString());
    }
    
    @Test
    public void testStreamReader() throws IOException {
        assertRead("Quick brown fox jumps over lazy dog\n", Charsets.US_ASCII);
        
        assertRead(randomstr(256*1024, 777), Charsets.UTF8);
        
        assertRead("Pchnąć w tę\nłódź jeża\nlub ośm\nskrzyń fig\n", Charsets.UTF8);
    }
    
    private void assertRead(String text, Charset cs) throws IOException {
        Reader reader = new StringReader(text);
        InputStream istream = IOStreams.stream(reader, cs);
        assertEquals(text, StringIO.read(istream, cs));
    }
    
    public static void main(String[] args) {
        Charset.availableCharsets().values().stream().forEach(cs -> {
            try {
                float avg = cs.newEncoder().averageBytesPerChar();
                if(CompareUtils.equals(avg, 1.0, 0.01)) {
                    return;
                }
                System.out.printf("%.2f %s%n", avg, cs.name());
            } catch(UnsupportedOperationException nse) {
                // do nothig
            }
        });
    }
    
}
