# render_profile.py
"""
Blender Python code to set parameters based on render profiles.

The purpose of the "Render Profiles" feature is to simplify setting up
Blender to render animation according to a small number of standardized, 
named profiles, instead of having to control each setting separately.

They're sort of like predefined radio buttons for your render settings.

I wrote this because I kept having to repeat the same steps to go from
quick "GL" or "Paint" renders at low frame rates to fully-configured
final renders, and I found the process was error-prone.

In particular, it was very easy to accidentally forget to change the render
filepath and have a previous render get overwritten! Or, alternatively, I
might forget to set things back up for a final render after I did a previz
animation.
"""

import bpy
import bpy, bpy.types, bpy.utils, bpy.props

from abx import ink_paint

from . import file_context


class RenderProfile(object):
    """
    A named set of render settings for Blender.
    
    The profile is designed to be defined by a dictionary of fields, typically
    loaded from a project YAML file (under the key 'render_profiles').
    
    Attributes:
        engine (str):
            Mandatory choice of engine. Some aliases are supported, but the
            standard values are: 'gl', meaning a setup for GL viewport
            rendering, or one 'bi'/'BLENDER_INTERNAL', 'cycles'/'CYCLES',
            or 'bge' / 'BLENDER_GAME' for rendering with the respective 
            engines. There is no support for Eevee, because this is a 2.7-only
            Add-on. It should be included in the port. No third-party engines
            are currently supported.
            
        fps (float):
            Frames-per-second.
            
        fps_skip (int):
            Frames to skip between rendered frames (effectively divides the
            frame rate).
            
        fps_divisor (float):
            This is the weird hack for specifying NTSC-compliant fps of 29.97
            by using 1.001 as a divisor, instead of 1.0. Avoid if you can!
            
        rendersize (int):
            Percentage size of defined pixel dimensions to render. Note that
            we don't support setting the pixel size directly. You should
            configure that in Blender, but you can use this feature to make
            a lower-resolution render.
            
        compress (int):
            Compression ratio for image formats that support it.
            
        format (str):
            Image or video output format.
            One of: 'PNG', 'JPG', 'EXR', 'AVI' or 'MKV'.
            Note that we don't support the full range of options, just some
            common ones for previz and final rendering.
            
        freestyle (bool):
            Whether to turn on Freestyle ink rendering.
            
        antialiasing_samples (str):
            Controlled by 'antialias' key, which can be a number: 5,8,11, or 16.
            Note that this attribute, which is used to directly set the value
            in Blender is a string, not an integer.
            
        use_antialiasing (bool):
            Controlled by 'antialias' key. Whether to turn on antialiasing.
            Any value other than 'False' or 'None' will turn it on.
            False turns it off. None leaves it as-is.
            
        motion_blur_samples (int):
            Controlled by 'motionblur' key, which can be a number determining
            the number of samples.
            
        use_motion_blur (bool):
            Controlled by 'motionblur' key. Any value other than False or None
            will turn on motion blur. A value of True turns it on without
            changing the samples. A value of False turns it off. None causes
            is to be left as-is.
            
        framedigits (int):
            The number of '#' characters to use in the render filename to
            indicate frame number. Only used if the format is an image stream.
            
        suffix (str):
            A string suffix placed after the base name, but before the frame
            number to indicate what profile was used for the render. This
            avoids accidentally overwriting renders made with other profiles.
            
    Note that these attributes are not intended to be manipulated directly
    by the user. The production designer is expected to define these
    profiles in the <project>.yaml file under the 'render_profiles' key,
    like this:
        
        render_profiles:
            previz:
                engine: gl
                suffix: MP
                fps: 30
                fps_skip: 6
                motionblur: False
                antialias: False
                freestyle: False
                rendersize: 50
                
    and so on. This is then loaded by ABX into a list of RenderProfile
    objects. Calling the RenderProfile.apply() method actually causes the
    settings to be made.
    """
    render_formats = {
        # VERY simplified and limited list of formats from Blender that we need:
        # <API 'format'>: (<bpy file format>, <filename extension>),
        'PNG':      ('PNG',  'png'),
        'JPG':      ('JPEG', 'jpg'),
        'EXR':      ('OPEN_EXR_MULTILAYER', 'exr'),
        'AVI':      ('AVI_JPEG', 'avi'),
        'MKV':      ('FFMPEG', 'mkv')
        }    
    
    engines = {
        'bi': 'BLENDER_RENDER',
        'BLENDER_RENDER': 'BLENDER_RENDER',
        'BI': 'BLENDER_RENDER',
        
        'cycles': 'CYCLES',
        'CYCLES': 'CYCLES',
        
        'bge':  'BLENDER_GAME',
        'BLENDER_GAME': 'BLENDER_GAME',
        'BGE': 'BLENDER_GAME',
        
        'gl': None,
        'GL': None
        }
    
    
    def __init__(self, fields):
        
        # Note:  Settings w/ value *None* are left unaltered
        #        That is, they remain whatever they were before
        #        If a setting isn't included in the fields, then
        #        the attribute will be *None*.
        
        if 'engine' not in fields:
            fields['engine'] = None
            
        if fields['engine']=='gl':
            self.viewport_render = True
            self.engine = None
        else:
            self.viewport_render = False
            
        if fields['engine'] in self.engines:
            self.engine = self.engines[fields['engine']]
        else:
            self.engine = None
            
        # Parameters which are stored as-is, without modification:
        self.fps      = 'fps'      in fields and int(fields['fps'])      or None
        self.fps_skip = 'fps_skip' in fields and int(fields['fps_skip']) or None
        self.fps_divisor = 'fps_divisor' in fields and float(fields['fps_divisor']) or None
        self.rendersize  = 'rendersize'  in fields and int(fields['rendersize']) or None
        self.compress    = 'compress'    in fields and int(fields['compress']) or None
        
        self.format   = 'format'   in fields and str(fields['format'])   or None
        
        self.freestyle = 'freestyle' in fields and bool(fields['freestyle']) or None
        
        self.antialiasing_samples = None
        self.use_antialiasing = None
        if 'antialias' in fields:
            if fields['antialias']:
                self.use_antialiasing = True
                if fields['antialias'] in (5,8,11,16):
                    self.antialiasing_samples = str(fields['antialias'])
            else:
                self.use_antialiasing = False
        
        self.use_motion_blur = None
        self.motion_blur_samples = None
        if 'motionblur' in fields:
            if fields['motionblur']:
                self.use_motion_blur = True
                if type(fields['motionblur'])==int:
                    self.motion_blur_samples = int(fields['motionblur'])
            else:
                self.use_motion_blur = False
                
        if 'framedigits' in fields:
            self.framedigits = fields['framedigits']
        else:
            self.framedigits = 5        
            
        if 'suffix' in fields:
            self.suffix = fields['suffix']
        else:
            self.suffix = ''        
            
    def apply(self, scene):
        """
        Apply the profile settings to the given scene.
        
        NOTE: in 0.2.6 this function isn't fully implemented, and the
        render filepath will not include the proper unit name.
        """
        if self.engine:         scene.render.engine = self.engine
        if self.fps:            scene.render.fps = self.fps
        if self.fps_skip:       scene.frame_step = self.fps_skip
        if self.fps_divisor:    scene.render.fps_base = self.fps_divisor
        if self.rendersize:     scene.render.resolution_percentage = self.rendersize
        if self.compress:       scene.render.image_settings.compression = self.compress
        
        if self.format:
            scene.render.image_settings.file_format = self.render_formats[self.format][0]
        
        if self.freestyle:      scene.render.use_freestyle = self.freestyle
        if self.use_antialiasing:
            scene.render.use_antialiasing = self.use_antialiasing
            
        if self.antialiasing_samples:
            scene.render.antialiasing_samples = self.antialiasing_samples
        if self.use_motion_blur:
            scene.render.use_motion_blur = self.use_motion_blur
            
        if self.motion_blur_samples:
            scene.render.motion_blur_samples = self.motion_blur_samples
            
        if self.format:
            # prefix = scene.name_context.render_path
            # prefix = BlendfileContext.name_contexts[scene.name_context].render_path
            prefix = 'path_to_render'  # We actually need to get this from NameContext
            if self.suffix:
                scene.render.filepath = (prefix + '-' + self.suffix + '-' +
                    'f'+('#'*self.framedigits) + '.' +
                    self.render_formats[self.format][1])
                
        

