import pandas as pd
import numpy as np
import os
import json
import sumolib
from tqdm import tqdm

def check_inter_info(inter_info):
    print(inter_info)
    print('check')

def make_match1(path_root):
    '''
    신호 DB에는 매 초마다 이동류정보가 업데이트 된다. 그리고 이 이동류정보를 매 5초마다 불러와서 사용하게 된다.
    '../Data/tables/move/'에는 5초마다의 이동류정보가 저장되어 있다.

    return : 통합된 이동류정보
     - 모든 inter_no(교차로번호)에 대한 A, B링 현시별 이동류정보

    match1을 만드는 데 시간이 소요되므로 한 번 만들어서 저장해두고 저장해둔 것을 쓴다.
    '''
    # [이동류번호] 불러오기 (약 1분의 소요시간)
    path_move = os.path.join(path_root, 'Data', 'tables', 'move')
    csv_moves = os.listdir(path_move)
    moves = [pd.read_csv(os.path.join(path_move, csv_move), index_col=0) for csv_move in tqdm(csv_moves, desc='이동류정보 불러오는 중 : match1')]
    match1 = pd.concat(moves).drop_duplicates().sort_values(by=['inter_no','phas_A','phas_B']).reset_index(drop=True)
    match1.to_csv(os.path.join(path_root, 'Intermediates', 'match1.csv'))
    return match1

def make_match2(match1):
    '''
    match1을 계층화함.
     - match1의 컬럼 : inter_no, phas_A, phas_B, move_A, move_B
     - match2의 컬럼 : inter_no, phase_no, ring_type, move_no
    '''
    # 계층화 (inter_no, phas_A, phas_B, move_A, move_B) -> ('inter_no', 'phase_no', 'ring_type', 'move_no')
    matchA = match1[['inter_no', 'phas_A', 'move_A']].copy()
    matchA.columns = ['inter_no', 'phase_no', 'move_no']
    matchA['ring_type'] = 'A'
    matchB = match1[['inter_no', 'phas_B', 'move_B']].copy()
    matchB.columns = ['inter_no', 'phase_no', 'move_no']
    matchB['ring_type'] = 'B'
    match2 = pd.concat([matchA, matchB]).drop_duplicates()
    match2 = match2[['inter_no', 'phase_no', 'ring_type', 'move_no']]
    match2 = match2.sort_values(by=list(match2.columns))
    return match2

def make_match3(match2, nema):
    '''
    각 movement들에 방향(진입방향, 진출방향)을 매칭시켜 추가함.
     - match2의 컬럼 : inter_no, phase_no, ring_type, move_no
     - match3의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir

    nema : 
     - 컬럼 : move_no, inc_dir, out_dir
     - 모든 종류의 이동류번호에 대하여 진입방향과 진출방향을 매칭시키는 테이블
     - 이동류번호 : 1 ~ 16, 17, 18, 21
     - 진입, 진출방향(8방위) : 동, 서, 남, 북, 북동, 북서, 남동, 남서
    '''
    # nema 정보 불러오기 및 병합
    match3 = pd.merge(match2, nema, how='left', on='move_no').drop_duplicates()
    return match3

