# ##### BEGIN GPL LICENSE BLOCK #####
#
#  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 2
#  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, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

# <pep8 compliant>

import bpy

from bpy.utils import register_class
from bpy.types import (
        Node,
        ShaderNodeTree,
        CompositorNodeTree,
        TextureNodeTree,
        #NodeSocket,
        Operator,
        )
from bpy.props import (
        StringProperty,
        BoolProperty,
        IntProperty,
        FloatProperty,
        FloatVectorProperty,
        EnumProperty,
        #PointerProperty,
        #CollectionProperty,
        )



############### object

class ObjectNodeTree(bpy.types.NodeTree):
    '''Povray Material Nodes'''

    bl_idname = 'ObjectNodeTree'
    bl_label = 'Povray Object Nodes'
    bl_icon = 'PLUGIN'

    @classmethod
    def poll(cls, context):
        return context.scene.render.engine == 'POVRAY_RENDER'

    @classmethod
    def get_from_context(cls, context):
        ob = context.active_object
        if ob and ob.type not in {'LIGHT'}:
            ma = ob.active_material
            if ma is not None:
                nt_name = ma.node_tree
                if nt_name != '':
                    return nt_name, ma, ma
        return (None, None, None)

    def update(self):
        self.refresh = True
################### output #############################################################################################

class PovrayOutputNode(Node, ObjectNodeTree):
    '''Output'''
    bl_idname = 'PovrayOutputNode'
    bl_label = 'Output'
    bl_icon = 'SOUND'

    def init(self, context):

        self.inputs.new('PovraySocketTexture', "Texture")

    def draw_buttons(self, context, layout):

        ob=context.object
        layout.prop(ob.pov, "object_ior",slider=True)

    def draw_buttons_ext(self, context, layout):

        ob=context.object
        layout.prop(ob.pov, "object_ior",slider=True)

    def draw_label(self):
        return "Output"



################### material ###########################################################################################
class PovrayTextureNode(Node, ObjectNodeTree):
    '''Texture'''
    bl_idname = 'PovrayTextureNode'
    bl_label = 'Simple texture'
    bl_icon = 'SOUND'

    def init(self, context):

        color=self.inputs.new('PovraySocketColor', "Pigment")
        color.default_value=(1,1,1)
        normal=self.inputs.new('NodeSocketFloat', "Normal")
        normal.hide_value=True
        finish=self.inputs.new('NodeSocketVector', "Finish")
        finish.hide_value=True

        self.outputs.new('PovraySocketTexture', "Texture")

    def draw_label(self):
        return "Simple texture"

class PovrayFinishNode(Node, ObjectNodeTree):
    '''Finish'''
    bl_idname = 'PovrayFinishNode'
    bl_label = 'Finish'
    bl_icon = 'SOUND'

    def init(self, context):

        self.inputs.new('PovraySocketFloat_0_1', "Emission")
        ambient=self.inputs.new('NodeSocketVector', "Ambient")
        ambient.hide_value=True
        diffuse=self.inputs.new('NodeSocketVector', "Diffuse")
        diffuse.hide_value=True
        specular=self.inputs.new('NodeSocketVector', "Highlight")
        specular.hide_value=True
        mirror=self.inputs.new('NodeSocketVector', "Mirror")
        mirror.hide_value=True
        iridescence=self.inputs.new('NodeSocketVector', "Iridescence")
        iridescence.hide_value=True
        subsurface=self.inputs.new('NodeSocketVector', "Translucency")
        subsurface.hide_value=True
        self.outputs.new('NodeSocketVector', "Finish")

    def draw_label(self):
        return "Finish"

class PovrayDiffuseNode(Node, ObjectNodeTree):
    '''Diffuse'''
    bl_idname = 'PovrayDiffuseNode'
    bl_label = 'Diffuse'
    bl_icon = 'SOUND'

    def init(self, context):

        intensity=self.inputs.new('PovraySocketFloat_0_1', "Intensity")
        intensity.default_value=0.8
        albedo=self.inputs.new('NodeSocketBool', "Albedo")
        albedo.default_value=False
        brilliance=self.inputs.new('PovraySocketFloat_0_10', "Brilliance")
        brilliance.default_value=1.8
        self.inputs.new('PovraySocketFloat_0_1', "Crand")
        self.outputs.new('NodeSocketVector', "Diffuse")

    def draw_label(self):
        return "Diffuse"

class PovrayPhongNode(Node, ObjectNodeTree):
    '''Phong'''
    bl_idname = 'PovrayPhongNode'
    bl_label = 'Phong'
    bl_icon = 'SOUND'

    def init(self, context):

        albedo=self.inputs.new('NodeSocketBool', "Albedo")
        intensity=self.inputs.new('PovraySocketFloat_0_1', "Intensity")
        intensity.default_value=0.8
        phong_size=self.inputs.new('PovraySocketInt_0_256', "Size")
        phong_size.default_value=60
        metallic=self.inputs.new('PovraySocketFloat_0_1', "Metallic")

        self.outputs.new('NodeSocketVector', "Phong")

    def draw_label(self):
        return "Phong"

class PovraySpecularNode(Node, ObjectNodeTree):
    '''Specular'''
    bl_idname = 'PovraySpecularNode'
    bl_label = 'Specular'
    bl_icon = 'SOUND'

    def init(self, context):

        albedo=self.inputs.new('NodeSocketBool', "Albedo")
        intensity=self.inputs.new('PovraySocketFloat_0_1', "Intensity")
        intensity.default_value=0.8
        roughness=self.inputs.new('PovraySocketFloat_0_1', "Roughness")
        roughness.default_value=0.02
        metallic=self.inputs.new('PovraySocketFloat_0_1', "Metallic")

        self.outputs.new('NodeSocketVector', "Specular")

    def draw_label(self):
        return "Specular"

