# Copyright 2015 Sean Vig
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from pywayland.client.proxy import ProxyMeta
from pywayland import ffi

from .message import Message

import weakref

weakkeydict = weakref.WeakKeyDictionary()


class Interface(object):
    """Wrapper class for wl_wayland structs

    Base class for interfaces that are defined by the wayland.xml class and
    generated by the scanner.  Sub-classes should use the InterfaceMeta
    metaclass, which will define subcls.events and subcls.requests, the lists
    of the methods on this interface.  These class variables are populated by
    the event and request decorators.
    """

    @classmethod
    def event(cls, signature, types):
        """Decorator for interface events

        Adds the decorated method to the list of events of the interface
        (server-side method).

        Parameters
        ----------

        signature : string
            Encodes the types of the arguments to the decorated function.

        types : list
            List of the types of any objects included in the argument list,
            None if otherwise.

        """
        def wrapper(func):
            cls.events.append(Message(func, signature, types))
            return func
        return wrapper

    @classmethod
    def request(cls, signature, types):
        """Decorator for interface requests

        Adds the decorated method to the list of requests of the interface
        (client-side method).

        Parameters
        ----------

        signature : string
            Encodes the types of the arguments to the decorated function.

        types : list
            List of the types of any objects included in the argument list,
            None if otherwise.

        """
        def wrapper(func):
            cls.requests.append(Message(func, signature, types))
            return func
        return wrapper

    @classmethod
    def proxy_class(interface, proxy_ptr):
        """Return a proxy class for the given interface and wl_proxy pointer"""
        new_class = ProxyMeta(
            interface.name, (), {'_interface': interface, '_ptr': proxy_ptr}
        )
        return new_class()

    @classmethod
    def _gen_c(cls):
        """Creates the wl_interface C struct

        Generates the CFFI cdata for the wl_interface struct given by the
        interface.

        """
        # Initialize the interface cdata
        cls._ptr.name = name = ffi.new('char[]', cls.name.encode())
        cls._ptr.version = cls.version

        # Determine the number of methods to assign and assign them
        cls._ptr.method_count = len(cls.requests)
        cls._ptr.methods = methods_ptr = ffi.new("struct wl_message[]", len(cls.requests))
        # Iterate over the methods
        for i, message in enumerate(cls.requests):
            # First, generate the wl_message cdata
            msg_buf = ffi.buffer(message._ptr)
            methods_buf = ffi.buffer(methods_ptr + i)
            # Copy the contents of the cdata into the allocated cdata
            methods_buf[:] = msg_buf

        cls._ptr.event_count = len(cls.events)
        cls._ptr.events = events_ptr = ffi.new("struct wl_message[]", len(cls.events))
        # Iterate over the methods
        for i, message in enumerate(cls.events):
            # First, generate the wl_message cdata
            msg_buf = ffi.buffer(message._ptr)
            events_buf = ffi.buffer(events_ptr + i)
            # Copy the contents of the cdata into the allocated cdata
            events_buf[:] = msg_buf

        weakkeydict[cls._ptr] = (name, methods_ptr, events_ptr)


class InterfaceMeta(type):
    def __init__(self, name, bases, dct):
        """Metaclass for Interfaces

        Initializes empty lists for events and requests for the given class.
        Also allocates a cdata struct for the Interface.
        """
        self._ptr = ffi.new("struct wl_interface *")
        self.events = []
        self.requests = []
