# SPDX-FileCopyrightText: 2020-2024 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later

# To run all tests, use
# BLENDER_VERBOSE=1 ./bin/blender ../blender/tests/files/modeling/object_conversion.blend --python ../blender/tests/python/object_conversion.py -- --run-all-tests
# (that assumes the test is run from a build directory in the same directory as the source code)
import bpy
import os
import sys
import inspect

sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from modules.mesh_test import SpecMeshTest, OperatorSpecObjectMode, RunTest


class ConversionTypeTestHelper:
    def __init__(self, tests):
        self.tests = tests
        self._failed_tests_list = []

    def run_all_tests(self):
        """
        Run all tests in self.tests list. Displays all failed tests at bottom.
        """
        for _, each_test in enumerate(self.tests):
            test_name = each_test.test_name
            success = self.run_test(test_name)

            if not success:
                self._failed_tests_list.append(test_name)

        if len(self._failed_tests_list) != 0:
            print("\nFollowing tests failed: {}".format(self._failed_tests_list))

            blender_path = bpy.app.binary_path
            blend_path = bpy.data.filepath
            frame = inspect.stack()[1]
            module = inspect.getmodule(frame[0])
            python_path = module.__file__

            print("Run following command to open Blender and run the failing test:")
            print("{} {} --python {} -- {} {}"
                  .format(blender_path, blend_path, python_path, "--run-test", "<test_name>"))

            raise Exception("Tests {} failed".format(self._failed_tests_list))

    def run_test(self, test_name: str):
        """
        Run a single test from self.tests list.

        :arg test_name: int - name of test
        :return: bool - True if test passed, False otherwise.
        """
        case = None
        for index, each_test in enumerate(self.tests):
            if test_name == each_test.test_name:
                case = self.tests[index]
                break

        if case is None:
            raise Exception('No test called {} found!'.format(test_name))

        test = case
        print("Running test '{}'".format(test.test_name))

        test_object = bpy.data.objects[test.from_object]
        with bpy.context.temp_override(object=test_object, selected_objects=[test_object]):
            bpy.context.view_layer.objects.active = test_object

            selection = test_object.select_get()
            test_object.select_set(True)
            retval = bpy.ops.object.convert(target=test.to_type, keep_original=True)
            test_object.select_set(False)

            if retval != {'FINISHED'}:
                raise RuntimeError("Unexpected operator return value: {}".format(retval))

        resulting_type = bpy.context.view_layer.objects.active.type
        bpy.ops.object.delete()
        if resulting_type != test.resulting_type:
            raise RuntimeError(
                "Converted object does not match expected type.\nTest '{}': Converting '{}' to '{}' expecting '{}' got '{}'\n" .format(
                    test.test_name,
                    test.from_object,
                    test.to_type,
                    test.resulting_type,
                    resulting_type))

        print("Success\n")
        return True


class ConversionPair:
    def __init__(self, test_name, from_object, to_type, resulting_type):
        self.test_name = test_name
        self.from_object = from_object
        self.to_type = to_type
        self.resulting_type = resulting_type


def main():
    tests = [
        SpecMeshTest('Mesh 1', 'Cube', 'Cube_Mesh',
                     [OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
        SpecMeshTest('Mesh 2', 'CubeWithEdges', 'CubeWithEdges_Mesh',
                     [OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
        SpecMeshTest('Mesh 3', 'Plane', 'Plane_Mesh',
                     [OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
        SpecMeshTest('Mesh 4', 'HollowPlane', 'HollowPlane_Mesh',
                     [OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
        SpecMeshTest('Mesh 5', 'Suzanne', 'Suzanne_Mesh',
                     [OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
        SpecMeshTest('Mesh 6', 'BezierCircle', 'BezierCircle_Mesh',
                     [OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
        SpecMeshTest('Mesh 7', 'BezierCurve', 'BezierCurve_Mesh',
                     [OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
        SpecMeshTest('Mesh 8', 'Text', 'Text_Mesh',
                     [OperatorSpecObjectMode('convert', {'target': 'MESH'})]),
    ]

    type_tests = [
        ConversionPair('Legacy Curves 1', 'Cube', 'CURVE', 'MESH'),
        ConversionPair('Legacy Curves 2', 'CubeWithEdges', 'CURVE', 'CURVE'),
        ConversionPair('Legacy Curves 3', 'Plane', 'CURVE', 'CURVE'),
        ConversionPair('Legacy Curves 4', 'HollowPlane', 'CURVE', 'CURVE'),
        ConversionPair('Legacy Curves 5', 'Suzanne', 'CURVE', 'GREASEPENCIL'),
        ConversionPair('Legacy Curves 6', 'BezierCircle', 'CURVE', 'CURVE'),
        ConversionPair('Legacy Curves 7', 'BezierCurve', 'CURVE', 'CURVE'),
        ConversionPair('Legacy Curves 8', 'Text', 'CURVE', 'CURVE'),
        ConversionPair('Curves 1', 'Cube', 'CURVES', 'MESH'),
        ConversionPair('Curves 2', 'CubeWithEdges', 'CURVES', 'MESH'),
        ConversionPair('Curves 3', 'Plane', 'CURVES', 'MESH'),
        ConversionPair('Curves 4', 'HollowPlane', 'CURVES', 'MESH'),
        ConversionPair('Curves 5', 'Suzanne', 'CURVES', 'CURVES'),
        ConversionPair('Curves 6', 'BezierCircle', 'CURVES', 'CURVES'),
        ConversionPair('Curves 7', 'BezierCurve', 'CURVES', 'CURVES'),
        ConversionPair('Curves 8', 'Text', 'CURVES', 'CURVES'),
        ConversionPair('GreasePencil 1', 'Cube', 'GREASEPENCIL', 'GREASEPENCIL'),
        ConversionPair('GreasePencil 2', 'CubeWithEdges', 'GREASEPENCIL', 'GREASEPENCIL'),
        ConversionPair('GreasePencil 3', 'Plane', 'GREASEPENCIL', 'GREASEPENCIL'),
        ConversionPair('GreasePencil 4', 'HollowPlane', 'GREASEPENCIL', 'GREASEPENCIL'),
        ConversionPair('GreasePencil 5', 'Suzanne', 'GREASEPENCIL', 'GREASEPENCIL'),
        ConversionPair('GreasePencil 6', 'BezierCircle', 'GREASEPENCIL', 'GREASEPENCIL'),
        ConversionPair('GreasePencil 7', 'BezierCurve', 'GREASEPENCIL', 'GREASEPENCIL'),
        ConversionPair('GreasePencil 8', 'Text', 'GREASEPENCIL', 'GREASEPENCIL'),
        ConversionPair('GreasePencil 9', 'Suzanne_Curves', 'GREASEPENCIL', 'GREASEPENCIL'),
        ConversionPair('GreasePencil 10', 'BezierCircle_Curves', 'GREASEPENCIL', 'GREASEPENCIL'),
        ConversionPair('GreasePencil 11', 'BezierCurve_Curves', 'GREASEPENCIL', 'GREASEPENCIL'),
    ]

    operator_test = RunTest(tests)
    all_type_tests = ConversionTypeTestHelper(type_tests)

    command = list(sys.argv)
    for i, cmd in enumerate(command):
        if cmd == "--run-all-tests":
            operator_test.do_compare = True
            operator_test.run_all_tests()
            all_type_tests.run_all_tests()
            break
        elif cmd == "--run-test":
            name = command[i + 1]
            operator_test.do_compare = False
            operator_test.run_test(name)
            break


if __name__ == "__main__":
    main()
