#  ***** BEGIN GPL LICENSE BLOCK *****
#
#   Copyright (C) 2024 Ilia Kurochkin <brothermechanic@yandex.com>
#
#   Created by Ilia Kurochkin (brothermechanic)
#   contact:    brothermechanic@yandex.com
#
#   This is free software;
#   you can redistribute it and/or modify
#   it under the terms of the GNU General Public License
#   as published by the Free Software Foundation; either version 3
#   of the License, or (at your option) any later version.
#
#   This program 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 General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, see <https://www.gnu.org/licenses/>.
#
#  ***** END GPL LICENSE BLOCK *****
#
# coding: utf-8
'''
DESCRIPTION.
Various mesh tools for Edit Mode.
'''

__version__ = '0.1'

import math
import bpy
import bmesh


def select_peaks(me, peak_limit_angle=60, peak_accuracy_angle=10):
    ''' Select sharp vertices that stand alone. '''
    bm = bmesh.from_edit_mesh(me)

    def is_sharp(vert, eps=math.radians(peak_limit_angle)):
        sharps = []
        face_before = None
        for face in vert.link_faces:
            if face_before:
                face_angle = face.normal.angle(face_before.normal)
                if face_angle > math.radians(peak_accuracy_angle):
                    angle = vert.normal.angle(face.normal)
                    if angle > eps:
                        sharps.append(angle)
            face_before = face
        return (
            (len(sharps) + 1) == len(vert.link_faces)
            or (len(sharps) + 2) == len(vert.link_faces)
        )

    def non_single(vert):
        for edge in vert.link_edges:
            if edge.other_vert(vert).select:
                return False
        return True

    for v in bm.verts:
        v.select_set(
            is_sharp(v)
        )

    for v in bm.verts:
        if v.select:
            v.select_set(
                non_single(v)
            )

    bmesh.update_edit_mesh(me)
    return me


def select_zero_faces(me):
    ''' Select very small faces. '''
    bm = bmesh.from_edit_mesh(me)
    for myface in bm.faces:
        if myface.calc_area() < 1e-7:
            myface.select_set(True)
    bmesh.update_edit_mesh(me)
    return me


def select_stratched_edges(me, edge_length_limit=0.002):
    ''' Select very stratched edges of small faces. '''
    bm = bmesh.from_edit_mesh(me)
    faces_stratched = [f for f in bm.faces if f.calc_area() < 1e-6]
    for face in faces_stratched:
        edges_lengths = {e: e.calc_length() for e in face.edges}
        edge_max_length = max(edges_lengths.values())
        if edge_max_length > edge_length_limit:
            edge_max = [k for k, v in edges_lengths.items() if v == edge_max_length][0]
            edge_max.select_set(True)
            bmesh.update_edit_mesh(me)
    return me


def collect_less_volume_objs(objs: list, min_volume):
    ''' Separate selection for less volume objects. '''
    less_volume_objs = []
    for obj in objs:
        bpy.ops.object.select_all(action='DESELECT')
        if obj.type != 'MESH':
            continue
        # requed for bmesh
        obj.hide_set(False)
        obj.select_set(state=True)
        if obj.type == 'MESH':
            bpy.ops.object.mode_set(mode='EDIT')
            bm = bmesh.from_edit_mesh(obj.data)
            if bm.calc_volume() < min_volume:
                less_volume_objs.append(obj)
            bpy.ops.object.mode_set(mode='OBJECT')
    return less_volume_objs
