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

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.CharBuffer;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import net.ranides.assira.generic.Serializer;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
public class StrBuilderTest {
    
    public StrBuilderTest() {
    }

    @Test
    public void testNull() {
        StrBuilder builder = new StrBuilder();
        
        builder.setNullText("#");
        builder.append((StrBuilder)null);
        builder.append((CharSequence)null);
        builder.append((Object)null);
        builder.append((String)null);
        builder.append((StringBuffer)null);
        builder.append((char[])null);
        
        builder.setNullText("$");
        builder.append((StrBuilder)null, 3, 14);
        builder.append((CharSequence)null, 3, 14);
        builder.append((String)null, 3, 14);
        builder.append((StringBuffer)null, 3, 14);
        builder.append((char[])null, 3, 14);
        
        builder.setNullText("");
        builder.append((String)null);
        
        builder.setNullText(null);
        builder.append((String)null);

        assertEquals("######$$$$$", builder.toString());
    }
    
    @Test
    public void testAppend() {
        StrBuilder builder = new StrBuilder(32);
        builder.append( new StrBuilder("Hello").append(" world") );
        builder.append( Strings.asSequence(new char[]{'!',' '}) );
        builder.append( (Object)Integer.valueOf(14) );
        builder.append(" Next ");
        builder.append( new StringBuffer("sequence").append(" in "));
        builder.append( "test. ".toCharArray() );
        
        builder.append( new StrBuilder("--ABC").append("DE--"), 2, 5);
        builder.append( Strings.asSequence(new char[]{'-','F','G','-'}), 1, 2);
        builder.append( "...[letters]...", 3, 9);
        builder.append( "--end --".toCharArray(), 2, 4);
        builder.append( new StringBuffer("--now.--").append("DE--"), 2, 4);
        
        assertEquals("Hello world! 14 Next sequence in test. ABCDEFG[letters]end now.", builder.toString());
        
        StrBuilder builder2 = new StrBuilder("primitives: ");
        builder2.append(true);
        builder2.append(false);
        builder2.append('.');
        builder2.append(14.0);
        builder2.append('/');
        builder2.append(14.0f);
        builder2.append('/');
        builder2.append(14);
        builder2.append('/');
        builder2.append(14L);
        assertEquals("primitives: truefalse.14.0/14.0/14/14", builder2.toString());
    }
    
    @Test
    public void testResize() {
        StrBuilder builder = new StrBuilder(8);
        builder.append("hello");
        builder.append(" world!?");
        
        builder.resize(11);
        assertEquals(11, builder.length());
        assertEquals("hello world", builder.toString());
        
        builder.resize(16, '.');
        assertEquals(16, builder.length());
        assertEquals("hello world.....", builder.toString());
        
        builder.resize(18);
        assertEquals("hello world.....\000\000", builder.toString());
        
        int len = builder.length();
        assertTrue(builder.length() < builder.capacity());
        
        builder.minimizeCapacity();
        assertEquals(len, builder.capacity());
        assertEquals(len, builder.length());
        
        builder.minimizeCapacity();
        assertEquals(len, builder.capacity());
        assertEquals(len, builder.length());
        
    }
    