class PovrayMirrorNode(Node, ObjectNodeTree):
    '''Mirror'''
    bl_idname = 'PovrayMirrorNode'
    bl_label = 'Mirror'
    bl_icon = 'SOUND'

    def init(self, context):

        color=self.inputs.new('PovraySocketColor', "Color")
        color.default_value=(1,1,1)
        metallic=self.inputs.new('PovraySocketFloat_0_1', "Metallic")
        metallic.default_value=1.0
        exponent=self.inputs.new('PovraySocketFloat_0_1', "Exponent")
        exponent.default_value=1.0
        self.inputs.new('PovraySocketFloat_0_1', "Falloff")
        self.inputs.new('NodeSocketBool', "Fresnel")
        self.inputs.new('NodeSocketBool', "Conserve energy")
        self.outputs.new('NodeSocketVector', "Mirror")

    def draw_label(self):
        return "Mirror"

class PovrayAmbientNode(Node, ObjectNodeTree):
    '''Ambient'''
    bl_idname = 'PovrayAmbientNode'
    bl_label = 'Ambient'
    bl_icon = 'SOUND'

    def init(self, context):

        self.inputs.new('PovraySocketColor', "Ambient")

        self.outputs.new('NodeSocketVector', "Ambient")

    def draw_label(self):
        return "Ambient"

class PovrayIridescenceNode(Node, ObjectNodeTree):
    '''Iridescence'''
    bl_idname = 'PovrayIridescenceNode'
    bl_label = 'Iridescence'
    bl_icon = 'SOUND'

    def init(self, context):

        amount=self.inputs.new('NodeSocketFloat', "Amount")
        amount.default_value=0.25
        thickness=self.inputs.new('NodeSocketFloat', "Thickness")
        thickness.default_value=1
        self.inputs.new('NodeSocketFloat', "Turbulence")

        self.outputs.new('NodeSocketVector', "Iridescence")

    def draw_label(self):
        return "Iridescence"

class PovraySubsurfaceNode(Node, ObjectNodeTree):
    '''Subsurface'''
    bl_idname = 'PovraySubsurfaceNode'
    bl_label = 'Subsurface'
    bl_icon = 'SOUND'

    def init(self, context):

        translucency=self.inputs.new('NodeSocketColor', "Translucency")
        translucency.default_value=(0,0,0,1)
        energy=self.inputs.new('PovraySocketInt_0_256', "Energy")
        energy.default_value=20
        self.outputs.new('NodeSocketVector', "Translucency")

    def draw_buttons(self, context, layout):
        scene=context.scene
        layout.prop(scene.pov, "sslt_enable",text="SSLT")


    def draw_buttons_ext(self, context, layout):
        scene=context.scene
        layout.prop(scene.pov, "sslt_enable",text="SSLT")

    def draw_label(self):
        return "Subsurface"

#####################################################################################################

class PovrayMappingNode(Node, ObjectNodeTree):
    '''Mapping'''
    bl_idname = 'PovrayMappingNode'
    bl_label = 'Mapping'
    bl_icon = 'SOUND'

    warp_type: EnumProperty(
            name="Warp Types",
            description="Select the type of warp",
            items=( ('cubic', "Cubic", ""),  ('cylindrical', "Cylindrical", ""),('planar', "Planar", ""),
                    ('spherical', "Spherical", ""),('toroidal', "Toroidal", ""),
                    ('uv_mapping', "UV", ""),
                    ('NONE', "None", "No indentation")),
            default='NONE')

    warp_orientation: EnumProperty(
            name="Warp Orientation",
            description="Select the orientation of warp",
            items=(('x', "X", ""), ('y', "Y", ""), ('z', "Z", "")),
            default='y')

    warp_dist_exp: FloatProperty(
            name="Distance exponent",
            description="Distance exponent",
            min=0.0, max=100.0, default=1.0)

    warp_tor_major_radius: FloatProperty(
            name="Major radius",
            description="Torus is distance from major radius",
            min=0.0, max=5.0, default=1.0)

    def init(self, context):
        self.outputs.new('NodeSocketVector', "Mapping")

    def draw_buttons(self, context, layout):

        column=layout.column()
        column.prop(self,"warp_type",text="Warp type")
        if self.warp_type in {'toroidal','spherical','cylindrical','planar'}:
            column.prop(self,"warp_orientation",text="Orientation")
            column.prop(self,"warp_dist_exp",text="Exponent")
        if self.warp_type=='toroidal':
            column.prop(self,"warp_tor_major_radius",text="Major R")

    def draw_buttons_ext(self, context, layout):

        column=layout.column()
        column.prop(self,"warp_type",text="Warp type")
        if self.warp_type in {'toroidal','spherical','cylindrical','planar'}:
            column.prop(self,"warp_orientation",text="Orientation")
            column.prop(self,"warp_dist_exp",text="Exponent")
        if self.warp_type=='toroidal':
            column.prop(self,"warp_tor_major_radius",text="Major R")

    def draw_label(self):
        return "Mapping"

