'''

@author: Vijai Kumar Suriyababu

A simple script to update the lib3mf version number in all the necessary places.
Its run as follows

python lib3mf_version_update.py 2.3.1 2.3.2

First argument is current version number and the second one is new version number

It does the following
* Update version number in lib3mf.xml and generate bindings using ACT
* Patch the Python bindings
* Update the CMakeLists.txt
* Update all the docs, readme's and python, CPP examples and so on

'''

import os
import argparse
import re
import platform
import subprocess


def update_version_in_xml(file_path, old_version, new_version):
    with open(file_path, 'r', encoding='utf-8') as file:
        content = file.read()

    # Use regex to replace the old version with the new version
    updated_content = re.sub(
        fr'(<component[^>]*version="){old_version}(")',
        fr'\g<1>{new_version}\g<2>',
        content
    )

    with open(file_path, 'w', encoding='utf-8') as file:
        file.write(updated_content)

    print(f"Updated {file_path}")


def run_act_command():
    start_dir = os.getcwd()
    act_dir = os.path.join(start_dir, 'AutomaticComponentToolkit')

    # Determine the command based on the operating system and architecture
    os_type = platform.system()
    arch = platform.machine()

    if os_type == 'Darwin':
        if 'arm' in arch.lower():
            command = './act.arm.darwin lib3mf.xml -bindings ../Autogenerated/Bindings -interfaces ../Autogenerated/Source -suppresssubcomponents -suppresslicense -suppressstub -suppressexamples'
        else:
            command = './act.darwin lib3mf.xml -bindings ../Autogenerated/Bindings -interfaces ../Autogenerated/Source -suppresssubcomponents -suppresslicense -suppressstub -suppressexamples'
    elif os_type == 'Linux':
        if 'arm' in arch.lower():
            command = './act.arm.linux64 lib3mf.xml -bindings ../Autogenerated/Bindings -interfaces ../Autogenerated/Source -suppresssubcomponents -suppresslicense -suppressstub -suppressexamples'
        else:
            command = './act.linux64 lib3mf.xml -bindings ../Autogenerated/Bindings -interfaces ../Autogenerated/Source -suppresssubcomponents -suppresslicense -suppressstub -suppressexamples'
    elif os_type == 'Windows':
        command = 'act.win64.exe lib3mf.xml -bindings ..\\Autogenerated\\Bindings -interfaces ..\\Autogenerated\\Source -suppresssubcomponents -suppresslicense -suppressstub -suppressexamples'
    else:
        raise RuntimeError(f'Unsupported OS: {os_type}')

    try:
        # Change to the AutomaticComponentToolkit directory
        os.chdir(act_dir)

        # Execute the command
        subprocess.run(command, shell=True, check=True)
        print(f"Command executed successfully: {command}")
    except subprocess.CalledProcessError as e:
        print(f"Command failed with error: {e}")
    finally:
        # Change back to the original directory
        os.chdir(start_dir)


def patch_python_bindings():
    start_dir = os.getcwd()
    bindings_path = os.path.join(start_dir, 'Autogenerated', 'Bindings', 'Python', 'Lib3MF.py')

    search_line = r"\tNone = 0"
    new_line = r"\tBeamLatticeBallModeNone = 0"

    with open(bindings_path, 'r', encoding='utf-8') as file:
        content = file.read()

    # Use regex to replace the specific line
    updated_content = re.sub(search_line, new_line, content)

    with open(bindings_path, 'w', encoding='utf-8') as file:
        file.write(updated_content)

    print(f"Python bindings patched successfully: {bindings_path}")


def update_version_in_cmakelists(new_version):
    if '-' in new_version:
        version, prerelease = new_version.split('-', 1)
    else:
        version, prerelease = new_version, ""

    major, minor, micro = version.split('.')

    with open('CMakeLists.txt', 'r', encoding='utf-8') as file:
        content = file.read()

    content = re.sub(r'set\(LIB3MF_VERSION_MAJOR \d+\)', f'set(LIB3MF_VERSION_MAJOR {major})', content)
    content = re.sub(r'set\(LIB3MF_VERSION_MINOR \d+\)', f'set(LIB3MF_VERSION_MINOR {minor})', content)
    content = re.sub(r'set\(LIB3MF_VERSION_MICRO \d+\)', f'set(LIB3MF_VERSION_MICRO {micro})', content)
    content = re.sub(r'set\(LIB3MF_VERSION_PRERELEASE ".*"\)', f'set(LIB3MF_VERSION_PRERELEASE "{prerelease}")',
                     content)

    with open('CMakeLists.txt', 'w', encoding='utf-8') as file:
        file.write(content)

    print(f"Updated CMakeLists.txt")


def update_version_in_conf_py(file_path, old_version, new_version):
    with open(file_path, 'r', encoding='utf-8') as file:
        content = file.read()

    # Update the release version
    updated_content = re.sub(
        fr"release = 'v{old_version}'",
        fr"release = 'v{new_version}'",
        content
    )

    with open(file_path, 'w', encoding='utf-8') as file:
        file.write(updated_content)

    print(f"Updated {file_path}")