# def set_render_from_profile(scene, profile):
#     if 'engine' in profile:
#         if profile['engine'] == 'gl':
#             pass
#         elif profile['engine'] == 'bi':
#             scene.render.engine = 'BLENDER_RENDER'
#         elif profile['engine'] == 'cycles':
#             scene.render.engine = 'CYCLES'
#         elif profile['engine'] == 'bge':
#             scene.render.engine = 'BLENDER_GAME'
#
#     if 'fps' in profile:
#         scene.render.fps = profile['fps']
#
#     if 'fps_skip' in profile:
#         scene.frame_step = profile['fps_skip']
#
#     if 'format' in profile:
#         scene.render.image_settings.file_format = render_formats[profile['format']][0]
#
#     if 'freestyle' in profile:
#         scene.render.use_freestyle = profile['freestyle']
#
#     if 'antialias' in profile:
#         if profile['antialias']:
#             scene.render.use_antialiasing = True
#             if profile['antialias'] in (5,8,11,16):
#                 scene.render.antialiasing_samples = str(profile['antialias'])
#         else:
#             scene.render.use_antialiasing = False
#
#     if 'motionblur' in profile:
#         if profile['motionblur']:
#             scene.render.use_motion_blur = True
#             if type(profile['motionblur'])==int:
#                 scene.render.motion_blur_samples = profile['motionblur']
#         else:
#             scene.render.use_motion_blur = False
#
#     # Use Lunatics naming scheme for render target:
#     if 'framedigits' in profile:
#         framedigits = profile['framedigits']
#     else:
#         framedigits = 5
#
#     if 'suffix' in profile:
#         suffix = profile['suffix']
#     else:
#         suffix = ''
#
#     if 'format' in profile:
#         rdr_fmt = render_formats[profile['format']][0]
#         ext = render_formats[profile['format']][1]
#     else:
#         rdr_fmt = 'PNG'
#         ext = 'png'
#
#     path = ink_paint.LunaticsShot(scene).render_path(
#         suffix=suffix, framedigits=framedigits, ext=ext, rdr_fmt=rdr_fmt)
#
#     scene.render.filepath = path
    
        