"""
PIX4Dmapper command line class created by thau
to do all the jobs relating to point-clouds generation
for HumanTech EU project
"""

import os
import subprocess
import xml.etree.ElementTree as ET
import xml.dom.minidom as minidom

from pybimscantools import textcolor as tc


class Pix4dCli:
    """This class triggers PIX4Dmapper to run in command line mode"""

    def __init__(self) -> None:
        """Constructor"""

    def run_pix4dtagger(self, img_dataset: str, marker_loc: str, output_loc: str, file_name: str, user_name: str) -> None:
        """This function triggers PIX4Dtagger to map images with markers"""
        if not os.path.exists(output_loc):
            os.makedirs(output_loc)
        output_loc = os.path.join(output_loc, file_name)

        # change "\" to "/" in the path
        img_dataset = img_dataset.replace("\\", "/")
        marker_loc = marker_loc.replace("\\", "/")
        output_loc = output_loc.replace("\\", "/")

        print(tc.colored_text("Running PIX4Dtagger!", "Orange"))

        # check if img_dataset, marker_loc exist and exit the function if not
        if not os.path.exists(img_dataset):
            print(tc.colored_text("Image dataset location does not exist!", "Red"))
            return
        if not os.path.exists(marker_loc):
            print(tc.colored_text("Marker location does not exist!", "Red"))
            return

        # pix4dtagger configuration
        cli = ["pix4dtagger"]
        cli.append("-c")
        cli.append("-i")  # input images
        cli.append("\"" + img_dataset + "\"")
        cli.append("-t")  # marker location
        cli.append("\"" + marker_loc + "\"")
        cli.append("-o")  # output location
        cli.append("\"" + output_loc + "\"")
        cli.append("-f")  # output format
        cli.append("project")
        cli.append("--usedb")  # use database
        cli.append("1")
        cli.append("-d")
        cli.append("\"" + "C:/Users/" + user_name + "/AppData/Local/pix4d/common/19/icmdb.xml" + "\"")
        # cli.append("-h")        # for help
        cli = " ".join(cli)
        print(cli)

        # For miniforge python, it handles subprocess.Popen differently
        process = subprocess.run(cli, cwd="C:/Program Files/Pix4Dmapper", capture_output=True, shell=True)
        # Print stdout (standard output)
        if process.stdout:
            print("STDOUT:")
            print(process.stdout)

        # # Print stderr (standard error)
        # if process.stderr:
        #     print("STDERR:")
        #     print(process.stderr)

        # pix4dtagger run
        cli = ["pix4dtagger"]
        cli.append("-i")
        cli.append("\"" + img_dataset + "\"")
        cli.append("-t")
        cli.append("\"" + marker_loc + "\"")
        cli.append("-o")
        cli.append("\"" + output_loc + "\"")
        cli = " ".join(cli)

        # For miniforge python, it handles subprocess.Popen differently
        process = subprocess.run(cli, cwd="C:/Program Files/Pix4Dmapper", capture_output=True, shell=True)
        # Print stdout (standard output)
        if process.stdout:
            print("STDOUT:")
            print(process.stdout)

        # # Print stderr (standard error)
        # if process.stderr:
        #     print("STDERR:")
        #     print(process.stderr)

        # Get the return code of the command
        return_code = process.returncode
        if return_code == 0:
            # check if output file exists
            if os.path.exists(output_loc):
                print(tc.colored_text(f"{output_loc} has been successfully created!", "Green"))
                print(
                    tc.colored_text("########################################", "Green")
                )
            else:
                print(tc.colored_text("No output file generated!", "Red"))
        else:
            print(tc.colored_text("Error occurred!", "Red"))

    def run_pix4dmapper_marker_system(self, project_loc: str) -> None:
        """This function triggers PIX4Dmapper to generate point-clouds from a pre-defined .p4d file generated by PIX4Dtagger"""
        print(tc.colored_text("Running PIX4Dmapper to generate point clouds!", "Orange"))

        # check if project_loc exist and exit the function if not
        if not os.path.exists(project_loc):
            print(tc.colored_text("Project location does not exist!", "Red"))
            return

        # pix4dmapper configuration
        cli = ["pix4dmapper"]
        # cli.append("-h")
        cli.append("-r")  # run pix4dmapper
        cli.append("--template")  # template
        cli.append("BIMProve_Default_No_Multiscale_High_Density")
        cli.append(project_loc)
        # cli.append("exit")
        cli = " ".join(cli)
        print(cli)

        # For miniforge python, it handles subprocess.Popen differently
        process = subprocess.run(cli, cwd="C:/Program Files/Pix4Dmapper", capture_output=True, shell=True)
        # Print stdout (standard output)
        if process.stdout:
            print("STDOUT:")
            print(process.stdout)

        # # Print stderr (standard error)
        # if process.stderr:
        #     print("STDERR:")
        #     print(process.stderr)

        # Get the return code of the command
        return_code = process.returncode
        if return_code == 0:
            # check if output file exists
            if os.path.exists(project_loc):
                print(tc.colored_text(f"{project_loc} has been successfully created!", "Green"))
                print(
                    tc.colored_text("########################################", "Green")
                )
            else:
                print(tc.colored_text("No output file generated!", "Red"))
        else:
            print(tc.colored_text("Error occurred!", "Red"))

    def run_pix4dmapper_rtk(self, img_dataset: str, project_loc: str, file_name: str) -> None:
        """This function triggers PIX4Dmapper to generate point-clouds based on rtk data"""
        if not os.path.exists(project_loc):
            os.makedirs(project_loc)
        project_loc = os.path.join(project_loc, file_name)

        # change "\" to "/" in the path
        img_dataset = img_dataset.replace("\\", "/")
        project_loc = project_loc.replace("\\", "/")

        # The project file can not be created using CLI anymore because of the license and being obsolete
        # Manually create the project file
        if self.create_pix4d_xml(img_dataset, project_loc):
            print(tc.colored_text("Running PIX4Dmapper to generate point clouds!", "Orange"))
            # pix4dmapper run
            cli = ["pix4dmapper"]
            cli.append("-r")  # run pix4dmapper
            cli.append("--template")  # template
            cli.append("BIMProve_Default_No_Multiscale_High_Density")
            cli.append("\"" + project_loc + "\"") 
            # cli.append("exit")
            cli = " ".join(cli)
            print(cli)

            # process = subprocess.Popen(
            #     cli,
            #     stdout=subprocess.PIPE,
            #     stderr=subprocess.PIPE,
            #     text=True,
            #     bufsize=1,
            #     universal_newlines=True,
            #     shell=True,
            #     executable='C:/Windows/System32/cmd.exe')

            # # Read the output and error streams in real-time
            # for line in process.stdout:
            #     text = ""
            #     # Remove any weird whitespaces
            #     for count in range(0, len(line) - 1):
            #         if line[count] != chr(0):
            #             text = text + line[count]
            #     print(tc.colored_text(text, "Pink"))

            # for line in process.stderr:
            #     err_str = ""
            #     # Remove any weird whitespaces
            #     for count in range(0, len(line) - 1):
            #         if line[count] != chr(0):
            #             err_str = err_str + line[count]
            #     print(err_str)

            # # Wait for the process to complete
            # process.wait()

            # For miniforge python, it handles subprocess.Popen differently
            process = subprocess.run(cli, cwd="C:/Program Files/Pix4Dmapper", capture_output=True, shell=True)
            # Print stdout (standard output)
            if process.stdout:
                print("STDOUT:")
                print(process.stdout)

            # # Print stderr (standard error)
            # if process.stderr:
            #     print("STDERR:")
            #     print(process.stderr)

            # Get the return code of the command
            return_code = process.returncode
            if return_code == 0:
                # check if output file exists
                if os.path.exists(project_loc):
                    print(tc.colored_text(f"{project_loc} has been successfully created!", "Green"))
                    print(
                        tc.colored_text("########################################", "Green")
                    )
                else:
                    print(tc.colored_text("No output file generated!", "Red"))
            else:
                print(tc.colored_text("Error occurred!", "Red"))
        else:
            print(tc.colored_text("PIX4Dmapper couldn't start!", "Red"))

    def create_pix4d_xml(self, img_dataset: str, project_loc: str) -> bool:
        """This function creates a Pix4D project file"""
        print(tc.colored_text("Creating PIX4D project file!", "Orange"))
        image_files = []
        # get all the image files (.jpg) in the image dataset
        for file in os.listdir(img_dataset):
            # Extract file extension and compare case-insensitively
            if os.path.splitext(file)[1].lower() == ".jpg":
                image_files.append(file)

        # create the root element
        root = ET.Element("pix4dmapper")
        # Create the inputs element
        inputs = ET.SubElement(root, "inputs")
        # Create the images element
        images = ET.SubElement(inputs, "images")

        # Add each image as a child element
        for image_file in image_files:
            image_path = os.path.join(img_dataset, image_file)
            # change "\" to "/" in the path
            image_path = image_path.replace("\\", "/")
            image_element = ET.SubElement(images, "image")
            image_element.set("path", image_path)
            image_element.set("enabled", "true")

        # Convert the ElementTree to a string
        xml_str = ET.tostring(root, encoding="utf-8")

        # Use minidom to pretty-print the XML
        dom = minidom.parseString(xml_str)
        pretty_xml_as_string = dom.toprettyxml(indent="  ", encoding="utf-8").decode('utf-8')

        # Post-process the output to ensure no self-closing tags for <image>
        pretty_xml_as_string = pretty_xml_as_string.replace('/>', '>\n\t</image>')

        # Write the formatted XML to a file
        output_file = project_loc
        with open(output_file, "w", encoding="utf-8") as f:
            f.write(pretty_xml_as_string)
        
        # check if output file exists
        if os.path.exists(output_file):
            print(tc.colored_text(f"{output_file} has been successfully created!", "Green"))
            print(
                tc.colored_text("########################################", "Green")
            )
            return True
        else:
            print(tc.colored_text("No output file generated!", "Red"))
            return False