    @Test
    public void testChars() {
        StrBuilder builder = new StrBuilder(32);
        builder.append("hello");
        builder.append(" world!?");
        
        assertEquals('h', builder.charAt(0));
        assertEquals('!', builder.charAt(11));
        assertEquals('?', builder.charAt(12));
        try {
            builder.charAt(-5);
            fail("StringIndexOutOfBoundsException expected");
        } catch(StringIndexOutOfBoundsException cause) {
            assertTrue(true);
        }
        try {
            builder.charAt(13);
            fail("StringIndexOutOfBoundsException expected");
        } catch(StringIndexOutOfBoundsException cause) {
            assertTrue(true);
        }
        try {
            builder.charAt(16);
            fail("StringIndexOutOfBoundsException expected");
        } catch(StringIndexOutOfBoundsException cause) {
            assertTrue(true);
        }
        
        assertEquals("hello world!?", String.valueOf(builder.getChars()));
        assertEquals("ello", String.valueOf(builder.getChars(1, 5)));
        
        char[] target1 = "-------------".toCharArray();
        char[] out1 = builder.getChars(target1);
        assertSame(target1, out1);
        assertEquals("hello world!?", String.valueOf(out1));
        
        char[] target4 = "-------------....".toCharArray();
        char[] out4 = builder.getChars(target4);
        assertSame(target4, out4);
        assertEquals("hello world!?....", String.valueOf(out4));
        
        char[] target2 = "---------".toCharArray();
        char[] out2 = builder.getChars(target2);
        assertNotSame(target2, out2);
        assertEquals("---------", String.valueOf(target2));
        assertEquals("hello world!?", String.valueOf(out2));
        
        char[] out3 = builder.getChars(null);
        assertEquals("hello world!?", String.valueOf(out3));
        
        char[] target5 = "----".toCharArray();
        builder.getChars(1, 5, target5, 0);
        assertEquals("ello", String.valueOf(target5));
        
        char[] target6 = "..----..".toCharArray();
        builder.getChars(1, 5, target6, 2);
        assertEquals("..ello..", String.valueOf(target6));
    }
    
    @Test
    public void testReverse() {
        StrBuilder builder = new StrBuilder();
        
        builder.append("world");
        builder.reverse();
        assertEquals("dlrow", builder.toString());
        
        builder.append(" olleh");
        builder.reverse();
        assertEquals("hello world", builder.toString());
    }
    
    @Test
    public void testWriter() throws IOException {
        StrBuilder builder1 = new StrBuilder();
        Writer writer1 = builder1.asWriter();
        
        writer1.write("Hello ");
        writer1.write("--world-", 2, 5);
        writer1.write('.');
        
        assertEquals("Hello world.", builder1.toString());
    }
    
    @Test
    public void testReaderWriter() throws IOException {
        StrBuilder builder = new StrBuilder();
        Writer writer = builder.asWriter();
        Reader reader = builder.asReader();
        
        writer.append("Hello ");
        writer.append("--world-", 2, 7);
        writer.append('.');
        
        assertEquals("Hello world.", builder.toString());
        
        assertTrue(reader.markSupported());
        reader.mark(Integer.MAX_VALUE);
        
        assertEquals('H', reader.read());
        char[] target2 = "..######..".toCharArray();
        assertEquals(6, reader.read(target2, 2, 6));
        assertEquals("..ello w..", String.valueOf(target2));
        
        char[] target3 = "####".toCharArray();
        assertEquals(2, reader.skip(2));
        assertEquals(3, reader.read(target3));
        assertEquals("ld.#", String.valueOf(target3));
        
        assertFalse(reader.ready());
        writer.append(" EOF");
        assertTrue(reader.ready());
        
        assertEquals(4, reader.skip(128));
        assertFalse(reader.ready());
        reader.reset();
        assertTrue(reader.ready());
        
        char[] target4 = "######".toCharArray();
        assertEquals(6, reader.read(target4));
        assertEquals("Hello ", String.valueOf(target4));
        
        assertEquals(7, reader.skip(7));
        CharBuffer buffer1 = CharBuffer.wrap("--------".toCharArray());
        assertEquals(3, reader.read(buffer1));
        assertEquals("EOF-----", String.valueOf(buffer1.array()));
    }
    
    @Test
    public void testSubstring() {
        StrBuilder builder = new StrBuilder("Hello HERE world.");
        
        assertEquals("HERE", builder.subSequence(6, 10).toString());
        assertEquals("HERE", builder.substring(6, 10));
        assertEquals("world.", builder.substring(11));
    }
    
