/*
 * SLD Editor - The Open Source Java SLD Editor
 *
 * Copyright (C) 2016, SCISYS UK Limited
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.sldeditor.test.unit.ui.detail.config;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.sldeditor.common.xml.ui.FieldIdEnum;
import com.sldeditor.ui.detail.GraphicPanelFieldManager;
import com.sldeditor.ui.detail.config.FieldConfigBoolean;
import com.sldeditor.ui.detail.config.FieldConfigColour;
import com.sldeditor.ui.detail.config.FieldConfigCommonData;
import com.sldeditor.ui.detail.config.FieldConfigDouble;
import com.sldeditor.ui.detail.config.FieldConfigEnum;
import com.sldeditor.ui.detail.config.FieldConfigInteger;
import com.sldeditor.ui.detail.config.FieldConfigPopulation;
import com.sldeditor.ui.detail.config.FieldConfigString;
import com.sldeditor.ui.detail.config.colourmap.FieldConfigColourMap;
import com.sldeditor.ui.detail.config.font.FieldConfigFont;
import com.sldeditor.ui.detail.config.symboltype.SymbolTypeConfig;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import org.geotools.filter.LiteralExpressionImpl;
import org.geotools.styling.ColorMap;
import org.geotools.styling.ColorMapEntry;
import org.geotools.styling.ColorMapEntryImpl;
import org.geotools.styling.ColorMapImpl;
import org.geotools.styling.FeatureTypeConstraint;
import org.geotools.styling.Font;
import org.geotools.styling.StyleBuilder;
import org.geotools.styling.UserLayer;
import org.junit.jupiter.api.Test;
import org.locationtech.jts.geom.Geometry;
import org.opengis.filter.expression.Expression;

/**
 * The unit test for FieldConfigPopulation.
 *
 * <p>{@link com.sldeditor.ui.detail.config.FieldConfigPopulation}
 *
 * @author Robert Ward (SCISYS)
 */
public class FieldConfigPopulationTest {

    /**
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#FieldConfigPopulation(com.sldeditor.ui.detail.GraphicPanelFieldManager)}.
     */
    @Test
    public void testFieldConfigPopulation() {
        FieldIdEnum fieldId = FieldIdEnum.UNKNOWN;
        FieldConfigPopulation obj = new FieldConfigPopulation(null);
        obj.populateBooleanField(fieldId, Boolean.TRUE);
        obj.populateComboBoxField(fieldId, "");
        obj.populateColourField(fieldId, null);
        obj.populateColourMapField(FieldIdEnum.ANCHOR_POINT_V, (ColorMap) null);
        obj.populateFontField(FieldIdEnum.ANCHOR_POINT_V, (Font) null);
        obj.populateTextField(fieldId, (String) null);
        obj.populateDoubleField(fieldId, (Double) null);
        obj.populateIntegerField(fieldId, (Integer) null);
        obj.populateField(fieldId, (Expression) null);
        obj.populateUserLayer(fieldId, (UserLayer) null);
        obj.populateFieldTypeConstraint(fieldId, (List<FeatureTypeConstraint>) null);

        assertNull(obj.getExpression(fieldId));
        assertFalse(obj.getBoolean(fieldId));
        assertEquals(0, obj.getInteger(fieldId));
        assertTrue(Math.abs(obj.getDouble(fieldId) - 0.0) < 0.001);
        assertTrue(obj.getText(fieldId).compareTo("") == 0);
        assertNull(obj.getComboBox(fieldId));
        assertNull(obj.getColourMap(fieldId));
        assertNull(obj.getFieldConfig(fieldId));
        assertNull(obj.getFeatureTypeConstraint(fieldId));
    }

