# -*- 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
#
#*******************************************************************************
#
"""
Basic point cloud management interface
"""

import json
from ctypes import CFUNCTYPE, byref, c_uint8, c_uint32, c_void_p

from . import library
from . import utilities
from . import createsettings


class Management:
    """
    Basic point cloud management interface
    """

    def __init__(self, pointcloud):
        self.context = pointcloud.context
        self.pointcloud = pointcloud

    @property
    def lod_mode(self):
        """
        Level of detail mode

        Details see: riegl::rdb::pointcloud::CreateSettings::LodMode
        """
        buffer = c_uint32(0)
        self.context.check(
            library.handle.rdb_pointcloud_management_get_lod_mode(
                self.context.handle, self.pointcloud.handle, byref(buffer)
            )
        )
        return createsettings.CreateSettings.LodMode(buffer.value)

    @lod_mode.setter
    def lod_mode(self, value):
        buffer = c_uint32(value)
        self.context.check(
            library.handle.rdb_pointcloud_management_set_lod_mode(
                self.context.handle, self.pointcloud.handle, buffer
            )
        )

    @property
    def chunk_size_lod(self):
        """
        Level of detail size

        Details see: riegl::rdb::pointcloud::CreateSettings::LodMode
        """
        buffer = c_uint32(0)
        self.context.check(
            library.handle.rdb_pointcloud_management_get_chunk_size_lod(
                self.context.handle, self.pointcloud.handle, byref(buffer)
            )
        )
        return buffer.value

    @chunk_size_lod.setter
    def chunk_size_lod(self, value):
        buffer = c_uint32(value)
        self.context.check(
            library.handle.rdb_pointcloud_management_set_chunk_size_lod(
                self.context.handle, self.pointcloud.handle, buffer
            )
        )

    def finalize(self):
        """
        Dismiss database history

        This function deletes all transactions except the first (database
        creation) and the current transaction (last committed or restored).
        Please note that this operation only removes the transactions from
        the database history and releases the related data blocks in the
        database file so that they can be re-used by subsequent transactions.
        However the database file size will not decrease unless you call
        vacuum().
        """
        self.context.check(
            library.handle.rdb_pointcloud_management_finalize(
                self.context.handle, self.pointcloud.handle
            )
        )

    def vacuum(self, progress=None):
        """
        Optimize database file

        This function reorganizes the data blocks in the database file so
        that there are no (or as few as possible) unused blocks (gaps).
        This is especially helpful after deleting point attributes or
        calling finalize().

        Note: This might be a lengthy operation and no other client can
              access the database in the meantime, not even to read.
        """
        if progress is not None:

            # noinspection PyUnusedLocal
            def progress_wrapper(progress_value, userdata_value):
                try:
                    progress(progress_value)
                except Exception as error:
                    logger = logging.getLogger("riegl.rdb")
                    logger.warning("Commit progress callback failed: %s", error)

            progress_pointer = CFUNCTYPE(None, c_uint8, c_void_p)(
                progress_wrapper
            )

            self.context.check(
                library.handle.rdb_pointcloud_management_vacuum(
                    self.context.handle, self.pointcloud.handle,
                    progress_pointer, c_void_p(0)  # not used
                )
            )
        else:
            self.context.check(
                library.handle.rdb_pointcloud_management_vacuum(
                    self.context.handle, self.pointcloud.handle,
                    c_void_p(0), c_void_p(0)  # not used
                )
            )

    def validate(self, schema, strict=False):
        """
        Validate database file

        This function checks whether the database corresponds to the given schema.
        The schema contains a list of required and optional point attributes and
        metadata entries and is given in JSON format. Primary point attributes
        are marked with a "*", optional attributes or metadata entries are
        marked with a "?" appended to the name, all other items are required.

        The database must at least contain all primary and required point attributes
        and all required metadata entries to correspond to the schema. If "strict"
        is "True", then the database additionally is not allowed to contain extra
        point attributes or metadata entries that are not listed in the schema.

        If the database does not correspond to the schema, an exception
        is thrown and the reason can be found in the exception details.

        Example schema JSON string:

            {
                "extension": "rdbx",
                "attributes": [
                    "riegl.xyz*",
                    "riegl.timestamp",
                    "riegl.class?"
                ],
                "metadata": [
                    "riegl.geo_tag",
                    "riegl.device?"
                ]
            }
        """
        if not isinstance(schema, str):
            schema = json.dumps(schema)
        strict = c_uint32(1 if strict else 0)
        self.context.check(
            library.handle.rdb_pointcloud_management_validate(
                self.context.handle, self.pointcloud.handle,
                utilities.to_rdb_string(schema), strict

            )
        )