class PovrayMultiplyNode(Node, ObjectNodeTree):
    '''Multiply'''
    bl_idname = 'PovrayMultiplyNode'
    bl_label = 'Multiply'
    bl_icon = 'SOUND'

    amount_x : FloatProperty(
            name="X",
            description="Number of repeats",
            min=1.0, max=10000.0, default=1.0)

    amount_y : FloatProperty(
            name="Y",
            description="Number of repeats",
            min=1.0, max=10000.0, default=1.0)

    amount_z : FloatProperty(
            name="Z",
            description="Number of repeats",
            min=1.0, max=10000.0, default=1.0)


    def init(self, context):
        self.outputs.new('NodeSocketVector', "Amount")

    def draw_buttons(self, context, layout):

        column=layout.column()
        column.label(text="Amount")
        row=column.row(align=True)
        row.prop(self,"amount_x")
        row.prop(self,"amount_y")
        row.prop(self,"amount_z")

    def draw_buttons_ext(self, context, layout):

        column=layout.column()
        column.label(text="Amount")
        row=column.row(align=True)
        row.prop(self,"amount_x")
        row.prop(self,"amount_y")
        row.prop(self,"amount_z")

    def draw_label(self):
        return "Multiply"

class PovrayTransformNode(Node, ObjectNodeTree):
    '''Transform'''
    bl_idname = 'PovrayTransformNode'
    bl_label = 'Transform'
    bl_icon = 'SOUND'

    def init(self, context):

        self.inputs.new('PovraySocketFloatUnlimited', "Translate x")
        self.inputs.new('PovraySocketFloatUnlimited', "Translate y")
        self.inputs.new('PovraySocketFloatUnlimited', "Translate z")
        self.inputs.new('PovraySocketFloatUnlimited', "Rotate x")
        self.inputs.new('PovraySocketFloatUnlimited', "Rotate y")
        self.inputs.new('PovraySocketFloatUnlimited', "Rotate z")
        sX = self.inputs.new('PovraySocketFloatUnlimited', "Scale x")
        sX.default_value = 1.0
        sY = self.inputs.new('PovraySocketFloatUnlimited', "Scale y")
        sY.default_value = 1.0
        sZ = self.inputs.new('PovraySocketFloatUnlimited', "Scale z")
        sZ.default_value = 1.0

        self.outputs.new('NodeSocketVector', "Transform")

    def draw_label(self):
        return "Transform"

class PovrayValueNode(Node, ObjectNodeTree):
    '''Value'''
    bl_idname = 'PovrayValueNode'
    bl_label = 'Value'
    bl_icon = 'SOUND'

    def init(self, context):

        self.outputs.new('PovraySocketUniversal', "Value")

    def draw_label(self):
        return "Value"

class PovrayModifierNode(Node, ObjectNodeTree):
    '''Modifier'''
    bl_idname = 'PovrayModifierNode'
    bl_label = 'Modifier'
    bl_icon = 'SOUND'

    def init(self, context):

        turb_x=self.inputs.new('PovraySocketFloat_0_10', "Turb X")
        turb_x.default_value=0.1
        turb_y=self.inputs.new('PovraySocketFloat_0_10', "Turb Y")
        turb_y.default_value=0.1
        turb_z=self.inputs.new('PovraySocketFloat_0_10', "Turb Z")
        turb_z.default_value=0.1
        octaves=self.inputs.new('PovraySocketInt_1_9', "Octaves")
        octaves.default_value=1
        lambat=self.inputs.new('PovraySocketFloat_0_10', "Lambda")
        lambat.default_value=2.0
        omega=self.inputs.new('PovraySocketFloat_0_10', "Omega")
        omega.default_value=0.5
        freq=self.inputs.new('PovraySocketFloat_0_10', "Frequency")
        freq.default_value=2.0
        self.inputs.new('PovraySocketFloat_0_10', "Phase")

        self.outputs.new('NodeSocketVector', "Modifier")

    def draw_label(self):
        return "Modifier"

class PovrayPigmentNode(Node, ObjectNodeTree):
    '''Pigment'''
    bl_idname = 'PovrayPigmentNode'
    bl_label = 'Color'

    def init(self, context):

        color = self.inputs.new('PovraySocketColor', "Color")
        color.default_value = (1,1,1)
        povfilter = self.inputs.new('PovraySocketFloat_0_1', "Filter")
        transmit = self.inputs.new('PovraySocketFloat_0_1', "Transmit")
        self.outputs.new('NodeSocketColor', "Pigment")

    def draw_label(self):
        return "Color"

class PovrayColorImageNode(Node, ObjectNodeTree):
    '''ColorImage'''
    bl_idname = 'PovrayColorImageNode'
    bl_label = 'Image map'

    map_type: bpy.props.EnumProperty(
            name="Map type",
            description="",
            items=( ('uv_mapping', "UV", ""),
                    ('0', "Planar", "Default planar mapping"),
                    ('1', "Spherical", "Spherical mapping"),
                    ('2', "Cylindrical", "Cylindrical mapping"),
                    ('5', "Torroidal", "Torus or donut shaped mapping")),
            default='0')
    image: StringProperty(maxlen=1024) # , subtype="FILE_PATH"
    interpolate: EnumProperty(
            name="Interpolate",
            description="Adding the interpolate keyword can smooth the jagged look of a bitmap",
            items=(
                ('2', "Bilinear", "Gives bilinear interpolation"),
                ('4', "Normalized", "Gives normalized distance"),
            ),
            default='2')
    premultiplied: BoolProperty(default=False)
    once: BoolProperty(description="Not to repeat", default=False)

    def init(self, context):

        gamma=self.inputs.new('PovraySocketFloat_000001_10', "Gamma")
        gamma.default_value=2.0
        transmit=self.inputs.new('PovraySocketFloat_0_1', "Transmit")
        povfilter=self.inputs.new('PovraySocketFloat_0_1', "Filter")
        mapping=self.inputs.new('NodeSocketVector', "Mapping")
        mapping.hide_value=True
        transform=self.inputs.new('NodeSocketVector', "Transform")
        transform.hide_value=True
        modifier=self.inputs.new('NodeSocketVector', "Modifier")
        modifier.hide_value=True

        self.outputs.new('NodeSocketColor', "Pigment")

    def draw_buttons(self, context, layout):

        column=layout.column()
        im=None
        for image in bpy.data.images:
            if image.name == self.image:
                im=image
        split = column.split(factor=0.8,align=True)
        split.prop_search(self,"image",context.blend_data,"images",text="")
        split.operator("pov.imageopen",text="",icon="FILEBROWSER")
        if im is not None:
            column.prop(im,"source",text="")
        column.prop(self,"map_type",text="")
        column.prop(self,"interpolate",text="")
        row=column.row()
        row.prop(self,"premultiplied",text="Premul")
        row.prop(self,"once",text="Once")

    def draw_buttons_ext(self, context, layout):

        column=layout.column()
        im=None
        for image in bpy.data.images:
            if image.name == self.image:
                im=image
        split = column.split(factor=0.8,align=True)
        split.prop_search(self,"image",context.blend_data,"images",text="")
        split.operator("pov.imageopen",text="",icon="FILEBROWSER")
        if im is not None:
            column.prop(im,"source",text="")
        column.prop(self,"map_type",text="")
        column.prop(self,"interpolate",text="")
        row=column.row()
        row.prop(self,"premultiplied",text="Premul")
        row.prop(self,"once",text="Once")

    def draw_label(self):
        return "Image map"