    /**
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#populateBooleanField(com.sldeditor.ui.detail.config.FieldId,
     * java.lang.Boolean)}. Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#populateBooleanField(com.sldeditor.common.xml.ui.FieldIdEnum,
     * java.lang.Boolean)}. Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#getBoolean(com.sldeditor.ui.detail.config.FieldId)}.
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#getBoolean(com.sldeditor.common.xml.ui.FieldIdEnum)}.
     */
    @Test
    public void testBoolean() {

        FieldIdEnum fieldId = FieldIdEnum.DESCRIPTION;

        GraphicPanelFieldManager fieldConfigManager = new GraphicPanelFieldManager(String.class);

        FieldConfigBoolean booleanField =
                new FieldConfigBoolean(
                        new FieldConfigCommonData(Geometry.class, fieldId, "label", true, false));
        booleanField.createUI();
        fieldConfigManager.add(fieldId, booleanField);

        FieldConfigPopulation obj = new FieldConfigPopulation(fieldConfigManager);

        Boolean expectedValue = Boolean.TRUE;
        obj.populateBooleanField(fieldId, expectedValue);
        assertEquals(expectedValue.booleanValue(), obj.getBoolean(fieldId));

        // This shouldn't work as it does not know about the field
        FieldIdEnum wrongFieldEnum = FieldIdEnum.ELSE_FILTER;
        assertFalse(obj.getBoolean(wrongFieldEnum));

        // Try with null - should revert to default value (false)
        obj.populateBooleanField(fieldId, null);
        assertFalse(obj.getBoolean(fieldId));
    }

    /**
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#populateComboBoxField(com.sldeditor.ui.detail.config.FieldId,
     * java.lang.String)}. Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#populateComboBoxField(com.sldeditor.common.xml.ui.FieldIdEnum,
     * java.lang.String)}. Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#getComboBox(com.sldeditor.common.xml.ui.FieldIdEnum)}.
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#getComboBox(com.sldeditor.ui.detail.config.FieldId)}.
     */
    @Test
    public void testComboBox() {
        SymbolTypeConfig s1 = new SymbolTypeConfig(null);
        s1.addOption("key1", "Value 1");
        s1.addOption("key2", "Value 2");
        s1.addOption("key3", "Value 3");
        s1.addField(FieldIdEnum.ANCHOR_POINT_H, true);
        s1.addField(FieldIdEnum.ANCHOR_POINT_V, false);

        FieldIdEnum fieldId = FieldIdEnum.DESCRIPTION;

        List<SymbolTypeConfig> configList = new ArrayList<SymbolTypeConfig>();
        configList.add(s1);

        FieldConfigEnum enumField =
                new FieldConfigEnum(
                        new FieldConfigCommonData(Geometry.class, fieldId, "label", true, false));
        enumField.addConfig(configList);
        enumField.createUI();
        GraphicPanelFieldManager fieldConfigManager = new GraphicPanelFieldManager(String.class);
        fieldConfigManager.add(fieldId, enumField);

        FieldConfigPopulation obj = new FieldConfigPopulation(fieldConfigManager);

        String expectedValue = "key2";
        obj.populateComboBoxField(fieldId, expectedValue);
        assertTrue(expectedValue.compareTo(obj.getComboBox(fieldId).getKey()) == 0);

        // This shouldn't work as it does not know about the field
        FieldIdEnum wrongFieldEnum = FieldIdEnum.ELSE_FILTER;
        assertNull(obj.getComboBox(wrongFieldEnum));

        // Try with null - should revert to default value (first enum item)
        obj.populateComboBoxField(fieldId, null);
        expectedValue = "key1";
        assertTrue(expectedValue.compareTo(obj.getComboBox(fieldId).getKey()) == 0);
    }

    /**
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#populateColourField(com.sldeditor.ui.detail.config.FieldId,
     * org.opengis.filter.expression.Expression, org.opengis.filter.expression.Expression)}.
     */
    @Test
    public void testColour() {

        FieldIdEnum fieldId = FieldIdEnum.DESCRIPTION;

        GraphicPanelFieldManager fieldConfigManager = new GraphicPanelFieldManager(String.class);

        FieldConfigColour colourField =
                new FieldConfigColour(
                        new FieldConfigCommonData(Geometry.class, fieldId, "label", true, false));
        colourField.createUI();
        colourField.createUI();
        fieldConfigManager.add(fieldId, colourField);

        FieldConfigPopulation obj = new FieldConfigPopulation(fieldConfigManager);

        StyleBuilder styleBuilder = new StyleBuilder();
        Expression colour = styleBuilder.colorExpression(Color.red);

        obj.populateColourField(fieldId, colour);
        FieldIdEnum wrongFieldEnum = FieldIdEnum.ELSE_FILTER;
        obj.populateColourField(wrongFieldEnum, colour);
    }

