package io.cucumber.datatable;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import static io.cucumber.datatable.TypeFactory.aListOf;
import static io.cucumber.datatable.TypeFactory.constructType;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;

public class DataTableTypeRegistryTest {

    private static final Type LIST_OF_LIST_OF_PLACE = aListOf(aListOf(Place.class));
    private static final Type LIST_OF_PLACE = aListOf(Place.class);
    private static final Type LIST_OF_LIST_OF_DECIMAL = aListOf(aListOf(BigDecimal.class));
    private static final TableCellByTypeTransformer PLACE_TABLE_CELL_TRANSFORMER = new TableCellByTypeTransformer() {
        @Override
        @SuppressWarnings("unchecked")
        public <T> T transform(String value, Class<T> cellType) {
            return (T) new Place(value);
        }
    };
    private static final TableEntryByTypeTransformer PLACE_TABLE_ENTRY_TRANSFORMER = new TableEntryByTypeTransformer() {
        @Override
        @SuppressWarnings("unchecked")
        public <T> T transform(Map<String, String> entry, Class<T> type, TableCellByTypeTransformer cellTransformer) {
            return (T) new Place(entry.get("name"), Integer.valueOf(entry.get("index of place")));
        }
    };

    @Rule
    public ExpectedException expectedException = ExpectedException.none();

    private final DataTableTypeRegistry registry = new DataTableTypeRegistry(Locale.ENGLISH);


    @Test
    public void throws_duplicate_type_exception() {

        registry.defineDataTableType(new DataTableType(Place.class, new TableTransformer<Place>() {
            @Override
            public Place transform(DataTable table) {
                return new Place(table.cell(0, 0));
            }

        }));
        expectedException.expectMessage("" +
                "There is already a data table type registered for class io.cucumber.datatable.Place.\n" +
                "It registered an TableTransformer. You are trying to add a TableTransformer"
        );
        registry.defineDataTableType(new DataTableType(Place.class, new TableTransformer<Place>() {
            @Override
            public Place transform(DataTable table) {
                return new Place(table.cell(0, 0));
            }
        }));
    }

    @Test
    public void returns_null_data_table_type_if_none_match_and_no_default_registered() {

        registry.defineDataTableType(DataTableType.cell(Place.class));
        registry.defineDataTableType(DataTableType.entry(Place.class));

        DataTableType lookupTableTypeByType = registry.lookupTableTypeByType(constructType(Place.class));

        assertNull(lookupTableTypeByType);
    }

    @Test
    public void returns_null_data_table_type_for_cell_if_no_default_registered() {

        registry.setDefaultDataTableEntryTransformer(PLACE_TABLE_ENTRY_TRANSFORMER);

        DataTableType lookupTableTypeByType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_PLACE);

        assertNull(lookupTableTypeByType);
    }

    @Test
    public void returns_null_data_table_type_for_entry_if_no_default_registered() {

        registry.setDefaultDataTableCellTransformer(PLACE_TABLE_CELL_TRANSFORMER);


        DataTableType lookupTableTypeByType = registry.lookupTableTypeByType(LIST_OF_PLACE);

        assertNull(lookupTableTypeByType);
    }

    @Test
    public void returns_cell_data_table_type() {

        DataTableType cell = DataTableType.cell(Place.class);
        registry.defineDataTableType(cell);
        registry.defineDataTableType(DataTableType.entry(Place.class));

        DataTableType lookupTableTypeByType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_PLACE);

        assertSame(cell, lookupTableTypeByType);
    }

    @Test
    public void returns_entry_data_table_type() {

        registry.defineDataTableType(DataTableType.cell(Place.class));
        DataTableType entry = DataTableType.entry(Place.class);
        registry.defineDataTableType(entry);

        DataTableType lookupTableTypeByType = registry.lookupTableTypeByType(LIST_OF_PLACE);

        assertSame(entry, lookupTableTypeByType);
    }

    @Test
    public void parse_decimal_with_english_locale() {
        parse_decimal_with_provided_locale(registry, "12.45", 12.45);
    }

    @Test
    public void parse_decimal_with_polish_locale() {
        DataTableTypeRegistry plRegistry = new DataTableTypeRegistry(Locale.forLanguageTag("pl"));
        parse_decimal_with_provided_locale(plRegistry, "12,45", 12.45);

    }

    private void parse_decimal_with_provided_locale(DataTableTypeRegistry typeRegistry, String decimalString, double expectedDecimalValue) {
        //noinspection unchecked
        List<List<BigDecimal>> transform = (List<List<BigDecimal>>) typeRegistry.lookupTableTypeByType(LIST_OF_LIST_OF_DECIMAL).transform(Collections.singletonList(Collections.singletonList(decimalString)));

        assertEquals(1,transform.size());
        assertEquals(0,transform.get(0).get(0).compareTo(BigDecimal.valueOf(expectedDecimalValue)));
    }
}

@@ -4470,51 +4470,72 @@
     
-parse_decimal_with_provided_locale(registry
+expectDecimalValueForDecimalStringInLocale(Locale.ENGLISH, 12.45
 , %221
@@ -4539,23 +4539,16 @@
  %2212.45%22
-, 12.45
 );%0A    %7D
@@ -4624,168 +4624,94 @@
     
-DataTableTypeRegistry plRegistry = new DataTableTypeRegistry(Locale.forLanguageTag(%22pl%22));%0A        parse_decimal_with_provided_locale(plRegistry
+expectDecimalValueForDecimalStringInLocale(Locale.forLanguageTag(%22pl%22)
 , 
-%22
 12
-,
+.
 45
-%22
 , 
+%22
 12
-.
+,
 45
+%22
 );%0A%0A
@@ -4738,131 +4738,198 @@
 oid 
-parse_decimal_with_provided_locale(DataTableTypeRegistry typeRegistry, String decimalString, double expectedDecimalV
+expectDecimalValueForDecimalStringInLocale(Locale locale, double expectedDecimalValue, String decimalString) %7B%0A        DataTableTypeRegistry typeRegistry = new DataTableTypeRegistry(loc
 al
-u
 e)
- %7B
+;
 %0A   

change method parse_decimal_with_provided_locale to expectDecimalValueForDecimalStringInLocale