class PovrayBumpMapNode(Node, ObjectNodeTree):
    '''BumpMap'''
    bl_idname = 'PovrayBumpMapNode'
    bl_label = 'Bump map'
    bl_icon = 'SOUND'

    map_type : bpy.props.EnumProperty(
            name="Map type",
            description="",
            items=(
                ('uv_mapping', "UV", ""),
                ('0', "Planar", "Default planar mapping"),
                ('1', "Spherical", "Spherical mapping"),
                ('2', "Cylindrical", "Cylindrical mapping"),
                ('5', "Torroidal", "Torus or donut shaped mapping")
            ),
            default='0')
    image : StringProperty(maxlen=1024) # , subtype="FILE_PATH"
    interpolate : EnumProperty(
            name="Interpolate",
            description="Adding the interpolate keyword can smooth the jagged look of a bitmap",
            items=(
                ('2', "Bilinear", "Gives bilinear interpolation"),
                ('4', "Normalized", "Gives normalized distance"),
            ),
            default='2')
    once : BoolProperty(description="Not to repeat", default=False)

    def init(self, context):

        self.inputs.new('PovraySocketFloat_0_10', "Normal")
        mapping=self.inputs.new('NodeSocketVector', "Mapping")
        mapping.hide_value=True
        transform=self.inputs.new('NodeSocketVector', "Transform")
        transform.hide_value=True
        modifier=self.inputs.new('NodeSocketVector', "Modifier")
        modifier.hide_value=True

        normal=self.outputs.new('NodeSocketFloat', "Normal")
        normal.hide_value=True

    def draw_buttons(self, context, layout):

        column=layout.column()
        im=None
        for image in bpy.data.images:
            if image.name == self.image:
                im=image
        split = column.split(percentage=0.8,align=True)
        split.prop_search(self,"image",context.blend_data,"images",text="")
        split.operator("pov.imageopen",text="",icon="FILEBROWSER")
        if im is not None:
            column.prop(im,"source",text="")
        column.prop(self,"map_type",text="")
        column.prop(self,"interpolate",text="")
        column.prop(self,"once",text="Once")

    def draw_buttons_ext(self, context, layout):

        column=layout.column()
        im=None
        for image in bpy.data.images:
            if image.name == self.image:
                im=image
        split = column.split(percentage=0.8,align=True)
        split.prop_search(self,"image",context.blend_data,"images",text="")
        split.operator("pov.imageopen",text="",icon="FILEBROWSER")
        if im is not None:
            column.prop(im,"source",text="")
        column.prop(self,"map_type",text="")
        column.prop(self,"interpolate",text="")
        column.prop(self,"once",text="Once")

    def draw_label(self):
        return "Bump Map"

class PovrayImagePatternNode(Node, ObjectNodeTree):
    '''ImagePattern'''
    bl_idname = 'PovrayImagePatternNode'
    bl_label = 'Image pattern'
    bl_icon = 'SOUND'

    map_type: bpy.props.EnumProperty(
            name="Map type",
            description="",
            items=(
                ('uv_mapping', "UV", ""),
                ('0', "Planar", "Default planar mapping"),
                ('1', "Spherical", "Spherical mapping"),
                ('2', "Cylindrical", "Cylindrical mapping"),
                ('5', "Torroidal", "Torus or donut shaped mapping"),
            ),
            default='0')
    image: StringProperty(maxlen=1024) # , subtype="FILE_PATH"
    interpolate: EnumProperty(
            name="Interpolate",
            description="Adding the interpolate keyword can smooth the jagged look of a bitmap",
            items=(
                ('2', "Bilinear", "Gives bilinear interpolation"),
                ('4', "Normalized", "Gives normalized distance"),
            ),
            default='2')
    premultiplied: BoolProperty(default=False)
    once: BoolProperty(description="Not to repeat", default=False)
    use_alpha: BoolProperty(default=True)
    def init(self, context):

        gamma=self.inputs.new('PovraySocketFloat_000001_10', "Gamma")
        gamma.default_value=2.0

        self.outputs.new('PovraySocketPattern', "Pattern")

    def draw_buttons(self, context, layout):

        column=layout.column()
        im=None
        for image in bpy.data.images:
            if image.name == self.image:
                im=image
        split = column.split(factor=0.8,align=True)
        split.prop_search(self,"image",context.blend_data,"images",text="")
        split.operator("pov.imageopen",text="",icon="FILEBROWSER")
        if im is not None:
            column.prop(im,"source",text="")
        column.prop(self,"map_type",text="")
        column.prop(self,"interpolate",text="")
        row=column.row()
        row.prop(self,"premultiplied",text="Premul")
        row.prop(self,"once",text="Once")
        column.prop(self,"use_alpha",text="Use alpha")

    def draw_buttons_ext(self, context, layout):

        column=layout.column()
        im=None
        for image in bpy.data.images:
            if image.name == self.image:
                im=image
        split = column.split(factor=0.8,align=True)
        split.prop_search(self,"image",context.blend_data,"images",text="")
        split.operator("pov.imageopen",text="",icon="FILEBROWSER")
        if im is not None:
            column.prop(im,"source",text="")
        column.prop(self,"map_type",text="")
        column.prop(self,"interpolate",text="")
        row=column.row()
        row.prop(self,"premultiplied",text="Premul")
        row.prop(self,"once",text="Once")

    def draw_label(self):
        return "Image pattern"

