# coding: utf-8
# Copyright (C) 2023 Ilia Kurochkin <brothermechanic@yandex.com>
#
# This program 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.
'''
DESCRIPTION.
Retopology visual assets for simulation pipeline.
'''
__version__ = '0.3'

import logging
import bpy
import math

from blender.utils.generative_modifiers import shell_remesher
from blender.utils.object_converter import mesh_to_mesh
from blender.utils.object_relations import parenting
from blender.utils.mesh_tools import select_peaks, select_stratched_edges

logger = logging.getLogger(__name__)


def parts_to_shells(part_names, **cg_config):
    ''' Create lowpoly shells from parts collections. '''
    logger.info('Lowpoly shells creation launched...')

    lowpoly_col = bpy.data.collections.new(cg_config['lowpoly_col_name'])
    bpy.context.scene.collection.children.link(lowpoly_col)
    for part_name in part_names:
        # generate lowpoly objects from part collections
        lowpoly_name = '{}_{}'.format(part_name, cg_config['lowpoly'])
        lowpoly_mesh = bpy.data.meshes.new(lowpoly_name)
        lowpoly_obj = bpy.data.objects.new(lowpoly_name, lowpoly_mesh)
        bpy.context.view_layer.update()
        lowpoly_col.objects.link(lowpoly_obj)
        if bpy.data.objects[part_name].parent:
            root_locator = bpy.data.objects[part_name].parent
        else:
            root_locator = bpy.data.objects[part_name]
        lowpoly_obj.matrix_world = root_locator.matrix_world.copy()
        parenting(root_locator, lowpoly_obj)
        part_col = bpy.data.collections[
            '{}_{}'.format(part_name, cg_config['hightpoly'])]
        shell_remesher(lowpoly_obj, 'remesh_nodes', 'robossembler')
        lowpoly_obj.modifiers['remesh_nodes']['Input_0'] = part_col

        remesh_voxel = lowpoly_obj.modifiers.new('remesh_voxel', type='REMESH')
        remesh_voxel.mode = 'VOXEL'
        remesh_voxel.voxel_size = 0.001

        remesh_sharp = lowpoly_obj.modifiers.new('remesh_sharp', type='REMESH')
        remesh_sharp.mode = 'SHARP'
        remesh_sharp.octree_depth = 7

        decimate = lowpoly_obj.modifiers.new('decimate', type='DECIMATE')
        decimate.decimate_type = 'COLLAPSE'
        decimate.ratio = 0.1

        # apply all modifiers to mesh
        parenting(root_locator, mesh_to_mesh(lowpoly_obj))

    # fix non_manifold shape
    for lowpoly_obj in lowpoly_col.objects:
        bpy.ops.object.select_all(action='DESELECT')
        lowpoly_obj.select_set(state=True)
        bpy.context.view_layer.objects.active = lowpoly_obj
        bpy.ops.object.mode_set(mode='EDIT')
        # pass 1
        bpy.ops.mesh.select_all(action='DESELECT')
        bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
        select_peaks(lowpoly_obj.data)
        bpy.ops.mesh.select_non_manifold()
        bpy.ops.mesh.dissolve_mode(use_verts=True, use_boundary_tear=False)
        bpy.ops.mesh.delete(type='VERT')
        bpy.ops.mesh.select_all(action='SELECT')
        bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
        # pass 2
        bpy.ops.mesh.select_all(action='DESELECT')
        bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
        select_peaks(lowpoly_obj.data)
        bpy.ops.mesh.select_non_manifold()
        bpy.ops.mesh.dissolve_mode(use_verts=True, use_boundary_tear=False)
        bpy.ops.mesh.delete(type='VERT')
        bpy.ops.mesh.select_all(action='SELECT')
        bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
        # pass 3
        bpy.ops.mesh.select_all(action='DESELECT')
        bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
        select_peaks(lowpoly_obj.data)
        bpy.ops.mesh.select_non_manifold()
        bpy.ops.mesh.dissolve_mode(use_verts=True, use_boundary_tear=False)
        bpy.ops.mesh.delete(type='VERT')
        bpy.ops.mesh.select_all(action='SELECT')
        bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
        # pass 4
        bpy.ops.mesh.select_all(action='DESELECT')
        bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
        select_peaks(lowpoly_obj.data)
        bpy.ops.mesh.select_non_manifold()
        bpy.ops.mesh.dissolve_mode(use_verts=True, use_boundary_tear=False)
        bpy.ops.mesh.delete(type='VERT')
        bpy.ops.mesh.select_all(action='SELECT')
        bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
        # pass 5
        bpy.ops.mesh.select_all(action='DESELECT')
        bpy.ops.mesh.select_mode(type='EDGE')
        select_stratched_edges(lowpoly_obj.data)
        bpy.ops.mesh.dissolve_mode(use_verts=True)
        bpy.ops.mesh.select_all(action='SELECT')
        bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
        bpy.ops.mesh.normals_make_consistent()
        # final
        bpy.ops.mesh.select_all(action='DESELECT')
        bpy.ops.mesh.select_mode(type='FACE')
        bpy.ops.object.mode_set(mode='OBJECT')
        # shading
        bpy.ops.object.shade_smooth(use_auto_smooth=True)
        lowpoly_obj.data.auto_smooth_angle = math.radians(10)
        lowpoly_obj.modifiers.new(type='WEIGHTED_NORMAL', name='WeightedNormal')
        lowpoly_obj.modifiers['WeightedNormal'].keep_sharp = True
        bpy.ops.object.modifier_apply(modifier="WeightedNormal")

    logger.info('Generation of %s lowpoly shells is finished!', len(lowpoly_col.objects))

    return [obj.name for obj in lowpoly_col.objects]
