import requests
import re

# function for MIDAS Open API
def MidasAPI(method, command, body=None):
    base_url = "Input_Your_Base_URL_Here"
    mapi_key = "Input_Your_MAPI_Key_Here"

    url = base_url + command
    headers = {
        "Content-Type": "application/json",
        "MAPI-Key": mapi_key
    }

    if method == "POST":
        response = requests.post(url=url, headers=headers, json=body)
    elif method == "PUT":
        response = requests.put(url=url, headers=headers, json=body)
    elif method == "GET":
        response = requests.get(url=url, headers=headers)
    elif method == "DELETE":
        response = requests.delete(url=url, headers=headers)

    print(method, command, response.status_code)
    return response.json()

def divide_line_equally(start_coord, end_coord, divide):
    """
    Divide a line into equal segments.

    Args:
    start_coord (list): The starting coordinates of the line.
    end_coord (list): The ending coordinates of the line.
    divide (int): The number of segments to divide the line into.

    Returns:
    list: A list of coordinates for each new node created by dividing the line.
    """
    if divide <= 1:
        return [start_coord, end_coord]

    # Calculate the difference in each dimension
    diff = [end_coord[i] - start_coord[i] for i in range(len(start_coord))]

    # Calculate the step for each dimension
    step = [d / divide for d in diff]

    # Create new coordinates
    new_coords = [start_coord]
    for i in range(1, divide):
        new_coord = [start_coord[j] + step[j] * i for j in range(len(start_coord))]
        new_coords.append(new_coord)

    new_coords.append(end_coord)

    return new_coords

def divide_line_unequally(start_coord, end_coord, segment_lengths):
    """
    Divide a line based on absolute segment lengths and return the coordinates of each segment's end.
    This function does not use the ratio method, but calculates the coordinates based on the absolute lengths.

    Args:
    start_coord (list): The starting coordinates of the line.
    end_coord (list): The ending coordinates of the line.
    segment_lengths (list): The lengths of each segment along the line.

    Returns:
    list: A list of coordinates for each segment's end point.
    """
    # Calculate the total line vector and its length
    line_vector = [end_coord[i] - start_coord[i] for i in range(len(start_coord))]
    line_length = sum((comp ** 2 for comp in line_vector)) ** 0.5

    # Calculate the unit vector
    unit_vector = [comp / line_length for comp in line_vector]

    # Initialize the list of coordinates
    line_coords = [start_coord]
    current_coord = start_coord[:]

    for length in segment_lengths :  # Exclude the last length
        # Calculate the absolute length for each segment
        segment_vector = [comp * length for comp in unit_vector]
        new_coord = [current_coord[i] + segment_vector[i] for i in range(len(current_coord))]
        line_coords.append(new_coord)
        current_coord = new_coord

    line_coords.append(end_coord)  # Include the end coordinate
    return line_coords

def parse_unequal_value(value):
    """
    Parse the extended format of the unequal_value string to create a list.
    The format can include both numbers and 'Integer@Real' pairs, separated by commas.

    Args:
    value (str): The string to be parsed.

    Returns:
    list: A list of numbers based on the parsed string.
    """
    parsed_list = []
    elements = value.split(',')

    for element in elements:
        # Check if the element contains '@'
        if '@' in element:
            number, repeat_value = element.split('@')
            parsed_list.extend([float(repeat_value)] * int(number))
        else:
            parsed_list.append(float(element))

    return parsed_list

def validate_string(input_string):
    """
    Validate the input string based on the specified format.
    Each character should be a number or in the format 'Integer@Real', 
    separated by commas.

    Args:
    input_string (str): The string to be validated.

    Returns:
    bool: True if the string is valid, False otherwise.
    """
    # Regular expression to match the pattern
    pattern = re.compile(r'^(\d+|(\d+@\d+(\.\d+)?))(,\d+|,\d+@\d+(\.\d+)?)*$')

    # Check if the string matches the pattern
    return bool(pattern.match(input_string))

def divide_frame_elements(target_elem, option_value, option = "Equal"):
    """
    Divide a frame element into multiple segments based on the specified option.
    """
    # Get the node and element information
    res_elem = MidasAPI("GET", "/db/elem")["ELEM"]
    res_node = MidasAPI("GET", "/db/node")["NODE"]
    
    # Check Element Exist
    if str(target_elem) not in res_elem:
        print("Element does not exist")
        return
    
    # Check Options
    if option not in ["Equal", "Unequal"]:
        print("Invalid option")
        return
    # Equal Option Check
    if option == "Equal" and (option_value < 2 or not isinstance(option_value, int)):
        print("Invalid option value")
        return
    # Unequal Option Check
    if option == "Unequal" and not validate_string(option_value):
        print("Invalid option value")
        return

    # Create max node it
    max_node_id = int(max(res_node, key=int)) + 1
    
    # Get the start and end node coordinates
    start_node_id = res_elem[str(target_elem)]["NODE"][0]
    end_node_id = res_elem[str(target_elem)]["NODE"][1]

    start_node_coord = list(res_node[str(start_node_id)].values())
    end_node_coord = list(res_node[str(end_node_id)].values())

    # Element Length
    element_length = (sum([(end_node_coord[i] - start_node_coord[i])**2 for i in range(3)]))**0.5
    print(element_length)

    # Divide the line
    if option == "Equal":
        new_coords = divide_line_equally(start_node_coord, end_node_coord, option_value)
    elif option == "Unequal":
        length_list = parse_unequal_value(option_value)
        divide_length = sum(length_list)
        if divide_length > element_length:
            print("Summation of the unequal values exceeds the element length")
            return
        new_coords = divide_line_unequally(start_node_coord, end_node_coord, length_list)
    
    assigned_format = {"Assign":{}}
    for i, coord in enumerate(new_coords, start=max_node_id):
        assigned_format["Assign"][str(i)] = {"X": coord[0], "Y": coord[1], "Z": coord[2], "INTERSECT":True}
    
    MidasAPI("POST", "/db/node", assigned_format)


target_elem = 1
equal_value = 5
unequal_value = "1,2,3@1.5,4@2.5"

#divide_frame_elements(target_elem, equal_value, option="Equal")
divide_frame_elements(target_elem, unequal_value, option = "Unequal")