class ShaderPatternNode(Node, ObjectNodeTree):
    '''Pattern'''
    bl_idname = 'ShaderPatternNode'
    bl_label = 'Other patterns'

    pattern : EnumProperty(
            name="Pattern",
            description="Agate, Crackle, Gradient, Pavement, Spiral, Tiling",
            items=(('agate', "Agate", ""),('crackle', "Crackle", ""),('gradient', "Gradient", ""),
                   ('pavement', "Pavement", ""),
                   ('spiral1', "Spiral 1", ""),
                   ('spiral2', "Spiral 2", ""),
                   ('tiling', "Tiling", "")),
            default='agate')

    agate_turb : FloatProperty(
            name="Agate turb",
            description="Agate turbulence",
            min=0.0, max=100.0, default=0.5)

    crackle_form_x : FloatProperty(
            name="X",
            description="Form vector X",
            min=-150.0, max=150.0, default=-1)

    crackle_form_y : FloatProperty(
            name="Y",
            description="Form vector Y",
            min=-150.0, max=150.0, default=1)

    crackle_form_z : FloatProperty(
            name="Z",
            description="Form vector Z",
            min=-150.0, max=150.0, default=0)

    crackle_metric : FloatProperty(
            name="Metric",
            description="Crackle metric",
            min=0.0, max=150.0, default=1)

    crackle_solid : BoolProperty(
            name="Solid",
            description="Crackle solid",
            default=False)

    spiral_arms : FloatProperty(
            name="Number",
            description="",
            min=0.0, max=256.0, default=2.0)

    tiling_number : IntProperty(
            name="Number",
            description="",
            min=1, max=27, default=1)

    gradient_orient : EnumProperty(
            name="Orient",
            description="",
            items=(('x', "X", ""),
                   ('y', "Y", ""),
                   ('z', "Z", "")),
            default='x')

    def init(self, context):

        pat = self.outputs.new('PovraySocketPattern', "Pattern")

    def draw_buttons(self, context, layout):

        layout.prop(self, "pattern",text="")
        if self.pattern=='agate':
            layout.prop(self, "agate_turb")
        if self.pattern=='crackle':
            layout.prop(self, "crackle_metric")
            layout.prop(self, "crackle_solid")
            layout.label(text="Form:")
            layout.prop(self, "crackle_form_x")
            layout.prop(self, "crackle_form_y")
            layout.prop(self, "crackle_form_z")
        if self.pattern in {"spiral1","spiral2"}:
            layout.prop(self, "spiral_arms")
        if self.pattern in {'tiling'}:
            layout.prop(self, "tiling_number")
        if self.pattern in {'gradient'}:
            layout.prop(self, "gradient_orient")
    def draw_buttons_ext(self, context, layout):
        pass

    def draw_label(self):
        return "Other patterns"

class ShaderTextureMapNode(Node, ObjectNodeTree):
    '''Texture Map'''
    bl_idname = 'ShaderTextureMapNode'
    bl_label = 'Texture map'

    brick_size_x: FloatProperty(
            name="X",
            description="",
            min=0.0000, max=1.0000, default=0.2500)

    brick_size_y: FloatProperty(
            name="Y",
            description="",
            min=0.0000, max=1.0000, default=0.0525)

    brick_size_z: FloatProperty(
            name="Z",
            description="",
            min=0.0000, max=1.0000, default=0.1250)

    brick_mortar: FloatProperty(
            name="Mortar",
            description="Mortar",
            min=0.000, max=1.500, default=0.01)

    def init(self, context):
        mat = bpy.context.object.active_material
        self.inputs.new('PovraySocketPattern', "")
        color = self.inputs.new('NodeSocketColor', "Color ramp")
        color.hide_value = True
        for i in range(0,4):
            transform=self.inputs.new('PovraySocketTransform', "Transform")
            transform.hide_value=True
        number = mat.pov.inputs_number
        for i in range(number):
            self.inputs.new('PovraySocketTexture', "%s"%i)


        self.outputs.new('PovraySocketTexture', "Texture")

    def draw_buttons(self, context, layout):

        if self.inputs[0].default_value =='brick':
            layout.prop(self, "brick_mortar")
            layout.label(text="Brick size:")
            layout.prop(self, "brick_size_x")
            layout.prop(self, "brick_size_y")
            layout.prop(self, "brick_size_z")

    def draw_buttons_ext(self, context, layout):

        if self.inputs[0].default_value =='brick':
            layout.prop(self, "brick_mortar")
            layout.label(text="Brick size:")
            layout.prop(self, "brick_size_x")
            layout.prop(self, "brick_size_y")
            layout.prop(self, "brick_size_z")

    def draw_label(self):
        return "Texture map"


