__author__ = "Stefano"

import os
import substance_painter as sp
import substance_painter.textureset as ts
import substance_painter.layerstack as layerstack
import json
from . import parameters as gsg_params
from . import log as gsg_log

log = gsg_log.Log()

class MaterialManager:
    def __init__(self, in_path, in_triplanar=False):
        self.path = in_path
        self.triplanar = in_triplanar
        self.all_files = os.listdir(self.path)
        # List only files, not directories
        self.files = [f for f in self.all_files if os.path.isfile(os.path.join(self.path, f))]
        self.meta = {}

        self.maps_to_channels = {
        'anisotropyangle':     ts.ChannelType.Anisotropyangle,
        'anisotropylevel':     ts.ChannelType.Anisotropylevel,
        'ambientocclusion':    ts.ChannelType.AO,
        'basecolor':           ts.ChannelType.BaseColor,
        'coatcolor':           ts.ChannelType.CoatColor,
        'coatnormal':          ts.ChannelType.CoatNormal,
        'coatopacity':         ts.ChannelType.CoatOpacity,
        'coatroughness':       ts.ChannelType.CoatRoughness,
        'coatspecularlevel':   ts.ChannelType.CoatSpecularLevel,
        'height':              ts.ChannelType.Height,
        'indexofrefraction':   ts.ChannelType.Ior,
        'metallic':            ts.ChannelType.Metallic,
        'normal':              ts.ChannelType.Normal,
        'opacity':             ts.ChannelType.Opacity,
        'roughness':           ts.ChannelType.Roughness,
        'scatteringweight':    ts.ChannelType.Scattering,
        'sheencolor':          ts.ChannelType.SheenColor,
        'sheenopacity':        ts.ChannelType.SheenOpacity,
        'sheenroughness':      ts.ChannelType.SheenRoughness,
        'specularlevel':       ts.ChannelType.Specular,
        'specularedgecolor':   ts.ChannelType.SpecularEdgeColor
        }

        self.map_files = {}
        self.LookForMaps()

    def LookForMaps(self):
        gsgm_file = None
        for f in self.files:
            (base_name, ext) = os.path.splitext(f)

            if ext == '.tx':
                continue

            if gsgm_file is None and ext.lower() == '.gsgm':
                gsgm_file = f

            # check the if the file name contains a map name
            for map in self.maps_to_channels.keys():
                #check if the map name is at the end of the file name, after stripping out the extension
                if base_name.endswith(map):
                    self.map_files[map] = os.path.join(self.path, f)
                    break

        if gsgm_file:
            # log.debug('Found GSGM file ' + gsgm_file)
            full_gsgn_path = os.path.join(self.path, gsgm_file)

            with open(full_gsgn_path, mode='r', encoding='utf8') as f:
                self.meta = json.loads(f.read())
                self.gsgm_params = gsg_params.ShaderParams(self.meta, "standard_surface")
                # self.gsgm_params.Log()

    def Log(self):
        log.debug('FILES:')
        for f in self.files:
            log.debug(f'File = {f}')
        log.debug(f'MAP FILES:')
        for map in self.maps_to_channels.keys():
            if map in self.map_files.keys():
                log.debug(f'{map} + {self.map_files[map]}')

    def CreateLayer(self):
        if not sp.project.is_open():
            log.error('No active project. Please open a project or create a new one.')
            return
        
        # Check if the material subtype is supported
        if 'subtype' in self.meta:
            subtype = self.meta['subtype']
            if subtype in ('carpaint', 'neon'): # add all the unsupported subtypes here
                log.error(f'Material subtype not supported ({subtype})')
                return

        active_stack = ts.get_active_stack()        
        insert_position = layerstack.InsertPosition.from_textureset_stack(active_stack) 
        new_layer = layerstack.insert_fill(insert_position)

        if self.triplanar:
            new_layer.set_projection_mode(sp.layerstack.ProjectionMode.Triplanar)

        layer_name = os.path.basename(self.path)
        log.info(f'Creating fill layer from {layer_name}')

        new_layer.set_name(layer_name)
        new_layer_stack = new_layer.get_stack()

        # Set or create the channels out of the gsgm Arnold parameters
        # Note that for now the ones not read from the file (the defaults in standard_surface) are not set
        for arnold_name, values in self.gsgm_params.params.items():
            if arnold_name in self.gsgm_params.in_gsgm:
                try:
                    channel = values['channel']

                    # in gsgm, but not mapped to a channel ?
                    if channel == 0:
                        continue
                    
                    value = values['value']
                    is_color_or_vector = isinstance(value, dict)

                    if not new_layer_stack.has_channel(channel):
                        format = ts.ChannelFormat.sRGB8 if is_color_or_vector else ts.ChannelFormat.L8
                        new_layer_stack.add_channel(channel, format)

                    if is_color_or_vector:
                        if 'r' in value:
                            c = [value['r'], value['g'], value['b']]
                        elif 'x' in value:
                            c = [value['x'], value['y'], value['z']]
                    else:
                        c = [value, value, value]                        

                    log.debug(f'Setting channel {channel} to {value}')
                    new_layer.set_source(channel, sp.colormanagement.Color(c[0], c[1], c[2]))
                except Exception as e:
                    log.error(f'Error setting channel {channel} to {value}')
                    log.error(f'Error: {e}')
                    log.error(f'Arnold parameter: {arnold_name}, value = {value}')

        # Set or create the channels out of the texture maps 
        for map in self.maps_to_channels.keys():
            if map in self.map_files.keys():
                # The image exists, create a resource and set it as the source of the layer for that channel
                map_fullname = self.map_files[map]
                map_basename = os.path.basename(map_fullname)
                channel = self.maps_to_channels[map]

                # First check if the channel exists on the default layer we created, if not add it
                if not new_layer_stack.has_channel(channel):
                    log.info(f'Channel for {map_basename} not found, adding it')
                    new_layer_stack.add_channel(channel, ts.ChannelFormat.sRGB8)

                log.debug(f'Importing {map_basename}')
                new_resource = sp.resource.import_project_resource(map_fullname, sp.resource.Usage.TEXTURE)
                new_layer.set_source(channel, new_resource.identifier())

        layerstack.set_selected_nodes([new_layer])
        return new_layer.get_name()


def create_layer_from_path(path, in_triplanar=False):
    if not os.path.exists(path):
        log.error(path + ' does not exist')
        return
    
    mm = MaterialManager(path, in_triplanar)
    layer_name = mm.CreateLayer()
    return layer_name


# Auxiliary functions usable in remote mode

def delete_layer(in_layer_name):
    active_stack = ts.get_active_stack()
    stack_layers = layerstack.get_root_layer_nodes(active_stack)
    for layer in stack_layers:
        if layer.get_name() == in_layer_name:
            log.info(f'Deleting layer {in_layer_name}')
            layerstack.delete_node(layer)
            break


