# -*- 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
#
#*******************************************************************************
#
"""
Buffer for point attributes of multiple points
"""

import collections

from . import attributebuffer


class PointReference:
    """
    A storable reference to a single point

    The point reference stores the point index and a reference to the buffer
    where the point is stored (an instance of class PointBuffer).

    Note: The buffer will stay in memory, even when the originating query
    advances to the next block. It will disappear when there is no reference
    left, i.e., no instance of PointReference is referencing it.
    """

    def __init__(self, buffer, index):
        assert(isinstance(buffer, PointBuffer))
        assert(0 <= index < len(buffer))

        self.buffer = buffer
        self.index = index

        self.__class__.__getitem__ = (
            lambda self, key: self.buffer.buffers[key][self.index],
            lambda self, key: self.buffer.buffers[key].data[self.index]
        )[attributebuffer.RDB_HAVE_NUMPY is True]

        self.__class__.__getattr__ = (
            lambda self, name: getattr(self.buffer, name)[self.index],
            lambda self, name: getattr(self.buffer, name).data[self.index]
        )[attributebuffer.RDB_HAVE_NUMPY is True]


class PointIterator:
    """
    An iterator over the point container (an instance of class PointBuffer)
    """

    def __init__(self, buffer):
        assert(isinstance(buffer, PointBuffer))

        self.buffer = buffer
        self.total = buffer.point_count_total
        self.index = 0

    def __next__(self):
        if self.index < self.total:
            self.index += 1
            return PointReference(self.buffer, self.index - 1)
        else:
            raise StopIteration

    def __iter__(self):
        return self

    def format(self, delimiter=", "):
        if 0 < self.index <= self.total:
            result = list()
            for field in self.buffer.buffers.values():
                result.append(str(field[self.index - 1]))
            return delimiter.join(result)
        else:
            return str()

    def __repr__(self):
        return self.format()


class PointBuffer:
    """
    Buffer for point attributes of multiple points

    This class is a container for all point attributes of a set of points.
    """

    def __init__(self, pointcloud, attributes, count):
        self.point_count_limit = count
        self.point_count_total = 0
        self.buffers = collections.OrderedDict()

        if attributes is None:  # then simply use all point attributes
            attributes = pointcloud.point_attributes

        for attribute in attributes:  # create one buffer per attribute
            detail = pointcloud.point_attributes[attribute]
            buffer = attributebuffer.AttributeBuffer(detail, count)
            self.buffers[detail.name] = buffer
            setattr(self, detail.name.replace(".", "_"), buffer)

    def resize(self, point_count):
        """Resize all attribute buffers to given point count"""
        for buffer in self.buffers.values():
            buffer.resize(point_count)

    def __len__(self):
        return self.point_count_total

    def __getitem__(self, item):
        return self.buffers[item]

    def __iter__(self):
        return PointIterator(self)