    /**
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#populateColourMapField(com.sldeditor.common.xml.ui.FieldIdEnum,
     * org.geotools.styling.ColorMap)}. Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#getColourMap(com.sldeditor.common.xml.ui.FieldIdEnum)}.
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#getColourMap(com.sldeditor.ui.detail.config.FieldId)}.
     */
    @Test
    public void testColourMap() {

        FieldIdEnum fieldId = FieldIdEnum.DESCRIPTION;

        GraphicPanelFieldManager fieldConfigManager = new GraphicPanelFieldManager(String.class);

        FieldConfigColourMap colourMapField =
                new FieldConfigColourMap(
                        new FieldConfigCommonData(Geometry.class, fieldId, "label", true, false));
        colourMapField.createUI();
        fieldConfigManager.add(fieldId, colourMapField);

        ColorMap expectedValue = new ColorMapImpl();
        ColorMapEntry entry = new ColorMapEntryImpl();
        StyleBuilder styleBuilder = new StyleBuilder();

        entry.setColor(styleBuilder.colorExpression(Color.PINK));
        entry.setQuantity(styleBuilder.literalExpression(2.3));
        expectedValue.addColorMapEntry(entry);
        FieldConfigPopulation obj = new FieldConfigPopulation(fieldConfigManager);
        obj.populateColourMapField(fieldId, expectedValue);
        assertEquals(
                expectedValue.getColorMapEntries().length,
                obj.getColourMap(fieldId).getColorMapEntries().length);

        // This shouldn't work as it does not know about the field
        FieldIdEnum wrongFieldEnum = FieldIdEnum.ELSE_FILTER;
        obj.populateColourMapField(wrongFieldEnum, expectedValue);
        assertNull(obj.getColourMap(wrongFieldEnum));
    }

    /**
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#populateFontField(com.sldeditor.common.xml.ui.FieldIdEnum,
     * org.geotools.styling.Font)}.
     */
    @Test
    public void testFont() {

        FieldIdEnum fieldId = FieldIdEnum.DESCRIPTION;

        GraphicPanelFieldManager fieldConfigManager = new GraphicPanelFieldManager(String.class);

        FieldConfigFont fontField =
                new FieldConfigFont(
                        new FieldConfigCommonData(Geometry.class, fieldId, "label", true, false));
        fontField.createUI();
        fieldConfigManager.add(fieldId, fontField);

        FieldConfigPopulation obj = new FieldConfigPopulation(fieldConfigManager);

        StyleBuilder styleBuilder = new StyleBuilder();

        Font expectedValue = styleBuilder.createFont(java.awt.Font.decode(null));
        obj.populateFontField(fieldId, expectedValue);

        // This shouldn't work as it does not know about the field
        FieldIdEnum wrongFieldEnum = FieldIdEnum.ELSE_FILTER;
        obj.populateFontField(wrongFieldEnum, expectedValue);
    }

    /**
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#populateTextField(com.sldeditor.ui.detail.config.FieldId,
     * java.lang.String)}. Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#populateTextField(com.sldeditor.common.xml.ui.FieldIdEnum,
     * java.lang.String)}. Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#getText(com.sldeditor.ui.detail.config.FieldId)}.
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#getText(com.sldeditor.common.xml.ui.FieldIdEnum)}.
     */
    @Test
    public void testString() {

        FieldIdEnum fieldId = FieldIdEnum.DESCRIPTION;

        GraphicPanelFieldManager fieldConfigManager = new GraphicPanelFieldManager(String.class);

        FieldConfigString stringField =
                new FieldConfigString(
                        new FieldConfigCommonData(Geometry.class, fieldId, "label", true, false),
                        "button text");
        stringField.createUI();
        fieldConfigManager.add(fieldId, stringField);

        FieldConfigPopulation obj = new FieldConfigPopulation(fieldConfigManager);

        String expectedValue = "test string";
        obj.populateTextField(fieldId, expectedValue);
        assertTrue(expectedValue.compareTo(obj.getText(fieldId)) == 0);

        // This shouldn't work as it does not know about the field
        FieldIdEnum wrongFieldEnum = FieldIdEnum.ELSE_FILTER;
        assertTrue("".compareTo(obj.getText(wrongFieldEnum)) == 0);

        // Try with null
        obj.populateTextField(fieldId, null);
        obj.populateTextField(wrongFieldEnum, expectedValue);
    }

