package com.github.database.rider.core.api.dataset;

/**
 * Created by rafael-pestano on 22/07/2015.
 */

import com.github.database.rider.core.api.configuration.Orthography;
import com.github.database.rider.core.configuration.DBUnitConfig;
import org.dbunit.dataset.*;
import org.yaml.snakeyaml.Yaml;

import java.io.InputStream;
import java.util.*;

import static com.github.database.rider.core.dataset.builder.BuilderUtil.resolveColumnDataType;

public class YamlDataSet implements IDataSet {

    private Map<String, MyTable> tables = new HashMap<String, MyTable>();

    private DBUnitConfig dbUnitConfig;

    public YamlDataSet(InputStream source) {
        this(source, null);

    }

    public YamlDataSet(InputStream source, DBUnitConfig dbUnitConfig) {
        if (dbUnitConfig != null) {
            this.dbUnitConfig = dbUnitConfig;
        }
        @SuppressWarnings("unchecked")
        Map<String, List<Map<String, Object>>> data = (Map<String, List<Map<String, Object>>>) new Yaml().load(source);
        if (data != null) {
            for (Map.Entry<String, List<Map<String, Object>>> ent : data.entrySet()) {
                String tableName = ent.getKey();
                List<Map<String, Object>> rows = ent.getValue();
                createTable(tableName, rows);
            }
        }
    }

    class MyTable implements ITable {
        String name;

        List<Map<String, Object>> data;

        ITableMetaData meta;

        MyTable(String name, List<Map<String, Object>> rows) {
            this.name = name;
            this.data = new ArrayList<>();
            meta = createMeta(name, rows);
        }

        ITableMetaData createMeta(String name, List<Map<String, Object>> rows) {
            List<String> columnNames = getColumns(rows);
            Map<String, Object> firstRow = rows == null || rows.isEmpty() ? new HashMap<String, Object>() : rows.get(0);
            applyCase(columnNames);
            Column[] columns = null;
            if (columnNames != null) {
                columns = new Column[columnNames.size()];
                for (int i = 0; i < columnNames.size(); i++)
                    columns[i] = new Column(columnNames.get(i), resolveColumnDataType(firstRow.get(columnNames.get(i))));
            } else {
                columns = new Column[0];
            }
            return new DefaultTableMetaData(name, columns);
        }

        @Override
        public int getRowCount() {
            return data.size();
        }

        @Override
        public ITableMetaData getTableMetaData() {
            return meta;
        }

        @Override
        public Object getValue(int row, String column) throws DataSetException {
            if (data.size() <= row) {
                throw new RowOutOfBoundsException("" + row);
            }
            return data.get(row).get(applyCaseInsensitivity(column)); // issue #37
        }

        public void addRow(Map<String, Object> values) {
            data.add(convertMap(values));
        }

        Map<String, Object> convertMap(Map<String, Object> values) {
            Map<String, Object> row = new HashMap<>();
            for (Map.Entry<String, Object> ent : values.entrySet()) {
                row.put(applyCaseInsensitivity(ent.getKey()), ent.getValue()); // issue #37
            }
            return row;
        }
    }

    MyTable createTable(String name, List<Map<String, Object>> rows) {
        MyTable table = new MyTable(applyCaseInsensitivity(name), rows); // issue #37
        if (rows != null) {
            for (Map<String, Object> values : rows)
                table.addRow(values);
        }
        tables.put(name.toUpperCase(), table);
        return table;
    }

    public List<String> getColumns(List<Map<String, Object>> rows) {
        if (rows != null) {
            Set<String> columns = new HashSet<>();
            for (Map<String, Object> row : rows) {
                columns.addAll(applyCase(new ArrayList<>(row.keySet()))); // issue #37
            }

            return new ArrayList<>(columns);
        }
        return null;
    }

    @Override
    public ITable getTable(String tableName) throws DataSetException {
        return tables.get(tableName.toUpperCase());
    }

    @Override
    public ITableMetaData getTableMetaData(final String tableName) throws DataSetException {
        MyTable myTable = tables.get(tableName.toUpperCase());
        if (myTable != null) {
            return myTable.getTableMetaData();
        }
        return null;
    }

    @Override
    public String[] getTableNames() throws DataSetException {
        ITable[] tables = getTables();
        String[] tableNames = new String[tables.length];

        for (int i = 0; i < tables.length; i++) {
            tableNames[i] = tables[i].getTableMetaData().getTableName();
        }

        return tableNames;
    }

    @Override
    public ITable[] getTables() throws DataSetException {
        return tables.values().toArray(new ITable[tables.size()]);
    }

    @Override
    public ITableIterator iterator() throws DataSetException {
        return new DefaultTableIterator(getTables());
    }

    @Override
    public ITableIterator reverseIterator() throws DataSetException {
        return new DefaultTableIterator(getTables(), true);
    }

    @Override
    public boolean isCaseSensitiveTableNames() {
        // is a Boolean object for sure, add null-safety
        Boolean result = dbUnitConfig != null ? (Boolean)dbUnitConfig.getProperties().get("caseSensitiveTableNames") : Boolean.FALSE;
        return Boolean.TRUE.equals(result);
    }

    public boolean isCaseInsensitiveStrategyLowerCase() {

        return dbUnitConfig != null && Orthography.LOWERCASE.equals(dbUnitConfig.getCaseInsensitiveStrategy());
    }

    /**
     * Applies the case-insensitive strategy (orthography) to the given <code>name</code>, if
     * {@link #isCaseSensitiveTableNames()} is <code>false</code>.
     * 
     * @param name The identifier name
     * @return The adjusted identifier name
     * @see Orthography
     */
    String applyCaseInsensitivity(String name) {
        return (name != null)
                ? isCaseSensitiveTableNames() ? name : isCaseInsensitiveStrategyLowerCase()
                        ? name.toLowerCase(Locale.ENGLISH)
                        : name.toUpperCase(Locale.ENGLISH)
                : null;
    }

    private List<String> applyCase(List<String> names) {
        if (names != null) {
            for (int i = 0; i < names.size(); i++) {
                names.set(i, applyCaseInsensitivity(names.get(i)));
            }
        }
        return names;
    }
}

@@ -1484,32 +1484,26 @@
 ist%3C
-Map%3C
 String
-, Object%3E%3E row
+%3E columnName
 s) %7B
@@ -1571,16 +1571,35 @@
 rayList%3C
+Map%3CString, Object%3E
 %3E();%0A   
@@ -1627,27 +1627,34 @@
 eMeta(name, 
-row
+columnName
 s);%0A        
@@ -1712,250 +1712,30 @@
 ist%3C
-Map%3CString, Object%3E%3E rows) %7B%0A            List%3CString%3E columnNames = getColumns(rows);%0A            Map%3CString, Object%3E firstRow = rows == null %7C%7C rows.isEmpty() ? new HashMap%3CString, Object%3E() : rows.get(0);%0A            applyCase(columnNames);
+String%3E columnNames) %7B
 %0A   
@@ -1994,63 +1994,24 @@
 i), 
-resolveColumnDataType(firstRow.get(columnNames.get(i)))
+DataType.UNKNOWN
 );%0A 
@@ -3251,21 +3251,44 @@
 (name), 
+applyCase(getColumns(
 rows)
+))
 ; // iss

Revert "perf"