class ShaderNormalMapNode(Node, ObjectNodeTree):
    '''Normal Map'''
    bl_idname = 'ShaderNormalMapNode'
    bl_label = 'Normal map'

    brick_size_x : FloatProperty(
            name="X",
            description="",
            min=0.0000, max=1.0000, default=0.2500)

    brick_size_y : FloatProperty(
            name="Y",
            description="",
            min=0.0000, max=1.0000, default=0.0525)

    brick_size_z : FloatProperty(
            name="Z",
            description="",
            min=0.0000, max=1.0000, default=0.1250)

    brick_mortar : FloatProperty(
            name="Mortar",
            description="Mortar",
            min=0.000, max=1.500, default=0.01)

    def init(self, context):
        self.inputs.new('PovraySocketPattern', "")
        normal = self.inputs.new('PovraySocketFloat_10', "Normal")
        slope = self.inputs.new('PovraySocketMap', "Slope map")
        for i in range(0,4):
            transform=self.inputs.new('PovraySocketTransform', "Transform")
            transform.hide_value=True
        self.outputs.new('PovraySocketNormal', "Normal")

    def draw_buttons(self, context, layout):
        #for i, inp in enumerate(self.inputs):

        if self.inputs[0].default_value =='brick':
            layout.prop(self, "brick_mortar")
            layout.label(text="Brick size:")
            layout.prop(self, "brick_size_x")
            layout.prop(self, "brick_size_y")
            layout.prop(self, "brick_size_z")

    def draw_buttons_ext(self, context, layout):

        if self.inputs[0].default_value =='brick':
            layout.prop(self, "brick_mortar")
            layout.label(text="Brick size:")
            layout.prop(self, "brick_size_x")
            layout.prop(self, "brick_size_y")
            layout.prop(self, "brick_size_z")

    def draw_label(self):
        return "Normal map"

class ShaderNormalMapEntryNode(Node, ObjectNodeTree):
    '''Normal Map Entry'''
    bl_idname = 'ShaderNormalMapEntryNode'
    bl_label = 'Normal map entry'

    def init(self, context):
        self.inputs.new('PovraySocketFloat_0_1', "Stop")
        self.inputs.new('PovraySocketFloat_0_1', "Gray")
    def draw_label(self):
        return "Normal map entry"

class IsoPropsNode(Node, CompositorNodeTree):
    '''ISO Props'''
    bl_idname = 'IsoPropsNode'
    bl_label = 'Iso'
    node_label : StringProperty(maxlen=1024)
    def init(self, context):
        ob = bpy.context.object
        self.node_label = ob.name
        textName = ob.pov.function_text
        if textName:
            text = bpy.data.texts[textName]
            for line in text.lines:
                split = line.body.split()
                if split[0] == "#declare":
                    socket = self.inputs.new('NodeSocketFloat', "%s"%split[1])
                    value = split[3].split(";")
                    value = value[0]
                    socket.default_value=float(value)
    def draw_label(self):
        return self.node_label

class PovrayFogNode(Node, CompositorNodeTree):
    '''Fog settings'''
    bl_idname = 'PovrayFogNode'
    bl_label = 'Fog'
    def init(self, context):
        color=self.inputs.new('NodeSocketColor', "Color")
        color.default_value=(0.7,0.7,0.7,0.25)
        self.inputs.new('PovraySocketFloat_0_1', "Filter")
        distance = self.inputs.new('NodeSocketInt', "Distance")
        distance.default_value=150
        self.inputs.new('NodeSocketBool', "Ground")
        fog_offset=self.inputs.new('NodeSocketFloat', "Offset")
        fog_alt=self.inputs.new('NodeSocketFloat', "Altitude")
        turb = self.inputs.new('NodeSocketVector', "Turbulence")
        turb_depth=self.inputs.new('PovraySocketFloat_0_10', "Depth")
        turb_depth.default_value=0.5
        octaves=self.inputs.new('PovraySocketInt_1_9', "Octaves")
        octaves.default_value=5
        lambdat=self.inputs.new('PovraySocketFloat_0_10', "Lambda")
        lambdat.default_value=1.25
        omega=self.inputs.new('PovraySocketFloat_0_10', "Omega")
        omega.default_value=0.35
        translate = self.inputs.new('NodeSocketVector', "Translate")
        rotate = self.inputs.new('NodeSocketVector', "Rotate")
        scale = self.inputs.new('NodeSocketVector', "Scale")
        scale.default_value=(1,1,1)
    def draw_label(self):
        return "Fog"

class PovraySlopeNode(Node, TextureNodeTree):
    '''Output'''
    bl_idname = 'PovraySlopeNode'
    bl_label = 'Slope Map'

    def init(self, context):
        self.use_custom_color = True
        self.color = (0,0.2,0)
        slope = self.inputs.new('PovraySocketSlope', "0")
        slope = self.inputs.new('PovraySocketSlope', "1")
        slopemap = self.outputs.new('PovraySocketMap', "Slope map")
        output.hide_value = True
    def draw_buttons(self, context, layout):

        layout.operator("pov.nodeinputadd")
        row = layout.row()
        row.label(text='Value')
        row.label(text='Height')
        row.label(text='Slope')

    def draw_buttons_ext(self, context, layout):

        layout.operator("pov.nodeinputadd")
        row = layout.row()
        row.label(text='Value')
        row.label(text='Height')
        row.label(text='Slope')

    def draw_label(self):
        return "Slope Map"

