import os
import numpy as np

def write_txt(path, text, mode="a"):
    with open(path, mode, encoding="utf-8") as f:
        f.write(str(text))

def read_txt(path):
    with open(path, encoding="utf-8") as f:
        contents = f.read()
    return contents

def process_file(path, offset=None):
    """
    Args:
        path: str
            Path of the .obj file
        offset: np.array
            Move the coordinates of each point by <offset>.
    """
    # read file
    content = read_txt(path).split("\n")
    geometries = []
    content = content[1:]

    counter_v = 0
    counter_vn = 0
    last_counter_v = 0
    last_counter_vn = 0

    current_v = []
    current_vn = []
    current_f = []
    last_line_f = False
    for line in content:
        line_type = line[:2]

        if line_type!="f " and last_line_f:        
            last_line_f = False
            last_counter_v = counter_v
            last_counter_vn = counter_vn

            # geometry complete
            if offset is not None:
                # add offset to vertices
                current_v_array = np.array(current_v, dtype=float)
                current_v_array = current_v_array + offset
                current_v = []
                for line in current_v_array:
                    current_v.append(f"v {line[0]:.8f} {line[1]:.8f} {line[2]:.8f}")

            geometries.append((current_v, current_vn, current_f))

            # reset
            current_v = []
            current_vn = []
            current_f = []

        if len(line)==0: continue

        if line_type=="v ":
            counter_v +=1
            if offset is not None:
                current_v.append(line.split(" ")[1:]) # only need to modify for offset
            else:
                current_v.append(line)

        if line_type=="vn":
            counter_vn +=1
            current_vn.append(line)

        if line_type=="f ": # startet ab 1
            last_line_f = True
            line_split = line.split(" ")[1:]
            line_f = []
            # set all indices of the vertices to be based at 0 
            # in order to merge multiple files
            for i in range(len(line_split)):
                v, vn = line_split[i].split("//")
                v, vn = int(v), int(vn)
                v, vn = v - last_counter_v, vn - last_counter_vn
                line_f.append(v)
                line_f.append(vn)
            current_f.append(line_f)

    return geometries

def merge_files(out_path, base_path, left_path, right_path, moving_distance, move_axis="x"):
    """
    Args:
        out_path, str:
            Path where to save the result of the merged .obj file to.
        base_path, str:
            Path of the unmoved base.
        left_path, str:
            Path of the .obj file that is moved along the negative direction (e.g. the left finger) 
        right_path, str:
            Path of the .obj file that is moved along the positive direction (e.g. the right finger) 
        moving_distance, float:
            How far to move the left and right objects from another from the current distance.
        move_axis, str:
            Axis along to move the objects.
    """
    # get offset according to axis
    move_axis_dir = {"x": [1,0,0], "y": [0,1,0], "z": [0,0,1]}
    offset = np.array(move_axis_dir[move_axis]) * moving_distance/1000

    # get header
    file_content = read_txt(base_path).split("\n")
    header = file_content[0]

    geometries = process_file(base_path, offset=None)
    geometries += process_file(left_path, offset=-offset/2)
    geometries += process_file(right_path, offset=offset/2)

    lines = [header]
    N_v = 0
    N_vn = 0
    
    for i, geom in enumerate(geometries):
        lines.append(f"o geometry{i}")
        lines.extend(geom[0]) # vertices
        lines.extend(geom[1]) # vertices normals
        # add new offset to the vertices
        lines.extend([f"f {line_f[0]+N_v}//{line_f[1]+N_vn} {line_f[2]+N_v}//{line_f[3]+N_vn} {line_f[4]+N_v}//{line_f[5]+N_vn}" for line_f in geom[2]])

        N_v += len(geom[0])
        N_vn += len(geom[1])

    write_txt(out_path, "\n".join(lines) + "\n", "w")

if __name__ == "__main__":
    workdir = os.path.dirname(__file__)
    base_path = workdir + "/50mm_base.obj"
    left_path = workdir + "/50mm_left_finger.obj"
    right_path = workdir + "/50mm_right_finger.obj"

    dist = [20, 50, 80]
    reference = 50

    for i, d in enumerate(dist):
        d_rel = d-reference
        out_path = workdir + f"/{i+1:02}_{d}mm.obj"
        merge_files(out_path, base_path, left_path, right_path, moving_distance=d_rel)
