
# "Scatter" Add-on
# Copyright (C) 2019 Dorian Borremans aka BD3D
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# <pep8 compliant>

bl_info = {
    "name" : "Scatter [BD3D]", 
    "author" : "BD3D",   
    "description" : "The scattering tool of 2.8",
    "blender" : (2, 80, 0),
    "location" : "Operator",
    "warning" : "",
    "category" : "Generic"
}

import bpy
import random
from bpy.types import Menu
import webbrowser
from bpy.types import Operator, AddonPreferences, PropertyGroup
from bpy.props import StringProperty, IntProperty, BoolProperty, FloatProperty
import rna_keymap_ui
context = bpy.context

def find_collection(context, item):
    collections = item.users_collection
    if len(collections) > 0:
        return collections[0]
    return context.scene.collection

def make_collection(collection_name, parent_collection):
    if collection_name in bpy.data.collections:
        return bpy.data.collections[collection_name]
    else:
        new_collection = bpy.data.collections.new(collection_name)
        parent_collection.children.link(new_collection)
        return new_collection

######################################################################################
######################################################################################
# # # # # # # # # # # #         SCATTER ADDON PREF             # # # # # # # # # # # # 
######################################################################################
######################################################################################

class ScatterPref(AddonPreferences):
    bl_idname = __name__

    scatter_01_count = IntProperty( 
            name="Emission Number",
            subtype='NONE',
            default=1000,
            min=0,
            )#percentage couvert? 
    scatter_01_seed = IntProperty( 
            name="Emission Seed Value",
            subtype='NONE',
            default=5,
            min=0,
            )
    scatter_01_seed_is_random = BoolProperty( 
            name="Emission Random Seed Value",
            subtype='NONE',
            default=True,
            )
    scatter_01_particle_size = FloatProperty(
            name="Render Scale",
            subtype='NONE',
            default=0.25,
            min=0.01,
            max=3
            )
    scatter_01_size_random = FloatProperty(
            name="Render Scale Randomness",
            subtype='NONE',
            default=0.35,
            min=0,
            max=1
            )
    scatter_01_phase_factor_random = FloatProperty(
            name="Rotation Randomize Phase",
            subtype='NONE',
            default=2,
            min=0,
            max=2
            )
    scatter_01_display_percentage = IntProperty(
            name="Viewport Display Percentage",
            subtype='NONE',
            default=100,
            min=0,
            max=100
            )

    scatter_01_noise_scale = FloatProperty(
            name="Texture Noise Size",
            subtype='NONE',
            default=0.45,
            min=0.01,
            max=100
            )
    scatter_01_noise_depth = IntProperty(
            name="Texture Noise Depth",
            subtype='NONE',
            default=0,
            min=0,
            max=20
            )
    scatter_01_contrast = FloatProperty(
            name="Texture Color Contrast",
            subtype='NONE',
            default=3,
            min=0,
            max=5
            )
    scatter_01_intensity = FloatProperty(
            name="Texture Color Brightness",
            subtype='NONE',
            default=1,
            min=0,
            max=2
            )
    scatter_01_density_factor = FloatProperty(
            name="Texture Influence Density",
            subtype='NONE',
            default=1,
            min=0,
            max=1
            )
    scatter_01_length_factor = FloatProperty(
            name="Texture Influence Length",
            subtype='NONE',
            default=0.3,
            min=0,
            max=1
            )

    scatter_01_scalex = FloatProperty(
            name="Texture Mapping size X",
            subtype='NONE',
            default=1,
            min=-100,
            max=100
            )
    scatter_01_scaley = FloatProperty(
            name="Texture Mapping size Y",
            subtype='NONE',
            default=1,
            min=-100,
            max=100
            )
    scatter_01_scalez = FloatProperty(
            name="Texture Mapping size Z",
            subtype='NONE',
            default=1,
            min=-100,
            max=100
            )

    scatter_01_offsetx = FloatProperty(
            name="Texture Mapping offset X in meters",
            subtype='NONE',
            default=0,
            min=-10,
            max=10
            )
    scatter_01_offsety = FloatProperty(
            name="Texture Mapping offset Y in meters",
            subtype='NONE',
            default=0,
            min=-10,
            max=10
            )
    scatter_01_offsetz = FloatProperty(
            name="Texture Mapping offset Z in meters",
            subtype='NONE',
            default=0,
            min=-10,
            max=10
            )


    scatter_01_size_is_random = BoolProperty( 
            name="Texture Mapping Random Size Value XYZ",
            subtype='NONE',
            default=False,
            )
    scatter_01_size_A = FloatProperty(
            name="Size Possibilities Range From A",
            subtype='NONE',
            default=0.7,
            min=-100,
            max=100
            )
    scatter_01_size_B = FloatProperty(
            name="To B",
            subtype='NONE',
            default=1.4,
            min=-100,
            max=100
            )


    scatter_01_offset_is_random = BoolProperty( 
            name="Texture Mapping Random Offset Value XYZ in meters",
            subtype='NONE',
            default=True,
            )
    scatter_01_offset_A = FloatProperty(
            name="Offset Possibilities Range From A",
            subtype='NONE',
            default=0,
            min=-10,
            max=10
            )
    scatter_01_offset_B = FloatProperty(
            name="To B",
            subtype='NONE',
            default=10,
            min=-10,
            max=10
            )


    scatter_01_open= bpy.props.BoolProperty(
        name="Show Debug Tools",
        description="Expand me senpai",
        default=False
    )