def update_version_in_index_rst(file_path, old_version, new_version):
    with open(file_path, 'r', encoding='utf-8') as file:
        content = file.read()

    # Replace all instances of the old version with the new version
    updated_content = content.replace(old_version, new_version)

    with open(file_path, 'w', encoding='utf-8') as file:
        file.write(updated_content)

    print(f"Updated {file_path}")


def update_version_in_readme_md(file_path, old_version, new_version):
    with open(file_path, 'r', encoding='utf-8') as file:
        content = file.read()

    # Update the PDF link version
    updated_content = re.sub(
        fr"lib3mf_v{old_version}\.pdf",
        fr"lib3mf_v{new_version}.pdf",
        content)

    with open(file_path, 'w', encoding='utf-8') as file:
        file.write(updated_content)

    print(f"Updated {file_path}")


def update_version_in_file(file_path, old_version, new_version):
    with open(file_path, 'r', encoding='utf-8') as file:
        content = file.read()

    # Replace all instances of the old version with the new version
    updated_content = content.replace(old_version, new_version)

    with open(file_path, 'w', encoding='utf-8') as file:
        file.write(updated_content)

    print(f"Updated {file_path}")


def main():
    parser = argparse.ArgumentParser(description='Update lib3mf version in multiple files.')
    parser.add_argument('old_version', type=str, help='The old version number to be replaced.')
    parser.add_argument('new_version', type=str, help='The new version number to update to.')
    args = parser.parse_args()
    old_version = args.old_version
    new_version = args.new_version

    # Get the current working directory
    start_dir = os.getcwd()

    # Update AutomaticComponentToolkit/lib3mf.xml
    xml_file_path = os.path.join(start_dir, 'AutomaticComponentToolkit', 'lib3mf.xml')
    update_version_in_xml(xml_file_path, old_version, new_version)

    # Run the act command
    run_act_command()

    # Patch the Python bindings
    patch_python_bindings()

    # Update CMakeLists.txt
    update_version_in_cmakelists(new_version)

    # Update Documentation/conf.py
    conf_py_path = os.path.join(start_dir, 'Documentation', 'conf.py')
    update_version_in_conf_py(conf_py_path, old_version, new_version)

    # Update Documentation/index.rst
    index_rst_path = os.path.join(start_dir, 'Documentation', 'index.rst')
    update_version_in_index_rst(index_rst_path, old_version, new_version)

    # Update SDK/Readme.md
    readme_md_path = os.path.join(start_dir, 'SDK', 'Readme.md')
    update_version_in_readme_md(readme_md_path, old_version, new_version)

    # Update README.md at the same location as this script
    readme_path = os.path.join(start_dir, 'README.md')
    update_version_in_readme_md(readme_path, old_version, new_version)

    # Define all additional file paths that need to be updated
    additional_file_paths = [
        os.path.join(start_dir, 'SDK', 'Readme.md'),
        os.path.join(start_dir, 'README.md'),
        os.path.join(start_dir, 'Autogenerated', 'Bindings', 'Go', 'lib3mf_impl.go'),
        os.path.join(start_dir, 'Autogenerated', 'Bindings', 'CppDynamic', 'lib3mf_abi.hpp'),
        os.path.join(start_dir, 'SDK', 'Examples', 'CSharp', 'Lib3MF_Example.cs'),
        os.path.join(start_dir, 'SDK', 'Examples', 'Python', 'lib3mf_common.py'),
        os.path.join(start_dir, 'SDK', 'Examples', 'Python', 'Lib3MF_Example.py'),
        os.path.join(start_dir, 'SDK', 'Examples', 'Python', 'create_cube.py'),
        os.path.join(start_dir, 'SDK', 'Examples', 'Python', 'beam_lattice.py'),
        os.path.join(start_dir, 'SDK', 'Examples', 'Python', 'add_triangle.py'),
        os.path.join(start_dir, 'SDK', 'Examples', 'Python', 'color_cube.py'),
        os.path.join(start_dir, 'SDK', 'Examples', 'Python', '3mf_convert.py'),
        os.path.join(start_dir, 'SDK', 'Examples', 'Python', 'extract_info.py'),
        os.path.join(start_dir, 'SDK', 'Examples', 'Python', 'create_components.py'),
        os.path.join(start_dir, 'SDK', 'Examples', 'Pascal', 'Lib3MF_Example.lpr'),
        os.path.join(start_dir, 'SDK', 'CPackExamples', 'Cpp', 'CMakeLists.txt'),
        os.path.join(start_dir, 'SDK', 'CPackExamples', 'CppDynamic', 'CMakeLists.txt'),
        os.path.join(start_dir, 'Source', 'API', 'lib3mf.cpp'),
    ]

    # Update all additional specified files
    for file_path in additional_file_paths:
        update_version_in_file(file_path, old_version, new_version)


if __name__ == "__main__":
    main()
