# gobject_signal.py
#
# Copyright 2022 James Westman <james@jwestman.net>
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# This file 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: LGPL-3.0-or-later


from .gtkbuilder_template import Template
from .common import *


class Signal(AstNode):
    grammar = Statement(
        UseIdent("name"),
        Optional([
            "::",
            UseIdent("detail_name").expected("a signal detail name"),
        ]),
        "=>",
        UseIdent("handler").expected("the name of a function to handle the signal"),
        Match("(").expected("argument list"),
        Optional(UseIdent("object")).expected("object identifier"),
        Match(")").expected(),
        ZeroOrMore(AnyOf(
            [Keyword("swapped"), UseLiteral("swapped", True)],
            [Keyword("after"), UseLiteral("after", True)],
        )),
    )


    @property
    def gir_signal(self):
        if self.gir_class is not None:
            return self.gir_class.signals.get(self.tokens["name"])


    @property
    def gir_class(self):
        return self.parent.parent.gir_class


    @validate("name")
    def signal_exists(self):
        if self.gir_class is None:
            # Objects that we have no gir data on should not be validated
            # This happens for classes defined by the app itself
            return

        if isinstance(self.parent.parent, Template):
            # If the signal is part of a template, it might be defined by
            # the application and thus not in gir
            return

        if self.gir_signal is None:
            raise CompileError(
                f"Class {self.gir_class.full_name} does not contain a signal called {self.tokens['name']}",
                did_you_mean=(self.tokens["name"], self.gir_class.signals.keys())
            )


    @validate("object")
    def object_exists(self):
        object_id = self.tokens["object"]
        if object_id is None:
            return

        if self.root.objects_by_id.get(object_id) is None:
            raise CompileError(
                f"Could not find object with ID '{object_id}'"
            )


    @docs("name")
    def signal_docs(self):
        if self.gir_signal is not None:
            return self.gir_signal.doc


    def emit_xml(self, xml: XmlEmitter):
        name = self.tokens["name"]
        if self.tokens["detail_name"]:
            name += "::" + self.tokens["detail_name"]
        xml.put_self_closing(
            "signal",
            name=name,
            handler=self.tokens["handler"],
            swapped="true" if self.tokens["swapped"] else None,
            object=self.tokens["object"]
        )


@decompiler("signal")
def decompile_signal(ctx, gir, name, handler, swapped="false", object=None):
    object_name = object or ""
    name = name.replace("_", "-")
    if decompile.truthy(swapped):
        ctx.print(f"{name} => {handler}({object_name}) swapped;")
    else:
        ctx.print(f"{name} => {handler}({object_name});")
    return gir