def make_match4(match3, angle):
    '''
    방위각 정보를 매칭시켜 추가함.
     - match3의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir
     - match4의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle

    angle_original : 
     - 컬럼 : inter_no, angle_Aj, angle_Bj (j : 1 ~ 8)
     - 모든 종류의 이동류번호에 대하여 진입방향과 진출방향을 매칭시키는 테이블
     - 이동류번호 : 1 ~ 16, 17, 18, 21
     - 진입, 진출방향(8방위) : 동, 서, 남, 북, 북동, 북서, 남동, 남서
    '''

    # 계층화
    angles = []
    for i, row in angle.iterrows():
        angle_codes = row[[f'angle_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]]
        new = pd.DataFrame({'inter_no':[row.inter_no] * 16, 'phase_no':list(range(1, 9))*2, 'ring_type':['A'] * 8 + ['B'] * 8, 'angle_code':angle_codes.to_list()})
        angles.append(new)
    angles = pd.concat(angles)
    angles = angles.dropna().reset_index(drop=True)

    # 병합
    six_chars = angles.angle_code.apply(lambda x:len(x)==6)
    angles.loc[six_chars,'inc_angle'] = angles.angle_code.apply(lambda x:x[:3])
    angles.loc[six_chars,'out_angle'] = angles.angle_code.apply(lambda x:x[3:])
    angles = angles.drop('angle_code', axis=1)
    match4 = pd.merge(match3, angles, how='left', left_on=['inter_no', 'phase_no', 'ring_type'],
                    right_on=['inter_no', 'phase_no', 'ring_type']).drop_duplicates()
    return match4

def make_match5(match4, net, inter_node, inter_info):
    '''
    진입엣지id, 진출엣지id, 노드id를 추가함 (주교차로).
     - match4의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle
     - match5의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle, inc_edge, out_edge, node_id
    
    사용된 데이터 : 
    (1) net
     - 성남시 정자동 부근의 샘플 네트워크
    (2) inter_node
     - 교차로번호와 노드id를 매칭시키는 테이블.
     - parent/child 정보도 포함되어 있음
     - 컬럼 : inter_no, node_id, inter_type
    (3) inter_info
     - 교차로 정보. 여기에서는 위도와 경도가 쓰임.
     - 컬럼 : inter_no, inter_name, inter_lat, inter_lon, group_no, main_phase_no

    진입엣지id, 진출엣지id를 얻는 과정 :
     - match5 = match4.copy()의 각 열을 순회하면서 아래 과정을 반복함.
     * 진입에 대해서만 서술하겠지만 진출도 마찬가지로 설명될 수 있음
     - 해당 행의 교차로정보로부터 노드ID를 얻어내고, 해당 노드에 대한 모든 진출엣지id를 inc_edges에 저장.
     * inc_edge(진입엣지) : incoming edge, out_edge(진출엣지) : outgoing_edge
     - inc_edges의 모든 진입엣지에 대하여 진입방향(inc_dires, 2차원 단위벡터)을 얻어냄.
     - 해당 행의 진입각으로부터 그에 대응되는 진입각방향(단위벡터)를 얻어냄.
     - 주어진 진입각방향에 대하여 내적이 가장 작은 진입방향에 대한 진입엣지를 inc_edge_id로 지정함.
    '''

    # parent node만 가져옴.
    inter_node1 = inter_node[inter_node.inter_type == 'parent'].drop('inter_type', axis=1)
    inter_info1 = inter_info[['inter_no', 'inter_lat', 'inter_lon']]
    inter = pd.merge(inter_node1, inter_info1, how='left', left_on=['inter_no'],
                    right_on=['inter_no']).drop_duplicates()

    inter2node = dict(zip(inter['inter_no'], inter['node_id']))

    match5 = match4.copy()
    # 진입진출ID 매칭
    for index, row in match5.iterrows():
        node_id = inter2node[row.inter_no]
        node = net.getNode(node_id)
        # 교차로의 모든 (from / to) edges
        inc_edges = [edge for edge in node.getIncoming() if edge.getFunction() == ''] # incoming edges
        out_edges = [edge for edge in node.getOutgoing() if edge.getFunction() == ''] # outgoing edges
        # 교차로의 모든 (from / to) directions
        inc_dirs = []
        for inc_edge in inc_edges:
            start = inc_edge.getShape()[-2]
            end = inc_edge.getShape()[-1]
            inc_dir = np.array(end) - np.array(start)
            inc_dir = inc_dir / (inc_dir ** 2).sum() ** 0.5
            inc_dirs.append(inc_dir)
        out_dirs = []
        for out_edge in out_edges:
            start = out_edge.getShape()[0]
            end = out_edge.getShape()[1]
            out_dir = np.array(end) - np.array(start)
            out_dir = out_dir / (out_dir ** 2).sum() ** 0.5
            out_dirs.append(out_dir)
        # 진입각, 진출각 불러오기
        if not pd.isna(row.inc_angle):
            inc_angle = int(row.inc_angle)
            out_angle = int(row.out_angle)
            # 방위각을 일반각으로 가공, 라디안 변환, 단위벡터로 변환
            inc_angle = (-90 - inc_angle) % 360
            inc_angle = inc_angle * np.pi / 180.
            inc_dir_true = np.array([np.cos(inc_angle), np.sin(inc_angle)])
            out_angle = (90 - out_angle) % 360
            out_angle = out_angle * np.pi / 180.
            out_dir_true = np.array([np.cos(out_angle), np.sin(out_angle)])
            # 매칭 엣지 반환
            inc_index = np.array([np.dot(inc_dir, inc_dir_true) for inc_dir in inc_dirs]).argmax()
            out_index = np.array([np.dot(out_dir, out_dir_true) for out_dir in out_dirs]).argmax()
            inc_edge_id = inc_edges[inc_index].getID()
            out_edge_id = out_edges[out_index].getID()
            match5.at[index, 'inc_edge'] = inc_edge_id
            match5.at[index, 'out_edge'] = out_edge_id
    match5['node_id'] = match5['inter_no'].map(inter2node)
    match5 = match5.sort_values(by=['inter_no','phase_no','ring_type']).reset_index(drop=True)
    return match5

def make_match6(match5, inter_node, uturn, coord, path_root):
    '''
    진입엣지id, 진출엣지id, 노드id를 추가함 (부교차로).
     - match6의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle, inc_edge, out_edge, node_id
    
    사용된 데이터 : 
    (1) inter_node
     - 교차로번호와 노드id를 매칭시키는 테이블.
     - parent/child 정보도 포함되어 있음
     - 컬럼 : inter_no, node_id, inter_type
    (2) uturn (유턴정보)
     - 컬럼 : parent_id, child_id, direction, condition, inc_edge, out_edge
     - parent_id, child_id : 주교차로id, 유턴교차로id
     - direction : 주교차로에 대한 유턴노드의 상대적인 위치(방향)
     - condition : 좌회전시, 직진시, 직좌시, 보행신호시 중 하나
     - inc_edge, out_edge : 유턴에 대한 진입진출엣지
    (3) coord (연동교차로정보)
     - 컬럼 : parent_id, child_id, phase_no, ring_type, inc_edge, out_edge
     - parent_id, child_id : 주교차로id, 연동교차로id
     - 나머지 컬럼 : 각 (현시, 링)별 진입진출엣지

    설명 :
     - match5는 주교차로에 대해서만 진입엣지id, 진출엣지id, 노드id를 추가했었음.
       여기에서 uturn, coord를 사용해서 부교차로들(유턴교차로, 연동교차로)에 대해서도 해당 값들을 부여함.
    유턴교차로 :
     - directions를 정북기준 시계방향의 8방위로 정함.
        - 이를 통해 진입방향이 주어진 경우에 좌회전, 직진, 보행 등에 대한 (진입방향, 진출방향)을 얻어낼 수 있음.
        - 예) 진입방향(direction)이 '북'일 때, 
        - 직진 : (북, 남)
            * 남 : directions[(ind + 4) % len(directions)]
        - 좌회전 : (북, 동)
            * 동 : directions[(ind + 2) % len(directions)]
        - 보행 : (서, 동)
            * 서 : directions[(ind - 2) % len(directions)]
     - uturn의 각 행을 순회하면서 아래 과정을 반복함
        - match5에서 parent_id에 해당하는 행들을 가져옴(cmatch).
        - condition 별로 진입방향, 진출방향A, 진출방향B 정함.
            - 상술한 directions를 활용하여 정함.
        - (진입방향, 진출방향A, 진출방향B)을 고려하여 (현시, 링) 별로 진입엣지id, 진출엣지id를 정함.
            - ex) cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]
     - 순회하면서 만든 cmatch를 cmatchs라는 리스트에 저장함.

    연동교차로 :
     - 연동교차로의 경우 coord에 (현시, 링)별 진입엣지ID, 진출엣지ID가 명시되어 있음.
     - 'inc_dir', 'out_dir', 'inc_angle','out_angle'와 같은 열들은 np.nan을 지정해놓음.
     - 이 열들은, 사실상 다음 스텝부터는 사용되지 않는 열들이기 때문에 np.nan으로 지정해놓아도 문제없음.

    match6 :
     - 이렇게 얻은 match5, cmatchs, coord를 모두 pd.concat하여 match6을 얻어냄.
    '''

    node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no']))

    child_ids = inter_node[inter_node.inter_type=='child'].node_id.unique()
    ch2pa = {} # child to parent
    for child_id in child_ids:
        parent_no = inter_node[inter_node.node_id==child_id].inter_no.iloc[0]
        sub_inter_node = inter_node[inter_node.inter_no==parent_no]
        ch2pa[child_id] = sub_inter_node[sub_inter_node.inter_type=='parent'].iloc[0].node_id
    directions = ['북', '북동', '동', '남동', '남', '남서', '서', '북서'] # 정북기준 시계방향으로 8방향

    # 각 uturn node에 대하여 (inc_edge_id, out_edge_id) 부여
    cmatches = []
    for _, row in uturn.iterrows():
        child_id = row.child_id
        parent_id = row.parent_id
        direction = row.direction
        condition = row.condition
        inc_edge_id = row.inc_edge
        out_edge_id = row.out_edge
        # match5에서 parent_id에 해당하는 행들을 가져옴
        cmatch = match5.copy()[match5.node_id==parent_id] # match dataframe for a child node
        cmatch = cmatch.sort_values(by=['phase_no', 'ring_type']).reset_index(drop=True)
        cmatch['node_id'] = child_id
        cmatch[['inc_edge', 'out_edge']] = np.nan

        # condition 별로 inc_dire, out_dire_A, out_dire_B를 정함
        ind = directions.index(direction)
        if condition == "좌회전시":
            inc_dire = direction
            out_dire_A = out_dire_B = directions[(ind + 2) % len(directions)]
        elif condition == "직진시":
            inc_dire = direction
            out_dire_A = out_dire_B = directions[(ind + 4) % len(directions)]
        elif condition == "보행신호시":
            inc_dire = directions[(ind + 2) % len(directions)]
            out_dire_A = directions[(ind - 2) % len(directions)]
            out_dire_B = directions[(ind - 2) % len(directions)]

        # (inc_dire, out_dire_A, out_dire_B) 별로 inc_edge_id, out_edge_id를 정함
        if condition == '보행신호시':
            cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]
            cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]
            # 이동류번호가 17(보행신호)이면서 유턴노드방향으로 가는 신호가 없으면 (inc_edge_id, out_edge_id)를 부여한다.
            cmatch.loc[(cmatch.move_no==17) & (cmatch.out_dir!=direction), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]
        else: # '직진시', '좌회전시'
            cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]
            cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]
        # 유턴신호의 이동류번호를 19로 부여한다.
        cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), 'move_no'] = 19
        cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), 'move_no'] = 19
        cmatches.append(cmatch)

    # 각 coordination node에 대하여 (inc_edge_id, out_edge_id) 부여
    coord['inter_no'] = coord['parent_id'].map(node2inter)
    coord = coord.rename(columns={'child_id':'node_id'})
    coord[['inc_dir', 'out_dir', 'inc_angle','out_angle']] = np.nan
    coord['move_no'] = 20
    coord = coord[['inter_no', 'phase_no', 'ring_type', 'move_no', 'inc_dir', 'out_dir', 'inc_angle','out_angle', 'inc_edge', 'out_edge', 'node_id']]
    
    # display(coord)
    cmatches = pd.concat(cmatches)
    match6 = pd.concat([match5, cmatches, coord]).drop_duplicates().sort_values(by=['inter_no', 'node_id', 'phase_no', 'ring_type'])
    match6.to_csv(os.path.join(path_root, 'Intermediates', 'match6.csv'))
    return match6

