/*
 * Copyright Debezium Authors.
 *
 * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
 */
package io.debezium.util;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.List;
import java.util.Map;

import org.junit.Test;

import static org.fest.assertions.Assertions.assertThat;

/**
 * @author Randall Hauch
 * @author Horia Chiorean
 */
public class StringsTest {

    public void compareSeparatedLines( Object... lines ) {
        ByteArrayOutputStream content = new ByteArrayOutputStream();
        PrintStream stream = new PrintStream(content);
        for (Object line : lines) {
            stream.println(line);
        }
        List<String> actualLines = Strings.splitLines(content.toString());
        assertArrayEquals(lines, actualLines.toArray());
    }

    @Test
    public void splitLinesShouldWorkCorrectly() {
        compareSeparatedLines("Line 1", "Line 2", "Line 3", "Line 4");
    }

    @Test
    public void setLengthShouldTruncateStringsThatAreTooLong() {
        assertEquals("This is the st", Strings.setLength("This is the string", 14, ' '));
    }

    @Test
    public void setLengthShouldAppendCharacterForStringsThatAreTooShort() {
        assertEquals("This      ", Strings.setLength("This", 10, ' '));
    }

    @Test
    public void setLengthShouldNotRemoveLeadingWhitespace() {
        assertEquals(" This     ", Strings.setLength(" This", 10, ' '));
        assertEquals("\tThis     ", Strings.setLength("\tThis", 10, ' '));
    }

    @Test
    public void setLengthShouldAppendCharacterForEmptyStrings() {
        assertEquals("          ", Strings.setLength("", 10, ' '));
    }

    @Test
    public void setLengthShouldAppendCharacterForNullStrings() {
        assertEquals("          ", Strings.setLength(null, 10, ' '));
    }

    @Test
    public void setLengthShouldReturnStringsThatAreTheDesiredLength() {
        assertEquals("This is the string", Strings.setLength("This is the string", 18, ' '));
    }

    @Test
    public void justifyLeftShouldTruncateStringsThatAreTooLong() {
        assertEquals("This is the st", Strings.justifyLeft("This is the string", 14, ' '));
    }

    @Test
    public void justifyLeftShouldAppendCharacterForStringsThatAreTooShort() {
        assertEquals("This      ", Strings.justifyLeft("This", 10, ' '));
    }

    @Test
    public void justifyLeftShouldRemoveLeadingWhitespace() {
        assertEquals("This      ", Strings.justifyLeft(" This", 10, ' '));
        assertEquals("This      ", Strings.justifyLeft("\tThis", 10, ' '));
    }

    @Test
    public void justifyLeftShouldAppendCharacterForEmptyStrings() {
        assertEquals("          ", Strings.justifyLeft("", 10, ' '));
    }

    @Test
    public void justifyLeftShouldAppendCharacterForNullStrings() {
        assertEquals("          ", Strings.justifyLeft(null, 10, ' '));
    }

    @Test
    public void justifyLeftShouldReturnStringsThatAreTheDesiredLength() {
        assertEquals("This is the string", Strings.justifyLeft("This is the string", 18, ' '));
    }

    @Test
    public void justifyRightShouldTruncateStringsThatAreTooLong() {
        assertEquals(" is the string", Strings.justifyRight("This is the string", 14, ' '));
    }

    @Test
    public void justifyRightShouldPrependCharacterForStringsThatAreTooShort() {
        assertEquals("      This", Strings.justifyRight("This", 10, ' '));
    }

    @Test
    public void justifyRightShouldPrependCharacterForEmptyStrings() {
        assertEquals("          ", Strings.justifyRight("", 10, ' '));
    }

    @Test
    public void justifyRightShouldPrependCharacterForNullStrings() {
        assertEquals("          ", Strings.justifyRight(null, 10, ' '));
    }

    @Test
    public void justifyRightShouldReturnStringsThatAreTheDesiredLength() {
        assertEquals("This is the string", Strings.justifyRight("This is the string", 18, ' '));
    }

    @Test
    public void justifyCenterShouldTruncateStringsThatAreTooLong() {
        assertEquals("This is the st", Strings.justifyCenter("This is the string", 14, ' '));
    }

    @Test
    public void justifyCenterShouldPrependAndAppendSameNumberOfCharacterForStringsThatAreTooShortButOfAnEvenLength() {
        assertEquals("   This   ", Strings.justifyCenter("This", 10, ' '));
    }