######################################################################################

    def draw(self, context): #DRAWING HERE - DRAWING HERE - DRAWING HERE- DRAWING HERE
        layout = self.layout
        full= "------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
        addon_prefs = context.preferences.addons[__name__].preferences

        box = layout.box()
        col = box.column()
        row = col.row(align=True)
        row.alignment = 'LEFT'
        row.prop(self,
                 'scatter_01_open',
                 text="Custom Scattering Slot 01",
                 emboss=False,
                 icon="PLAY" if self.scatter_01_open else "PLAY")
        if self.scatter_01_open:

            col.label(text=full)            

            col.prop(self,"scatter_01_count")
            if addon_prefs.scatter_01_seed_is_random == True:
                col.prop(self,"scatter_01_seed_is_random")
            else:
                col.prop(self,"scatter_01_seed_is_random")
                col.prop(self,"scatter_01_seed")
            col.prop(self,"scatter_01_particle_size")
            col.prop(self,"scatter_01_size_random")
            col.prop(self,"scatter_01_phase_factor_random")

            col.label(text=' ')
            col.prop(self,"scatter_01_density_factor")
            col.prop(self,"scatter_01_length_factor")

            col.label(text=' ')
            col.prop(self,"scatter_01_offset_is_random")
            if addon_prefs.scatter_01_offset_is_random == True:
                col.prop(self,"scatter_01_offset_A")
                col.prop(self,"scatter_01_offset_B")
            else:
                col.prop(self,"scatter_01_offsetx")
                col.prop(self,"scatter_01_offsety")
                col.prop(self,"scatter_01_offsetz")
            
            col.label(text=' ')
            col.prop(self,"scatter_01_size_is_random")
            if addon_prefs.scatter_01_size_is_random == True:
                col.prop(self,"scatter_01_size_A")
                col.prop(self,"scatter_01_size_B")
            else:
                col.prop(self,"scatter_01_scalex")
                col.prop(self,"scatter_01_scaley")
                col.prop(self,"scatter_01_scalez")

            col.label(text=' ')
            col.prop(self,"scatter_01_noise_scale")
            col.prop(self,"scatter_01_noise_depth")
            col.prop(self,"scatter_01_contrast")
            col.prop(self,"scatter_01_intensity")

            col.label(text=' ')
            col.prop(self,"scatter_01_display_percentage")

 


######################################################################################
######################################################################################
# # # # # # # # # # # #        SCATTER OPERATOR                # # # # # # # # # # # # 
######################################################################################
######################################################################################