def make_matching(match6, inter_node, nema, path_root):
    '''
    이동류 매칭 : 각 교차로에 대하여, 가능한 모든 이동류 (1~18, 21)에 대한 진입·진출엣지ID를 지정한다.
    모든 이동류에 대해 지정하므로, 시차제시 이전과 다른 이동류가 등장하더라도 항상 진입·진출 엣지 ID를 지정할 수 있다.    
     - matching의 컬럼 : inter_no, move_no, inc_dir, out_dir, inc_edge, out_edge, node_id
    
    설명 : 
     - 필요한 리스트, 딕셔너리 등을 정의
        (1) 가능한 (진입방향, 진출방향) 목록 [리스트]
        (2) 각 교차로별 방향 목록 : pdires (possible directions) [딕셔너리]
        (3) 각 (교차로, 진입방향) 별 진입id 목록 : inc2id (incoming direction to incoming edge_id) [딕셔너리]
        (4) 각 (교차로, 진출방향) 별 진출id 목록 : out2id (outgoing direction to outgoing edge_id) [딕셔너리]
        (5) 각 교차로별 가능한 (진입방향, 진출방향) 목록 : pflow (possible flows) [딕셔너리]
     - matching은 빈 리스트로 지정.
     - 모든 노드id에 대하여 다음 과정을 반복
     - 해당 노드id에 대한 모든 가능한 (진입방향, 진출방향)에 대하여 다음 과정을 반복
     - (노드id, 진입방향)으로부터 진입엣지id를 얻어냄. 마찬가지로 진출엣지id도 얻어냄
     - 얻어낸 정보를 바탕으로 한 행(new_row)을 만들고 이것을 matching에 append
     '''

    match7 = match6.copy()
    match7 = match7[['inter_no', 'move_no', 'inc_dir', 'out_dir', 'inc_edge', 'out_edge', 'node_id']]

    parent_ids = sorted(inter_node[inter_node.inter_type=='parent'].node_id.unique())
    child_ids = sorted(inter_node[inter_node.inter_type=='child'].node_id.unique())

    # (1) 가능한 (진입방향, 진출방향) 목록 
    flows = nema.dropna().apply(lambda row: (row['inc_dir'], row['out_dir']), axis=1).tolist()
    # (2) 각 교차로별 방향 목록 : pdires (possible directions)
    pdires = {}
    for node_id in parent_ids:
        dires = match7[match7.node_id == node_id][['inc_dir','out_dir']].values.flatten()
        dires = {dire for dire in dires if type(dire)==str}
        pdires[node_id] = dires
    # (3) 각 (교차로, 진입방향) 별 진입id 목록 : inc2id (incoming direction to incoming edge_id)
    inc2id = {}
    for node_id in parent_ids:
        for inc_dir in pdires[node_id]:
            df = match7[(match7.node_id==node_id) & (match7.inc_dir==inc_dir)]
            inc2id[(node_id, inc_dir)] = df.inc_edge.iloc[0]
    # (4) 각 (교차로, 진출방향) 별 진출id 목록 : out2id (outgoing direction to outgoing edge_id)
    out2id = {}
    for node_id in parent_ids:
        for out_dir in pdires[node_id]:
            df = match7[(match7.node_id==node_id) & (match7.out_dir==out_dir)]
            out2id[(node_id, out_dir)] = df.out_edge.iloc[0]
    # (5) 각 교차로별 가능한 (진입방향, 진출방향) 목록 : pflow (possible flows)
    pflow = {}
    for node_id in parent_ids:
        pflow[node_id] = [flow for flow in flows if set(flow).issubset(pdires[node_id])]
    # (6) 가능한 이동류에 대하여 진입id, 진출id 배정 : matching
    node2inter = dict(zip(match7['node_id'], match7['inter_no']))
    dires_right = ['북', '서', '남', '동', '북'] # ex (북, 서), (서, 남) 등은 우회전 flow
    matching = []
    for node_id in parent_ids:
        inter_no = node2inter[node_id]
        # 좌회전과 직진(1 ~ 16)
        for (inc_dir, out_dir) in pflow[node_id]:
            move_no = nema[(nema.inc_dir==inc_dir) & (nema.out_dir==out_dir)].move_no.iloc[0]
            inc_edge = inc2id[(node_id, inc_dir)]
            out_edge = out2id[(node_id, out_dir)]
            new_row = pd.DataFrame({'inter_no':[inter_no], 'move_no':[move_no],
                                    'inc_dir':[inc_dir], 'out_dir':[out_dir],
                                    'inc_edge':[inc_edge], 'out_edge':[out_edge], 'node_id':[node_id]})
            matching.append(new_row)
        # 보행신호(17), 전적색(18)
        new_row = pd.DataFrame({'inter_no':[inter_no] * 2, 'move_no':[17, 18],
                                'inc_dir':[None]*2, 'out_dir':[None]*2,
                                'inc_edge':[None]*2, 'out_edge':[None]*2, 'node_id':[node_id]*2})
        matching.append(new_row)
        # 신호우회전(21)
        for d in range(len(dires_right)-1):
            inc_dir = dires_right[d]
            out_dir = dires_right[d+1]
            if {inc_dir, out_dir}.issubset(pdires[node_id]):
                inc_edge = inc2id[(node_id, inc_dir)]
                out_edge = out2id[(node_id, out_dir)]
                new_row = pd.DataFrame({'inter_no':[inter_no], 'move_no':[21],
                                        'inc_dir':[inc_dir], 'out_dir':[out_dir],
                                        'inc_edge':[inc_edge], 'out_edge':[out_edge], 'node_id':[node_id]})
                matching.append(new_row)
    matching.append(match7[match7.node_id.isin(child_ids)])
    matching = pd.concat(matching)
    matching = matching.dropna().sort_values(by=['inter_no', 'node_id', 'move_no']).reset_index(drop=True)
    matching['move_no'] = matching['move_no'].astype(int)
    matching.to_csv(os.path.join(path_root, 'Intermediates', 'matching.csv'))
    return matching

