/*
 * Copyright (c) 2010-2025 Contributors to the openHAB project
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 */
/*
 * generated by Xtext
 */
package org.openhab.core.model.sitemap.validation

import org.openhab.core.model.sitemap.sitemap.Button
import org.openhab.core.model.sitemap.sitemap.Buttongrid
import org.openhab.core.model.sitemap.sitemap.Colortemperaturepicker
import org.openhab.core.model.sitemap.sitemap.Frame
import org.openhab.core.model.sitemap.sitemap.LinkableWidget
import org.openhab.core.model.sitemap.sitemap.Setpoint
import org.openhab.core.model.sitemap.sitemap.Sitemap
import org.openhab.core.model.sitemap.sitemap.SitemapPackage
import org.openhab.core.model.sitemap.sitemap.Widget
import org.eclipse.xtext.validation.Check
import java.math.BigDecimal
import org.openhab.core.model.sitemap.sitemap.Input
import org.eclipse.xtext.nodemodel.util.NodeModelUtils

//import org.eclipse.xtext.validation.Check
/**
 * Custom validation rules. 
 * 
 * see http://www.eclipse.org/Xtext/documentation.html#validation
 */
class SitemapValidator extends AbstractSitemapValidator {

    val ALLOWED_HINTS = #["text", "number", "date", "time", "datetime"]
    
    @Check
    def void checkFramesInFrame(Frame frame) {
        for (Widget w : frame.children) {
            if (w instanceof Frame) {
                error("Frames must not contain other frames",
                    SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.FRAME__CHILDREN));
                return;
            }
            if (w instanceof Button) {
                error("Frames should not contain Button, Button is allowed only in Buttongrid",
                    SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.FRAME__CHILDREN));
                return;
            }
        }
    }

    @Check
    def void checkFramesInWidgetList(Sitemap sitemap) {
        var containsFrames = false
        var containsOtherWidgets = false

        for (Widget w : sitemap.children) {
            if (w instanceof Button) {
                error("Sitemap should not contain Button, Button is allowed only in Buttongrid",
                    SitemapPackage.Literals.SITEMAP.getEStructuralFeature(SitemapPackage.SITEMAP__NAME));
                return;
            }
            if (w instanceof Frame) {
                containsFrames = true
            } else {
                containsOtherWidgets = true
            }
            if (containsFrames && containsOtherWidgets) {
                error("Sitemap should contain either only frames or none at all",
                    SitemapPackage.Literals.SITEMAP.getEStructuralFeature(SitemapPackage.SITEMAP__NAME));
                return
            }
        }
    }

    @Check
    def void checkFramesInWidgetList(LinkableWidget widget) {
        if (widget instanceof Frame) {
            // we have a dedicated check for frames in place
            return;
        }
        if (widget instanceof Buttongrid) {
            // we have a dedicated check for Buttongrid in place
            return;
        }
        var containsFrames = false
        var containsOtherWidgets = false
        for (Widget w : widget.children) {
            if (w instanceof Button) {
                error("Linkable widget should not contain Button, Button is allowed only in Buttongrid",
                    SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.LINKABLE_WIDGET__CHILDREN));
                return;
            }
            if (w instanceof Frame) {
                containsFrames = true
            } else {
                containsOtherWidgets = true
            }
            if (containsFrames && containsOtherWidgets) {
                error("Linkable widget should contain either only frames or none at all",
                    SitemapPackage.Literals.FRAME.getEStructuralFeature(SitemapPackage.LINKABLE_WIDGET__CHILDREN));
                return
            }
        }
    }

    @Check
    def void checkWidgetsInButtongrid(Buttongrid grid) {
        val nb = grid.getButtons.size()
        if (nb > 0 && grid.item === null) {
            error("To use the \"buttons\" parameter in a Buttongrid, the \"item\" parameter is required",
                SitemapPackage.Literals.BUTTONGRID.getEStructuralFeature(SitemapPackage.BUTTONGRID__ITEM));
        }
        for (Widget w : grid.children) {
            if (!(w instanceof Button)) {
                error("Buttongrid must contain only Button",
                    SitemapPackage.Literals.BUTTONGRID.getEStructuralFeature(SitemapPackage.BUTTONGRID__CHILDREN));
                return;
            }
        }
    }

    @Check
    def void checkSetpoints(Setpoint sp) {
        if (BigDecimal.ZERO == sp.step) {
            error("Setpoint on item '" + sp.item + "' has step size of 0",
                SitemapPackage.Literals.SETPOINT.getEStructuralFeature(SitemapPackage.SETPOINT__STEP));
        }

        if (sp.step !== null && sp.step < BigDecimal.ZERO) {
            error("Setpoint on item '" + sp.item + "' has negative step size",
                SitemapPackage.Literals.SETPOINT.getEStructuralFeature(SitemapPackage.SETPOINT__STEP));
        }

        if (sp.minValue !== null && sp.maxValue !== null && sp.minValue > sp.maxValue) {
            error("Setpoint on item '" + sp.item + "' has larger minValue than maxValue",
                SitemapPackage.Literals.SETPOINT.getEStructuralFeature(SitemapPackage.SETPOINT__MIN_VALUE));
        }
    }

    @Check
    def void checkColortemperaturepicker(Colortemperaturepicker ctp) {
        if (ctp.minValue !== null && ctp.maxValue !== null && ctp.minValue > ctp.maxValue) {
            error("Colortemperaturepicker on item '" + ctp.item + "' has larger minValue than maxValue",
                SitemapPackage.Literals.COLORTEMPERATUREPICKER.getEStructuralFeature(SitemapPackage.COLORTEMPERATUREPICKER__MIN_VALUE));
        }
    }

    @Check
    def void checkInputHintParameter(Input i) {
        if (i.inputHint !== null && !ALLOWED_HINTS.contains(i.inputHint)) {
            val node = NodeModelUtils.getNode(i)
            val line = node.getStartLine()
            error("Input on item '" + i.item + "' has invalid inputHint '" + i.inputHint + "' at line " + line,
                SitemapPackage.Literals.INPUT.getEStructuralFeature(SitemapPackage.INPUT__INPUT_HINT))
        }
    }
}