    /**
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#populateDoubleField(com.sldeditor.ui.detail.config.FieldId,
     * java.lang.Double)}. Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#getDouble(com.sldeditor.ui.detail.config.FieldId)}.
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#getDouble(com.sldeditor.common.xml.ui.FieldIdEnum)}.
     */
    @Test
    public void testDouble() {

        FieldIdEnum fieldId = FieldIdEnum.DESCRIPTION;

        GraphicPanelFieldManager fieldConfigManager = new GraphicPanelFieldManager(String.class);

        FieldConfigDouble doubleField =
                new FieldConfigDouble(
                        new FieldConfigCommonData(Geometry.class, fieldId, "label", true, false));
        doubleField.createUI();
        fieldConfigManager.add(fieldId, doubleField);

        FieldConfigPopulation obj = new FieldConfigPopulation(fieldConfigManager);

        double expectedValue = 1.256;
        obj.populateDoubleField(fieldId, expectedValue);
        assertTrue(Math.abs(expectedValue - obj.getDouble(fieldId)) < 0.001);

        // This shouldn't work as it does not know about the field
        FieldIdEnum wrongFieldEnum = FieldIdEnum.ELSE_FILTER;
        assertTrue(Math.abs(obj.getDouble(wrongFieldEnum) - 0.0) < 0.001);

        // Try with null - should revert to default value (0.0)
        obj.populateDoubleField(fieldId, null);
        assertTrue(Math.abs(obj.getDouble(fieldId) - 0.0) < 0.001);
    }

    /**
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#populateIntegerField(com.sldeditor.ui.detail.config.FieldId,
     * java.lang.Integer)}. Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#getInteger(com.sldeditor.ui.detail.config.FieldId)}.
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#getInteger(com.sldeditor.common.xml.ui.FieldIdEnum)}.
     */
    @Test
    public void testInteger() {

        FieldIdEnum fieldId = FieldIdEnum.DESCRIPTION;

        GraphicPanelFieldManager fieldConfigManager = new GraphicPanelFieldManager(String.class);

        FieldConfigInteger intField =
                new FieldConfigInteger(
                        new FieldConfigCommonData(Geometry.class, fieldId, "label", true, false));
        intField.createUI();
        fieldConfigManager.add(fieldId, intField);

        FieldConfigPopulation obj = new FieldConfigPopulation(fieldConfigManager);

        int expectedValue = 1256;
        obj.populateIntegerField(fieldId, expectedValue);
        assertEquals(expectedValue, obj.getInteger(fieldId));

        // This shouldn't work as it does not know about the field
        FieldIdEnum wrongFieldEnum = FieldIdEnum.ELSE_FILTER;
        assertEquals(0, obj.getInteger(wrongFieldEnum));

        // Try with null - should revert to default value (0.0)
        obj.populateIntegerField(fieldId, null);
        assertEquals(0, obj.getInteger(fieldId));
    }