def make_movements(path_root):
    movements_path = os.path.join(path_root, 'Intermediates', 'movement')
    movements_list = [pd.read_csv(os.path.join(movements_path, file), index_col=0) for file in tqdm(os.listdir(movements_path), desc='이동류정보 불러오는 중 : movements')]
    movements = pd.concat(movements_list)
    movements = movements.drop(columns=['start_unix'])
    movements = movements.drop_duplicates()
    movements = movements.sort_values(by=['inter_no', 'phas_A', 'phas_B'])
    movements = movements.reset_index(drop=True)
    movements.to_csv(os.path.join(path_root, 'Intermediates', 'movements.csv'))
    return movements

# node2num_cycles : A dictionary that maps a node_id to the number of cycles
def get_node2num_cycles(plan, inter_node, path_root):
    node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no']))
    node_ids = sorted(inter_node.node_id.unique())

    Aplan = plan.copy()[['inter_no'] + [f'dura_A{j}' for j in range(1,9)] + ['cycle']]
    grouped = Aplan.groupby('inter_no')
    df = grouped.agg({'cycle': 'min'}).reset_index()
    df = df.rename(columns={'cycle': 'min_cycle'})
    df['num_cycle'] = 300 // df['min_cycle'] + 2
    inter2num_cycles = dict(zip(df['inter_no'], df['num_cycle']))
    node2numcycles = {node_id : inter2num_cycles[node2inter[node_id]] for node_id in node_ids}
    with open(os.path.join('Intermediates','node2numcycles.json'), 'w') as file:
        json.dump(node2numcycles, file, indent=4)
    return node2numcycles

def main():
    path_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

    inter_info = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'inter_info.csv'))
    check_inter_info(inter_info)
    angle = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'angle.csv'), dtype = {f'angle_{alph}{j}':'str' for alph in ['A', 'B'] for j in range(1,9)})
    plan = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'plan.csv'))
    inter_node = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'inter_node.csv'))
    uturn = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'child_uturn.csv'))
    coord = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'child_coord.csv'))
    nema = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'nema.csv'), encoding='cp949')

    net = sumolib.net.readNet(os.path.join(path_root, 'Data', 'networks', 'sn.net.xml'))

    match1 = make_match1(path_root)
    match2 = make_match2(match1)
    match3 = make_match3(match2, nema)
    match4 = make_match4(match3, angle)
    match5 = make_match5(match4, net, inter_node, inter_info)
    match6 = make_match6(match5, inter_node, uturn, coord, path_root)
    matching = make_matching(match6, inter_node, nema, path_root)
    movements = make_movements(path_root)
    node2num_cycles = get_node2num_cycles(plan, inter_node, path_root)

if __name__ == '__main__':
    main()
