#    This file is part of Korman.
#
#    Korman 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.
#
#    Korman 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 Korman.  If not, see <http://www.gnu.org/licenses/>.

import bpy
from bpy.props import *

import cProfile
from contextlib import contextmanager
import pstats

from ..exporter.etlight import LightBaker
from ..exporter.explosions import ExportError
from ..helpers import UiHelper
from ..korlib import ConsoleToggler

class _LightingOperator:
    @contextmanager
    def _oven(self, context):
        if context.scene.world is not None:
            verbose = context.scene.world.plasma_age.verbose
            console = context.scene.world.plasma_age.show_console
        else:
            verbose = False
            console = True
        with UiHelper(context), ConsoleToggler(console), LightBaker(verbose=verbose) as oven:
            yield oven

    @classmethod
    def poll(cls, context):
        if context.mode != "OBJECT":
            return False
        if context.object is not None:
            return context.scene.render.engine == "PLASMA_GAME"


class LightmapAutobakePreviewOperator(_LightingOperator, bpy.types.Operator):
    bl_idname = "object.plasma_lightmap_preview"
    bl_label = "Preview Lightmap"
    bl_description = "Preview Lighting"
    bl_options = {"INTERNAL"}

    final = BoolProperty(name="Use this lightmap for export")

    def __init__(self):
        super().__init__()

    def execute(self, context):
        with self._oven(context) as bake:
            if not self.final:
                bake.lightmap_name = "{}_LIGHTMAPGEN_PREVIEW.png"
                bake.lightmap_uvtex_name = "LIGHTMAPGEN_PREVIEW"
            bake.force = True
            bake.retain_lightmap_uvtex = self.final
            if not bake.bake_static_lighting([context.object,]):
                self.report({"WARNING"}, "No valid lights found to bake.")
                return {"FINISHED"}

        lightmap_mod = context.object.plasma_modifiers.lightmap
        if lightmap_mod.bake_lightmap:
            tex = bpy.data.textures.get("LIGHTMAPGEN_PREVIEW")
            if tex is None:
                tex = bpy.data.textures.new("LIGHTMAPGEN_PREVIEW", "IMAGE")
            tex.extension = "CLIP"
            image = bake.get_lightmap(context.object)
            tex.image = image
            if self.final:
                lightmap_mod.image = image
        else:
            for i in context.object.data.vertex_colors:
                i.active = i.name == bake.vcol_layer_name

        return {"FINISHED"}


class LightmapBakeMultiOperator(_LightingOperator, bpy.types.Operator):
    bl_idname = "object.plasma_lightmap_bake"
    bl_label = "Bake Lighting"
    bl_description = "Bake scene lighting to object(s)"

    bake_selection = BoolProperty(name="Bake Selection",
                                  description="Bake only the selected objects (else all objects)",
                                  options=set())

    def __init__(self):
        super().__init__()

    def execute(self, context):
        profile_me = False

        try:
            if profile_me:
                cProfile.runctx("self._run(context)", globals(), locals(), "bake_cProfile")
            else:
                self._run(context)
        except ExportError as error:
            self.report({"ERROR"}, str(error))
            return {"CANCELLED"}
        else:
            if profile_me:
                with open("bake_profile.log", "w") as out:
                    stats = pstats.Stats("bake_cProfile", stream=out)
                    stats = stats.sort_stats("time", "cumtime")
                    stats.print_stats()
        return {"FINISHED"}

    def _run(self, context):
        all_objects = context.selected_objects if self.bake_selection else context.scene.objects
        filtered_objects = [i for i in all_objects if i.type == "MESH" and i.plasma_object.enabled]

        with self._oven(context) as bake:
            bake.force = True
            if not bake.bake_static_lighting(filtered_objects):
                self.report({"WARNING"}, "Nothing was baked.")
                return {"FINISHED"}

        for i in filtered_objects:
            lightmap_mod = i.plasma_modifiers.lightmap
            if lightmap_mod.bake_lightmap:
                lightmap_mod.image = bake.get_lightmap(i)


class LightmapClearMultiOperator(_LightingOperator, bpy.types.Operator):
    bl_idname = "object.plasma_lightmap_clear"
    bl_label = "Clear Lighting"
    bl_description = "Clear baked lighting"

    clear_selection = BoolProperty(name="Clear Selection",
                                   description="Clear only the selected objects (else all objects)",
                                   options=set())

    def __init__(self):
        super().__init__()

    def _iter_lightmaps(self, objects):
        yield from filter(lambda x: x.type == "MESH" and x.plasma_modifiers.lightmap.bake_lightmap, objects)

    def _iter_vcols(self, objects):
        yield from filter(lambda x: x.type == "MESH" and not x.plasma_modifiers.lightmap.bake_lightmap, objects)

    def execute(self, context):
        all_objects = context.selected_objects if self.clear_selection else context.scene.objects

        for i in self._iter_lightmaps(all_objects):
            i.plasma_modifiers.lightmap.image = None

        for i in self._iter_vcols(all_objects):
            vcols = i.data.vertex_colors
            col_layer = vcols.get("autocolor")
            if col_layer is not None:
                vcols.remove(col_layer)
        return {"FINISHED"}


@bpy.app.handlers.persistent
def _toss_garbage(scene):
    """Removes all LIGHTMAPGEN and autocolor garbage before saving"""
    bpy_data = bpy.data
    tex = bpy_data.textures.get("LIGHTMAPGEN_PREVIEW")
    if tex is not None:
        bpy_data.textures.remove(tex)

    for i in bpy_data.images:
        if i.name.endswith("_LIGHTMAPGEN_PREVIEW.png"):
            bpy_data.images.remove(i)
    for i in bpy_data.meshes:
        uvtex = i.uv_textures.get("LIGHTMAPGEN_PREVIEW")
        if uvtex is not None:
            i.uv_textures.remove(uvtex)

# collects light baking garbage
bpy.app.handlers.save_pre.append(_toss_garbage)
