import os
import paraview.simple as pw
import json
import common


def read_config():
    userprofile = os.getenv("USERPROFILE")
    config_path = os.path.join(userprofile, ".cenos-INDUCTION/config.json")
    with open(config_path) as f:
        config = json.load(f)

    default = {
        "resolution": [1080, 1080],
        "framerate": 15,
        "background": "White",
        "fontsize": 72,
    }

    if not "postprocessing" in config:
        config["postprocessing"] = {"video": default}
    else:  # "postprocessing" exists but does not have "video" in it
        if not "video" in config["postprocessing"]:
            config["postprocessing"]["video"] = default

    # check if all keys are in config, and add it if not
    for key in default.keys():
        if key not in config["postprocessing"]["video"]:
            config["postprocessing"]["video"][key] = default[key]

    with open(config_path, "w") as write_file:
        json.dump(config, write_file)

    return config


def set_customization():
    config = read_config()

    resolution = config["postprocessing"]["video"]["resolution"]
    framerate = config["postprocessing"]["video"]["framerate"]
    background = config["postprocessing"]["video"]["background"]
    fontsize = config["postprocessing"]["video"]["fontsize"]

    return resolution, framerate, background, fontsize


def findStateFile(directory):
    for file in os.listdir(directory):
        if file.endswith(".pvsm"):
            return os.path.join(directory, file)


def gotoLastTimestep():
    animationScene1 = pw.GetAnimationScene()
    timeKeeper1 = pw.GetTimeKeeper()
    animationScene1.GoToLast()


def add_time_annotation(simulationData, timesteps, view, fontsize):
    caseName = pw.FindSource("Case Name")
    if caseName:
        pw.Hide(caseName, view)

    # Time annotation must be applied to simulationData, so it displays animation time (each frame not time step)
    pythonAnnotation1 = pw.PythonAnnotation(
        registrationName="PythonAnnotation1", Input=simulationData
    )
    # Freeze timer at the last simulationData timestep (for end freeze-frame)
    pythonAnnotation1.Expression = (
        f'"%.1fs" %time_value if time_value < {timesteps[-1]} else "{timesteps[-1]}s"'
    )
    annotateTime1Display = pw.Show(pythonAnnotation1, view, "TextSourceRepresentation")
    annotateTime1Display.FontSize = fontsize


def case_has_motion(last_params):
    for motion in last_params["motionList"]:
        if motion["isEnabled"] and motion["type"] in ["COMPLEX", "OLD_WORKFLOW"]:
            return True
    return False


def generate_video(view):
    """Export the view as an AVI video.

    There are two scenarios (for IH) for video export:
    1. Scanning - this will have fixed time step, and the simulationData cannot be temporally interpolated.
    2. Static - this can have both fixed and adaptive time steps. These cases are interpolated, so the animation will always be linear

    For both cases the framerate is around 15 fps, and ParaView by default snaps the data to the next time step.
    To acieve more intuitive representation, an "ExtractTimeSteps" is applied because it has "ApproximationMode = 'Previous Time Step'"

    A 1 second freeze-frame is added to the end of the animation,
    the time actually keeps running, however the timer annotation is frozen at the last time step
    """

    resolution, framerate, background, fontsize = set_customization()

    simulationData = pw.FindSource("Simulation Data")
    if not simulationData:
        simulationData = pw.FindSource("SimulationData")
    timesteps = simulationData.TimestepValues

    if not case_has_motion(common.LAST_PARAMS):
        # Temporal interpolation sadly got broken when using OCC for scanning,
        # so it is only used for static cases
        temporalInterpolator = pw.TemporalInterpolator(
            registrationName="TemporalInterpolator", Input=simulationData
        )
        # This just makes all the objects that were applied onto simulationData, be applied to the new block instead
        for i in range(len(pw.GetSources())):
            obj = list(pw.GetSources().items())[i][1]
            if hasattr(obj, "Input"):
                if (
                    type(obj.Input).__name__ == "EnSightReader"
                    and type(obj).__name__ != "TemporalInterpolator"
                ):
                    print(obj)
                    obj.Input = temporalInterpolator
    else:  # case has motion
        # Motion cases will have a fxied time step, as adaptive time step is forbidden
        # Because paraview "snaps" to the nearest time NEXT step,
        # we have to make the time steps snap to 'Previous Time Step' using ExtractTimeSteps

        extractTimeSteps1 = pw.ExtractTimeSteps(
            registrationName="ExtractTimeSteps1", Input=simulationData
        )
        # This just makes all the objects that were applied onto simulationData, be applied to the new block instead
        for i in range(len(pw.GetSources())):
            obj = list(pw.GetSources().items())[i][1]
            if hasattr(obj, "Input"):
                if (
                    type(obj.Input).__name__ == "EnSightReader"
                    and type(obj).__name__ != "ExtractTimeSteps"
                ):
                    obj.Input = extractTimeSteps1

        animationScene1 = pw.GetAnimationScene()
        animationScene1.UpdateAnimationUsingDataTimeSteps()
        extractTimeSteps1.SelectionMode = "Select Time Range"
        extractTimeSteps1.ApproximationMode = "Previous Time Step"

    animationScene1 = pw.GetAnimationScene()
    animationScene1.PlayMode = "Sequence"
    animationScene1.NumberOfFrames = round((timesteps[-1] - timesteps[0]) * framerate)
    animationScene1.EndTime = (
        timesteps[-1] + 1
    )  # Add 1 second of pause at the end of video

    # Transparency in config is treated as a color, but in paraview it is actaully a seperate flag,
    # should probabaly fix this, as transparency can be used with any color pallette
    transparent = 0
    if background == "Transparent":
        transparent = 1
        background = "White"

    view = pw.FindView("RenderView1")
    view.OrientationAxesVisibility = 0
    add_time_annotation(simulationData, timesteps, view, fontsize)

    pw.SaveAnimation(
        os.path.join(common.CASE_PATH, "video.avi"),
        view,
        ImageResolution=resolution,
        OverrideColorPalette=background + "Background",
        TransparentBackground=transparent,
        FrameRate=framerate,
        #  FrameWindow=[0, 15], # saves only the specified frames
        Quality="1",
    )  # 0 lowest, 2 highest