    @Test
    public void testEquals() {
        StrBuilder builder1 = new StrBuilder("Hello").append(' ').append("world.");
        StrBuilder builder2 = new StrBuilder(512).append("Hello ").append("world").append('.');
        
        assertNotSame(builder1.capacity(), builder2.capacity());
        assertEquals(builder1, builder2);
        
        builder1.minimizeCapacity();
        builder2.ensureCapacity(1024);
        assertNotSame(builder1.capacity(), builder2.capacity());
        assertEquals(builder1, builder2);
        
        assertEquals(builder1.hashCode(), builder2.hashCode());
        
        assertEquals(builder1, builder1);
        
        builder1.setCharAt(2, 'z');
        assertFalse( builder1.equals(builder2) );
        builder2.setCharAt(2, 'z');
        assertEquals(builder1, builder2);
        
        builder2.clear();
        assertFalse( builder1.equals(builder2) );
        assertEquals(0, builder2.length() );
        
        builder2.append("Hezlo world.");
        assertEquals(builder1, builder2);
        
        assertFalse( builder2.equals("Hezlo world.") );
        assertTrue( builder2.toString().equals("Hezlo world.") );
    }
    
    @Test
    public void testSerialize() throws IOException {
        StrBuilder builder1 = new StrBuilder("Hello").append(' ').append("world.");
        StrBuilder builder2 = Serializer.copy(builder1);
        
        assertNotSame(builder1, builder2);
        assertEquals(builder1, builder2);
    }
    
    @Test
    public void testItems() {
        StrBuilder builder = new StrBuilder();
        
        Integer[] array = {1,2,3,4};
        List<Integer> list = Arrays.asList(array);
        
        
        builder.items(list);
        builder.items(list.iterator());
        builder.items(array);
        builder.append("-");
        
        builder.items(list, " ");
        builder.items(list.iterator(), " ");
        builder.items(array, " ");
        builder.append("-");
        
        assertEquals("123412341234-1 2 3 41 2 3 41 2 3 4-", builder.toString());
        
        try {
            builder.item().append("x");
            fail("IllegalStateException expected");
        } catch(IllegalStateException cause) {
            assertTrue(true);
        }
        
        try {
            builder.endlist();
            fail("IllegalStateException expected");
        } catch(IllegalStateException cause) {
            assertTrue(true);
        }
        
        assertEquals("123412341234-1 2 3 41 2 3 41 2 3 4-", builder.toString());
        
        builder.list("-");
        builder.item().append("x");
        assertEquals("123412341234-1 2 3 41 2 3 41 2 3 4-x", builder.toString());
        builder.clear();
        try {
            builder.endlist();
            fail("IllegalStateException expected");
        } catch(IllegalStateException cause) {
            assertTrue(true);
        }
        
        
        builder.setNullText(",");
        builder.items(list, null);
        assertEquals("1,2,3,4", builder.toString());
        
        // nested lists
        builder.clear();
        builder.list(", ");
        for(char c='a'; c<'e'; c++) {
            builder.item().append(c).append("=[").list("-");
            for(int i=0; i<4; i++) {
                builder.item().append(i);
            }
            builder.endlist().append("]");
        }
        builder.endlist();
        
        assertEquals("a=[0-1-2-3], b=[0-1-2-3], c=[0-1-2-3], d=[0-1-2-3]", builder.toString());
    }
    
    @Test
    public void testMidRightLeft() {
        StrBuilder builder = new StrBuilder("hello world");
        
        assertEquals("llo", builder.mid(2, 3));
        assertEquals("", builder.mid(2, 0));
        assertEquals("", builder.mid(120, 5));
        
        assertEquals("hello", builder.mid(0, 5));
        assertEquals("hello", builder.mid(-5, 10));
        assertEquals("world", builder.mid(6, 20));
        
        assertEquals("hello world", builder.left(50) );
        assertEquals("hello", builder.left(5) );
        assertEquals("", builder.left(0) );
        assertEquals("", builder.left(-10) );
        
        assertEquals("hello world", builder.right(50) );
        assertEquals("world", builder.right(5) );
        assertEquals("", builder.right(0) );
        assertEquals("", builder.right(-10) );
        
    }
    
    @Test
    public void testEndl() {
        StrBuilder builder = new StrBuilder();
        builder.append("line1").endl();
        builder.setEndl("-");
        builder.append("line2").endl();
        builder.setEndl(null);
        builder.append("line3").endl();
        String expected = 
            "line1" + StringUtils.ENDL +
            "line2" + "-" +
            "line3" + StringUtils.ENDL;
        assertEquals(expected, builder.toString());
    }
}