######################################## Texture nodes ###############################
class TextureOutputNode(Node, TextureNodeTree):
    '''Output'''
    bl_idname = 'TextureOutputNode'
    bl_label = 'Color Map'

    def init(self, context):
        tex = bpy.context.object.active_material.active_texture
        num_sockets = int(tex.pov.density_lines/32)
        for i in range(num_sockets):
            color = self.inputs.new('NodeSocketColor', "%s"%i)
            color.hide_value = True

    def draw_buttons(self, context, layout):

        layout.label(text="Color Ramps:")

    def draw_label(self):
        return "Color Map"


##################################################################################
#################################Operators########################################
##################################################################################


class NODE_OT_iso_add(Operator):
    bl_idname = "pov.nodeisoadd"
    bl_label = "Create iso props"

    def execute(self, context):
        ob = bpy.context.object
        if bpy.context.scene.use_nodes == False:
            bpy.context.scene.use_nodes = True
        tree = bpy.context.scene.node_tree
        for node in tree.nodes:
            if node.bl_idname == "IsoPropsNode" and node.label == ob.name:
                tree.nodes.remove(node)
        isonode = tree.nodes.new('IsoPropsNode')
        isonode.location = (0,0)
        isonode.label = ob.name
        return {'FINISHED'}

class NODE_OT_map_create(Operator):
    bl_idname = "node.map_create"
    bl_label = "Create map"

    def execute(self, context):
        x = y = 0
        space = context.space_data
        tree = space.edit_tree
        for node in tree.nodes:
            if node.select == True:
                x,y = node.location
            node.select=False
        tmap = tree.nodes.new('ShaderTextureMapNode')
        tmap.location = (x - 200,y)
        return {'FINISHED'}

    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_props_dialog(self)

    def draw(self, context):
        layout = self.layout
        mat = context.object.active_material
        layout.prop(mat.pov,"inputs_number")

class NODE_OT_povray_node_texture_map_add(Operator):
    bl_idname = "pov.nodetexmapadd"
    bl_label = "Texture map"

    def execute(self, context):
        tree=bpy.context.object.active_material.node_tree
        tmap = tree.nodes.active
        bpy.context.object.active_material.node_tree.nodes.active=tmap
        el=tmap.color_ramp.elements.new(0.5)
        for el in tmap.color_ramp.elements:
            el.color=(0,0,0,1)
        for inp in tmap.inputs:
            tmap.inputs.remove(inp)
        for outp in tmap.outputs:
            tmap.outputs.remove(outp)
        pattern=tmap.inputs.new('NodeSocketVector', "Pattern")
        pattern.hide_value=True
        for i in range(0,3):
            tmap.inputs.new('NodeSocketColor', "Shader")
        tmap.outputs.new('NodeSocketShader', "BSDF")
        tmap.label="Texture Map"
        return {'FINISHED'}


class NODE_OT_povray_node_output_add(Operator):
    bl_idname = "pov.nodeoutputadd"
    bl_label = "Output"

    def execute(self, context):
        tree=bpy.context.object.active_material.node_tree
        tmap = tree.nodes.new('ShaderNodeOutputMaterial')
        bpy.context.object.active_material.node_tree.nodes.active=tmap
        for inp in tmap.inputs:
            tmap.inputs.remove(inp)
        tmap.inputs.new('NodeSocketShader', "Surface")
        tmap.label="Output"
        return {'FINISHED'}

class NODE_OT_povray_node_layered_add(Operator):
    bl_idname = "pov.nodelayeredadd"
    bl_label = "Layered material"

    def execute(self, context):
        tree=bpy.context.object.active_material.node_tree
        tmap = tree.nodes.new('ShaderNodeAddShader')
        bpy.context.object.active_material.node_tree.nodes.active=tmap
        tmap.label="Layered material"
        return {'FINISHED'}

class NODE_OT_povray_input_add(Operator):
    bl_idname = "pov.nodeinputadd"
    bl_label = "Add entry"

    def execute(self, context):
        node=bpy.context.object.active_material.node_tree.nodes.active
        if node.type in {'VALTORGB'}:
            number=1
            for inp in node.inputs:
                if inp.type=='SHADER':
                    number+=1
            node.inputs.new('NodeSocketShader', "%s"%number)
            els=node.color_ramp.elements
            pos1=els[len(els)-1].position
            pos2=els[len(els)-2].position
            pos=(pos1-pos2)/2+pos2
            el=els.new(pos)

        if node.bl_idname == 'PovraySlopeNode':
            number=len(node.inputs)
            node.inputs.new('PovraySocketSlope', "%s"%number)


        return {'FINISHED'}

class NODE_OT_povray_input_remove(Operator):
    bl_idname = "pov.nodeinputremove"
    bl_label = "Remove input"

    def execute(self, context):
        node=bpy.context.object.active_material.node_tree.nodes.active
        if node.type in {'VALTORGB','ADD_SHADER'}:
            number=len(node.inputs)-1
            if number > 5:
                inp=node.inputs[number]
                node.inputs.remove(inp)
                if node.type in {'VALTORGB'}:
                    els=node.color_ramp.elements
                    number=len(els)-2
                    el=els[number]
                    els.remove(el)
        return {'FINISHED'}

class NODE_OT_povray_image_open(Operator):
    bl_idname = "pov.imageopen"
    bl_label = "Open"

    filepath: StringProperty(
            name="File Path",
            description="Open image",
            maxlen=1024,
            subtype='FILE_PATH',
            )

    def invoke(self, context, event):
        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}

    def execute(self, context):
        im=bpy.data.images.load(self.filepath)
        node=context.object.active_material.node_tree.nodes.active
        node.image = im.name
        return {'FINISHED'}