    /**
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#populateField(com.sldeditor.ui.detail.config.FieldId,
     * org.opengis.filter.expression.Expression)}. Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#populateField(com.sldeditor.common.xml.ui.FieldIdEnum,
     * org.opengis.filter.expression.Expression)}. Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#getExpression(com.sldeditor.ui.detail.config.FieldId)}.
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#getExpression(com.sldeditor.common.xml.ui.FieldIdEnum)}.
     */
    @Test
    public void testExpression() {

        FieldIdEnum fieldId = FieldIdEnum.DESCRIPTION;

        GraphicPanelFieldManager fieldConfigManager = new GraphicPanelFieldManager(String.class);

        FieldConfigInteger intField =
                new FieldConfigInteger(
                        new FieldConfigCommonData(Geometry.class, fieldId, "label", true, false));
        intField.createUI();
        fieldConfigManager.add(fieldId, intField);

        FieldConfigPopulation obj = new FieldConfigPopulation(fieldConfigManager);

        int expectedValue = 1256;
        StyleBuilder styleBuilder = new StyleBuilder();

        Expression expression = styleBuilder.literalExpression(expectedValue);
        obj.populateField(fieldId, expression);

        LiteralExpressionImpl actualValue = (LiteralExpressionImpl) obj.getExpression(fieldId);

        assertEquals(expectedValue, ((Integer) actualValue.getValue()).intValue());

        actualValue = (LiteralExpressionImpl) obj.getExpression(fieldId);
        assertEquals(expectedValue, ((Integer) actualValue.getValue()).intValue());

        // This shouldn't work as it does not know about the field
        FieldIdEnum wrongFieldEnum = FieldIdEnum.ELSE_FILTER;
        obj.populateField(wrongFieldEnum, expression);
        assertNull(obj.getExpression(wrongFieldEnum));

        // Try with null
        obj.populateField(fieldId, null);
    }

    /**
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#isTreeDataUpdated()}. Test method for
     * {@link com.sldeditor.ui.detail.config.FieldConfigPopulation#resetTreeDataUpdated()}.
     */
    @Test
    public void testIsTreeDataUpdated() {
        FieldIdEnum fieldId = FieldIdEnum.DESCRIPTION;

        GraphicPanelFieldManager fieldConfigManager = new GraphicPanelFieldManager(String.class);

        FieldConfigString stringField =
                new FieldConfigString(
                        new FieldConfigCommonData(Geometry.class, fieldId, "label", true, false),
                        "button text");
        stringField.createUI();
        fieldConfigManager.add(fieldId, stringField);

        FieldConfigPopulation obj = new FieldConfigPopulation(fieldConfigManager);
        assertFalse(obj.isTreeDataUpdated());

        // Set data
        String expectedValue = "test string";
        obj.populateTextField(fieldId, expectedValue);
        assertTrue(expectedValue.compareTo(obj.getText(fieldId)) == 0);
        assertFalse(obj.isTreeDataUpdated());

        // Simulate a key being pressed
        expectedValue = "test string1";
        stringField.populateField(expectedValue);

        // Data is different so flag should have changed
        assertTrue(expectedValue.compareTo(obj.getText(fieldId)) == 0);
        assertTrue(obj.isTreeDataUpdated());
        obj.resetTreeDataUpdated();
        assertFalse(obj.isTreeDataUpdated());

        // Populate the same data again and the flag should not have changed
        obj.populateTextField(fieldId, expectedValue);
        assertTrue(expectedValue.compareTo(obj.getText(fieldId)) == 0);
        assertFalse(obj.isTreeDataUpdated());
    }

    /**
     * Test method for {@link
     * com.sldeditor.ui.detail.config.FieldConfigPopulation#getFieldConfig(com.sldeditor.ui.detail.config.FieldId)}.
     */
    @Test
    public void testGetFieldConfig() {

        FieldIdEnum fieldId = FieldIdEnum.DESCRIPTION;

        GraphicPanelFieldManager fieldConfigManager = new GraphicPanelFieldManager(String.class);

        FieldConfigInteger intField =
                new FieldConfigInteger(
                        new FieldConfigCommonData(Geometry.class, fieldId, "label", true, false));
        intField.createUI();
        fieldConfigManager.add(fieldId, intField);

        FieldConfigPopulation obj = new FieldConfigPopulation(fieldConfigManager);

        assertNotNull(obj.getFieldConfig(fieldId));
        FieldIdEnum wrongFieldEnum = FieldIdEnum.ELSE_FILTER;
        assertNull(obj.getFieldConfig(wrongFieldEnum));
    }
}