class Scatter_OT_Custom01(bpy.types.Operator):
    bl_idname = "scatter.custom01"
    bl_label = "custom scattering operator 01"
    bl_description = ""

    def execute(self, context):
        context = bpy.context
        addon_prefs = context.preferences.addons[__name__].preferences
        scene = context.scene
        A = context.object
        ### ### Naming
        slotname = "Custom1"
        if len(bpy.context.selected_objects) > 2:
            particlename = bpy.context.selected_objects[1].name + " ..."
        else:
            particlename = bpy.context.selected_objects[1].name #BUG why the f does the name change when multiple execution ???
        pref = "SCATTER: ["+slotname+"] ["+particlename+"] v."
        i = 1
        name = "%s%d" % (pref, i)
        while A.modifiers.get(name):    
            i += 1
            name = "%s%d" % (pref, i)
        ### ### add particle system + rename and add selection in new coll
        m = A.modifiers.new(name, type='PARTICLE_SYSTEM')
        A.select_set(state=False)
        for o in bpy.context.selected_objects:
            o_collection = find_collection(bpy.context, o)
            new_collection = make_collection(name , o_collection)
            new_collection.objects.link(o)
            o_collection.objects.unlink(o)
        A.select_set(state=True)
        ### ### particle parameters
        ps = m.particle_system
        ps.name = name
        ps.settings.name = name
        ps.settings.type = 'HAIR'
        ps.settings.render_type = 'COLLECTION'
        ps.settings.instance_collection = bpy.data.collections[name]
        ps.settings.particle_size = addon_prefs.scatter_01_particle_size
        ps.settings.hair_length = 4
        ps.settings.size_random = addon_prefs.scatter_01_size_random
        ps.settings.count = addon_prefs.scatter_01_count
        if addon_prefs.scatter_01_seed_is_random == True:
            bpy.context.object.particle_systems[name].seed = random.randint(0,10000)
        else:
            bpy.context.object.particle_systems[name].seed = addon_prefs.scatter_01_seed
        ps.settings.display_percentage = addon_prefs.scatter_01_display_percentage
        ps.settings.use_advanced_hair = True
        ps.settings.use_rotations = True
        ps.settings.rotation_factor_random = 1
        ps.settings.phase_factor = 1
        ps.settings.phase_factor_random = addon_prefs.scatter_01_phase_factor_random
        ### ### texture parameters
        bpy.ops.texture.new()
        texturename = name
        bpy.data.textures[-1].name = texturename
        bpy.data.textures[texturename].type = 'CLOUDS' #LATER SUPPORT OTHER NOISE TYPE AND OPTIONS
        bpy.data.textures[texturename].noise_scale = addon_prefs.scatter_01_noise_scale
        bpy.data.textures[texturename].noise_depth = addon_prefs.scatter_01_noise_depth
        bpy.data.textures[texturename].contrast = addon_prefs.scatter_01_contrast
        bpy.data.textures[texturename].intensity = addon_prefs.scatter_01_intensity
        ps.settings.texture_slots.add().texture = bpy.data.textures[texturename]
        ps.settings.texture_slots[0].blend_type = 'MULTIPLY'
        ps.settings.texture_slots[0].use_map_time = False
        ps.settings.texture_slots[0].use_map_density = True
        ps.settings.texture_slots[0].density_factor = addon_prefs.scatter_01_density_factor
        ps.settings.texture_slots[0].use_map_length = True 
        ps.settings.texture_slots[0].length_factor = addon_prefs.scatter_01_length_factor

        if addon_prefs.scatter_01_offset_is_random == False:
            ps.settings.texture_slots[0].scale[1] = addon_prefs.scatter_01_scalex
            ps.settings.texture_slots[0].scale[2] = addon_prefs.scatter_01_scaley
            ps.settings.texture_slots[0].scale[0] = addon_prefs.scatter_01_scalez
        else:
            randomn =round(random.uniform(addon_prefs.scatter_01_size_A,addon_prefs.scatter_01_size_B), 2)
            ps.settings.texture_slots[0].scale[1] = randomn
            ps.settings.texture_slots[0].scale[2] = randomn
            ps.settings.texture_slots[0].scale[0] = randomn

        if addon_prefs.scatter_01_offset_is_random == False:
            ps.settings.texture_slots[0].offset[1] = addon_prefs.scatter_01_offsetx
            ps.settings.texture_slots[0].offset[2] = addon_prefs.scatter_01_offsety
            ps.settings.texture_slots[0].offset[0] = addon_prefs.scatter_01_offsetz
        else:
            randomno =round(random.uniform(addon_prefs.scatter_01_offset_A,addon_prefs.scatter_01_offset_B), 2)
            ps.settings.texture_slots[0].offset[1] = randomno
            ps.settings.texture_slots[0].offset[2] = randomno
            ps.settings.texture_slots[0].offset[0] = randomno

        ps.settings.texture_slots[0].texture_coords = 'GLOBAL'
        return {'FINISHED'}

######################################################################################
######################################################################################
# # # # # # # # # # # #            N MENU                      # # # # # # # # # # # # 
######################################################################################
######################################################################################

class Scatter_PT_ScatteringPanel(bpy.types.Panel):
    bl_idname = "Scatter_PT_ScatteringPanel"
    bl_label = "Scatter"
    bl_category = "Scatter"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"

    def draw(self, context):
        layout = self.layout
        bigbutton = layout.column()
        bigbutton.scale_y=2
        bigbutton.operator('scatter.custom01', text="SCATTER",  icon='PLAY')

######################################################################################
######################################################################################
# # # # # # # # # # # #                 REG                    # # # # # # # # # # # # 
######################################################################################
######################################################################################

def register():
    bpy.utils.register_class(ScatterPref)
    bpy.utils.register_class(Scatter_OT_Custom01)
    bpy.utils.register_class(Scatter_PT_ScatteringPanel)

def unregister():
    bpy.utils.unregister_class(ScatterPref)
    bpy.utils.unregister_class(Scatter_OT_Custom01)
    bpy.utils.unregister_class(Scatter_PT_ScatteringPanel)


if __name__ == "__main__":
    register()
    #write ops to do on script exec