# class TEXTURE_OT_povray_open_image(Operator):
    # bl_idname = "pov.openimage"
    # bl_label = "Open Image"

    # filepath = StringProperty(
            # name="File Path",
            # description="Open image",
            # maxlen=1024,
            # subtype='FILE_PATH',
            # )

    # def invoke(self, context, event):
        # context.window_manager.fileselect_add(self)
        # return {'RUNNING_MODAL'}

    # def execute(self, context):
        # im=bpy.data.images.load(self.filepath)
        # tex = context.texture
        # tex.pov.image = im.name
        # view_layer = context.view_layer
        # view_layer.update()
        # return {'FINISHED'}

class PovrayPatternNode(Operator):
    bl_idname = "pov.patternnode"
    bl_label  = "Pattern"

    add=True

    def execute(self, context):
        space = context.space_data
        tree = space.edit_tree
        for node in tree.nodes:
            node.select=False
        if self.add==True:
            tmap = tree.nodes.new('ShaderNodeValToRGB')
            tmap.label="Pattern"
            for inp in tmap.inputs:
                tmap.inputs.remove(inp)
            for outp in tmap.outputs:
                tmap.outputs.remove(outp)
            pattern = tmap.inputs.new('PovraySocketPattern', "Pattern")
            pattern.hide_value = True
            mapping=tmap.inputs.new('NodeSocketVector', "Mapping")
            mapping.hide_value=True
            transform=tmap.inputs.new('NodeSocketVector', "Transform")
            transform.hide_value=True
            modifier=tmap.inputs.new('NodeSocketVector', "Modifier")
            modifier.hide_value=True
            for i in range(0,2):
                tmap.inputs.new('NodeSocketShader', "%s"%(i+1))
            tmap.outputs.new('NodeSocketShader', "Material")
            tmap.outputs.new('NodeSocketColor', "Color")
            tree.nodes.active=tmap
            self.add=False
        aNode=tree.nodes.active
        aNode.select=True
        v2d = context.region.view2d
        x, y = v2d.region_to_view(self.x, self.y)
        aNode.location = (x, y)

    def modal(self, context, event):
        if event.type == 'MOUSEMOVE':
            self.x = event.mouse_region_x
            self.y = event.mouse_region_y
            self.execute(context)
            return {'RUNNING_MODAL'}
        elif event.type == 'LEFTMOUSE':
            return {'FINISHED'}
        elif event.type in ('RIGHTMOUSE', 'ESC'):
            return {'CANCELLED'}

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        context.window_manager.modal_handler_add(self)
        return {'RUNNING_MODAL'}

class UpdatePreviewMaterial(Operator):
    '''Operator update preview material'''
    bl_idname = "node.updatepreview"
    bl_label = "Update preview"

    def execute(self, context):
        scene=context.view_layer
        ob=context.object
        for obj in scene.objects:
            if obj != ob:
                scene.objects.active = ob
                break
        scene.objects.active = ob

    def modal(self, context, event):
        if event.type == 'RIGHTMOUSE':
            self.execute(context)
            return {'FINISHED'}
        return {'PASS_THROUGH'}

    def invoke(self, context, event):
        context.window_manager.modal_handler_add(self)
        return {'RUNNING_MODAL'}

class UpdatePreviewKey(Operator):
    '''Operator update preview keymap'''
    bl_idname = "wm.updatepreviewkey"
    bl_label = "Activate RMB"
    @classmethod
    def poll(cls, context):
        conf = context.window_manager.keyconfigs.active
        mapstr = "Node Editor"
        map = conf.keymaps[mapstr]
        try:
            map.keymap_items["node.updatepreview"]
            return False
        except:
            return True

    def execute(self, context):
        conf = context.window_manager.keyconfigs.active
        mapstr = "Node Editor"
        map = conf.keymaps[mapstr]
        map.keymap_items.new("node.updatepreview",type='RIGHTMOUSE',value="PRESS")
        return {'FINISHED'}

classes = (
    ObjectNodeTree,
    PovrayOutputNode,
    PovrayTextureNode,
    PovrayFinishNode,
    PovrayDiffuseNode,
    PovrayPhongNode,
    PovraySpecularNode,
    PovrayMirrorNode,
    PovrayAmbientNode,
    PovrayIridescenceNode,
    PovraySubsurfaceNode,
    PovrayMappingNode,
    PovrayMultiplyNode,
    PovrayTransformNode,
    PovrayValueNode,
    PovrayModifierNode,
    PovrayPigmentNode,
    PovrayColorImageNode,
    PovrayBumpMapNode,
    PovrayImagePatternNode,
    ShaderPatternNode,
    ShaderTextureMapNode,
    ShaderNormalMapNode,
    ShaderNormalMapEntryNode,
    IsoPropsNode,
    PovrayFogNode,
    PovraySlopeNode,
    TextureOutputNode,
    NODE_OT_iso_add,
    NODE_OT_map_create,
    NODE_OT_povray_node_texture_map_add,
    NODE_OT_povray_node_output_add,
    NODE_OT_povray_node_layered_add,
    NODE_OT_povray_input_add,
    NODE_OT_povray_input_remove,
    NODE_OT_povray_image_open,
    PovrayPatternNode,
    UpdatePreviewMaterial,
    UpdatePreviewKey,
)


def register():
    #from bpy.utils import register_class

    for cls in classes:
        register_class(cls)


def unregister():
    from bpy.utils import unregister_class

    for cls in classes:
        unregister_class(cls)
