# -*- coding: utf-8 -*-
#
#*******************************************************************************
#
#  Copyright 2022 RIEGL Laser Measurement Systems
#
#  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.
#
#  SPDX-License-Identifier: Apache-2.0
#
#*******************************************************************************
#
"""
Point remove query
"""

from ctypes import byref, c_void_p, c_uint32

from . import library
from . import utilities


class QueryRemove:
    """
    Point remove query

    This class can be used to remove (delete) existing points.

    Note: You either must delete the query object or call close()
          __before__ the parent Pointcloud instance is closed/deleted!
    """

    def __init__(self, pointcloud):
        """
        Constructor

        Creates a query prepared for removing points.
        """
        self.context = pointcloud.context
        self.pointcloud = pointcloud
        self.handle = c_void_p(None)
        self.context.check(
            library.handle.rdb_pointcloud_query_remove_new(
                self.context.handle,
                self.pointcloud.handle,
                byref(self.handle)
            )
        )

    def __del__(self):
        self.close()

    def __enter__(self):
        return self

    # noinspection PyUnusedLocal
    def __exit__(self, exc_type, exc_value, traceback):
        self.close()

    @property
    def valid(self):
        """"
        Check if query is not null

        A null query cannot be used to remove points.

        Returns True if the query is not null
        """
        return self.handle != c_void_p(None)

    def close(self):
        """
        Finish query

        Call this function when done with removing points.
        """
        if self.valid:
            library.handle.rdb_pointcloud_query_remove_delete(
                self.context.handle,
                byref(self.handle)
            )
            self.handle = c_void_p(None)

    def bind(self, buffer):
        """
        Bind attribute buffer

        Use this function to define a source buffer for a point attribute.
        Exactly one buffer can be defined for an attribute (i.e. only the
        most recently defined buffer will be used).

        The buffer is expected to be n*d bytes large, where
        __n__ is the number of points defined in next(),
        __d__ is the number of attribute dimensions (elements).

        Note: This function just stores the buffer pointer - it does
              __NOT__ copy the data contained in the buffer. So make
              sure that the buffer remains valid until you call next().

        Note: This function expects a buffer for the point ID attribute.

        Returns the given buffer object
        """
        from . import attributebuffer
        from . import pointbuffer
        if isinstance(buffer, attributebuffer.AttributeBuffer):
            self.context.check(
                library.handle.rdb_pointcloud_query_remove_bind(
                    self.context.handle,
                    self.handle,
                    utilities.to_rdb_string(buffer.point_attribute.name),
                    c_uint32(buffer.data_type.value),
                    buffer.raw_data(),
                    0  # no stride
                )
            )
            return buffer
        elif isinstance(buffer, pointbuffer.PointBuffer):
            for item in buffer.buffers.values():
                self.bind(item)
            return buffer
        else:
            raise TypeError(
                "Buffer must be of type {0} or {1}.".format(
                    "riegl.rdb.attributebuffer.AttributeBuffer",
                    "riegl.rdb.pointbuffer.PointBuffer"
                )
            )

    def next(self, count):
        """
        Remove points

        Use this function to actually remove (delete) the points.
        All points given by their point ID stored in the previously
        bound attribute buffer are removed from the database.

        Afterwards you may re-fill the buffers or define a new buffer
        with bind() and call next() again until all points have been
        removed.

        Note: If __no__ point ID buffer is given and 'count' is 0xDEADFEED,
              then __all__ points are removed and next() always returns 1.

        Returns the number of points removed
        """
        count = c_uint32(count)
        result = c_uint32(0)
        self.context.check(
            library.handle.rdb_pointcloud_query_remove_next(
                self.context.handle,
                self.handle,
                count,
                byref(result)
            )
        )
        return result.value