    @Test
    public void justifyCenterShouldPrependOneMoreCharacterThanAppendingForStringsThatAreTooShortButOfAnOddLength() {
        assertEquals("   Thing  ", Strings.justifyCenter("Thing", 10, ' '));
    }

    @Test
    public void justifyCenterShouldPrependCharacterForEmptyStrings() {
        assertEquals("          ", Strings.justifyCenter("", 10, ' '));
    }

    @Test
    public void justifyCenterShouldPrependCharacterForNullStrings() {
        assertEquals("          ", Strings.justifyCenter(null, 10, ' '));
    }

    @Test
    public void justifyCenterShouldReturnStringsThatAreTheDesiredLength() {
        assertEquals("This is the string", Strings.justifyCenter("This is the string", 18, ' '));
    }
    
    @Test
    public void replaceVariablesShouldReplaceVariableThatHaveSameCase() {
        assertReplacement("some ${v1} text",vars("v1", "abc"), "some abc text");
        assertReplacement("some ${v1} text",vars("v1", "abc", "V1", "ABC"), "some abc text");
        assertReplacement("some ${v1:other} text",vars("V1", "ABC"), "some other text");
    }
    
    @Test
    public void replaceVariablesShouldNotReplaceVariableThatHasNoDefaultAndIsNotFound() {
        assertReplacement("some ${varName} text",vars("v1", "value1"), "some ${varName} text");
        assertReplacement("some${varName}text",vars("v1", "value1"), "some${varName}text");
        assertReplacement("${varName}",vars("v1", "value1"), "${varName}");
    }
    
    @Test
    public void replaceVariablesShouldReplaceVariablesWithNoDefault() {
        assertReplacement("${varName}",vars("varName", "replaced"), "replaced");
        assertReplacement("some${varName}text",vars("varName", "replaced"), "somereplacedtext");
        assertReplacement("some ${varName} text",vars("varName", "replaced"), "some replaced text");
        assertReplacement("some ${var1,var2,var3:other} text",vars("var1", "replaced"), "some replaced text");
        assertReplacement("some ${var1,var2,var3:other} text",vars("v1", "replaced", "var2", "new"), "some new text");
        assertReplacement("some ${var1,var2,var3:other} text",vars("v1", "replaced", "var3", "new"), "some new text");
    }
    
    @Test
    public void replaceVariablesShouldReplaceVariablesWithDefaultWhenNoReplacementIsFound() {
        assertReplacement("some${varName:other}text",vars("v1", "replaced"), "someothertext");
        assertReplacement("some ${varName:other} text",vars("v1", "replaced"), "some other text");
        assertReplacement("some ${var1,var2,var3:other} text",vars("var10", "replaced"), "some other text");
        assertReplacement("some ${var1,var2,var3:other} text",vars("var10", "replaced", "var11", "new"), "some other text");
    }
    
    @Test
    public void replaceVariablesShouldReplaceMultipleVariables() {
        assertReplacement("${v1}${v1}",vars("v1", "first", "v2", "second"), "firstfirst");
        assertReplacement("${v1}${v2}",vars("v1", "first", "v2", "second"), "firstsecond");
        assertReplacement("some${v1}text${v2}end",vars("v1", "first", "v2", "second"), "somefirsttextsecondend");
        assertReplacement("some ${v1} text ${v2} end",vars("v1", "first", "v2", "second"), "some first text second end");
        assertReplacement("some ${v1:other} text",vars("vx1", "replaced"), "some other text");
        assertReplacement("some ${v1,v2,v3:other} text ${v1,v2,v3:other}",vars("var10", "replaced", "v2", "s"), "some s text s");
        assertReplacement("some ${v1,v2:other}${v2,v3:other} text",vars("v1", "1", "v2", "2"), "some 12 text");
    }
    
    protected void assertReplacement(String before, Map<String, String> replacements, String after) {
        String result = Strings.replaceVariables(before, replacements::get);
        assertThat(result).isEqualTo(after);
    }
    
    protected Map<String, String> vars(String var1, String val1) {
        return Collect.hashMapOf(var1, val1);
    }
    
    protected Map<String, String> vars(String var1, String val1, String var2, String val2) {
        return Collect.hashMapOf(var1, val1, var2, val2);
    }
}