import pandas as pd
from time import sleep
from datetime import datetime, timedelta
from random import randint
from tqdm import tqdm
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service

CHROME_DRIVER_PATH = 'chromedriver.exe'
KAYAK = "https://www.kayak.com/explore/{}-anywhere/"

class FlightLoader:
    def __init__(self, url, airport=None, load_n=3, range=3):
        self.TIME = 0.5
        self.url = url
        self.airport = airport
        self.load_n = load_n
        self.range = range

    def get_flights(self):
        self.start_driver()
        return self.to_pandas([x.split('\n') for x in self.collect_flights() if x != ''])

    def increase_time(self):
        self.TIME += 0.25

    def start_driver(self):
        options = webdriver.ChromeOptions()
        options.add_experimental_option('excludeSwitches', ['enable-logging'])
        self.driver = webdriver.Chrome(service=Service(CHROME_DRIVER_PATH), options=options)
        self.driver.get(self.url)

    def collect_flights(self):
        flights = []
        flights += self.add_flights()
        for _ in range(self.range):
            self.zoom_out()
            flights += self.add_flights()
        return flights

    def add_flights(self):
        self.load_more_destinations()
        sleep(self.TIME)
        return [x.text for x in self.driver.find_elements(By.XPATH, '//*[@class = "_iae _lc _ss"]')]

    def load_more_destinations(self):
        self.press_button('//*[substring(@id, string-length(@id) - string-length("-showMoreButton") +1) = "-showMoreButton"]', n_presses=self.load_n)

    def zoom_out(self):
        self.driver.find_element(By.TAG_NAME, "Body").send_keys(Keys.CONTROL + Keys.HOME)
        self.press_button('//*[substring(@id, string-length(@id) - string-length("-zoomControl-minusButton") +1) = "-zoomControl-minusButton"]')

    def press_button(self, button, n_presses=1):
        i = 0
        while i < n_presses:
            try:
                self.driver.find_element(By.XPATH, button).click()
                i += 1
                sleep(0.1)
            except Exception:
                self.driver.find_element(By.TAG_NAME, "Body").send_keys(Keys.CONTROL + 'r')
                sleep(0.5)

    def to_pandas(self, flight_list):
        flights = pd.DataFrame(flight_list, columns=['City', 'Price', 'Country', 'Date']).loc[:, ['Country', 'City', 'Price', 'Date']]
        flights['Price'] = flights['Price'].map(lambda x: int(x[6:]))
        flights[['Departure date', 'Arrival date']] = flights['Date'].str.split('-', expand=True)
        flights.drop('Date', axis=1, inplace=True)
        if self.airport is not None: flights.rename({'Price': f"Price from {self.airport}"}, inplace=True, axis=1)
        return flights.drop_duplicates()

def load_flights(airports, from_date, to_date, min_duration, max_duration, ipy=False, n_results=20):
    flights = sort_and_format(get_flights_from_all_airports(airports, get_dates(from_date, to_date, min_duration, max_duration)), airports)
    if ipy: display(flights.iloc[:n_results])
    else: print(flights.iloc[:n_results])

def get_dates(from_date, to_date, min_duration, max_duration):
    return [
        (from_date + timedelta(i), from_date + timedelta(i) + delta)
        for i in range((to_date - from_date).days)
        for delta in [timedelta(i) for i in range(min_duration, max_duration+1)]
        if from_date + timedelta(i) + delta <= to_date
    ]

def get_flights_from_all_airports(airports, dates):
    all_flights = []
    for i, airport in enumerate(airports):
        print(f"Step {i+1}/{len(airports)}")
        all_flights.append(get_flights_from_airport(airport, dates))
    return all_flights

def get_flights_from_airport(airport, dates):
    flights = []
    for s_date, e_date in tqdm(dates):
        flights.append(flights_from_loader(FlightLoader(get_url(airport, s_date, e_date), airport)))
        sleep(randint(1, 3))
    return pd.concat(flights).drop_duplicates()

def get_url(airport, start_date, end_date):
    return KAYAK.format(airport) + start_date.strftime("%Y%m%d") + ',' + end_date.strftime("%Y%m%d")

def flights_from_loader(loader):
    flights = None
    while True:
        try: flights = loader.get_flights()
        except Exception: loader.increase_time()
        else: break
    del(loader)
    return flights

def sort_and_format(flights, airports):
    flight_list = flights[0].rename({'Price': f"Price from {airports[0]}"}, axis=1).merge(
        flights[1].rename({'Price': f"Price from {airports[1]}"}, axis=1))
    flight_list['Combined price'] = flight_list[f"Price from {airports[0]}"] + flight_list[f"Price from {airports[1]}"]
    return flight_list.sort_values('Combined price').iloc[:, [0, 1, 2, 5, 6, 3, 4]]


#%%
import pandas as pd
import os
from tqdm import tqdm
#%%
def read_dat_file(filename):
    """
    Read .dat file, discards some row headers and returns appropriate values.
    Parameters
    ----------
    filename : string with path and filename do .dat file
    Returns
    -------
    df : pandas.DataFrame
        A pandas dataframe contatining the data.
    """
    df = pd.read_csv(filename, skiprows=3)
    df_aux = pd.read_csv(filename, header=1)
    df.columns = df_aux.columns

    cols_to_drop = ['RECORD', 'Excedente_Avg', 'Compra_Avg']
    for col in cols_to_drop:
        if col in df.columns:
            df = df.drop([col], axis=1)

    for column in df.columns:
        if column != "TIMESTAMP":
            df[column] = df[column].astype('float')
    # Drop column 'RECORD' (if present) because from june 2019 is is no longer used
    return df

def get_list_of_files(folder):
    """
    Return a list of *.dat files inside the subfolders of folder 'folder'.
    Parameters
    ----------
    folder : string with path to root folder
    Returns
    -------
    lst : list
        A list containing all *.dat file strings
    """
    lst = []
    for root, dirs, files in os.walk(folder, topdown=False):
        for name in files:
            complete_filename = os.path.join(root, name)
            # print(complete_filename)
            lst.append(complete_filename)
        for name in dirs:
            complete_filename = os.path.join(root, name)
            # print(complete_filename)
            lst.append(complete_filename)

    lst.sort()
    return [x for x in lst if '.dat' in x]


class MonitoringConfig:
  def __init__(self, _type=None, name=None, machine=None,
    monitoring_endpoint=None, frequency=None, db_access_key_id=None,
    db_secret_access_key=None, monitoring_agent_endpoint=None):

    self._type = _type
    self.name = name
    self.machine = machine
    self.monitoring_endpoint = f'{monitoring_endpoint}/monitoring'
    self.monitoring_agent_endpoint = f'{monitoring_agent_endpoint}/monitoring'
    self.frequency = frequency

    self.db_access_key_id = db_access_key_id
    self.db_secret_access_key = db_secret_access_key

  def get_as_dict(self):
    config = {
      '_type': self._type,
      'name': self.name,
      'machine': self.machine,
    }

    if self._type == 'active':
      config['active'] = { 'monitoring-endpoint': self.monitoring_endpoint }
    else:
      config['passive'] = { 'frequency': self.frequency }

    return config


# 1967 트리의 지름
'''
12
1 2 3
1 3 2
2 4 5
3 5 11
3 6 9
4 7 1
4 8 7
5 9 15
5 10 4
6 11 6
6 12 10
'''

# ver 1 실패
# import sys
# import numpy as np

# N = int(input())

# graph = [[] for _ in range(N+1)]
# score_map = [[0]*(N+1) for _ in range(N+1)]
# print(np.array(score_map))

# for _ in range(N-1):
#     a, b, c  = map(int, input().split())
#     graph[a].append(b)
#     graph[b].append(a)
#     score_map[a][b] = c
#     score_map[b][a] = c

# print(np.array(score_map))
# print(graph)

# import sys
# def dfs(start, end):
#     visited[start] = 1
#     for n in graph[start]:
#         if visited[n] == 0:
#             if n == end:
#                 d += score_map[start][n]
#             else:
#                 dfs(n, end)
#     return ...

# result_list = []

# for i in range(1, N+1):
#     for j in range(1, N+1):
#         if i == j: continue
#         d = 0
#         visited = [0]*(N+1)
#         d = dfs(i, j)
#         result_list.append(d)

# print(max(result_list))

# ver 2 블로그 참조
# https://kyun2da.github.io/2021/05/04/tree's_diameter/

import sys

input = sys.stdin.readline
sys.setrecursionlimit(10**9)

n = int(input())
graph = [[] for _ in range(n + 1)]

def dfs(x, wei):
    for i in graph[x]:
        a, b = i
        if distance[a] == -1:
            distance[a] = wei + b
            dfs(a, wei + b)

# 트리 구현
for _ in range(n - 1):
    a, b, c = map(int, input().split())
    graph[a].append([b, c])
    graph[b].append([a, c])

# 1번 노드에서 가장 먼 곳을 찾는다.
distance = [-1] * (n + 1)
distance[1] = 0
dfs(1, 0)

# 위에서 찾은 노드에 대한 가장 먼 노드를 찾는다.
start = distance.index(max(distance))
distance = [-1] * (n + 1)
distance[start] = 0
dfs(start, 0)

print(max(distance))


# Advent of Code 2018 - Day 23

# Author:   Rachael Judy (c) rjudy1
# Date:     12/28/20
# Purpose:  3d manhattan spheres in space - find number inside biggest sphere and closest point in max number of spheres

import networkx as nx
import os
import sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import parseMod

# parse input into tuples
inp = parseMod.readCSV_row('data/23drones.csv')
bots = []
for line in inp:
    line = line.split(',')
    bots.append((int(line[0][5:]), int(line[1]), int(line[2].strip('>')), int(line[3][3:])))

X, Y, Z, RADIUS = 0, 1, 2, 3
ORIGIN = (0, 0, 0, 0)
manhattan = lambda a, b: abs(a[X] - b[X]) + abs(a[Y] - b[Y]) + abs(a[Z] - b[Z])  # function returns manhattan dist

# part 1
largest_radius_bot = max(bots, key=lambda bot: bot[RADIUS])
print('part 1:', sum((manhattan(largest_radius_bot, bot) <= largest_radius_bot[RADIUS]) for bot in bots))

# part 2
# build a graph with edges between overlapping nanobots
graph = nx.Graph()
for bot in bots:
    # two bots overlap if their distance is smaller or equal than the sum of their ranges
    overlaps = [(bot, other) for other in bots if manhattan(bot, other) <= bot[RADIUS] + other[RADIUS]]
    graph.add_edges_from(overlaps)

# find sets of overlapping nanobots (i.e. fully-connected sub-graphs)
cliques = list(nx.find_cliques(graph))
cliques_size = [len(c) for c in cliques]
assert len([s for s in cliques_size if s == max(cliques_size)]) == 1  # currently no tie breaking check so make sure it doesn't matter
clique = max(cliques, key=len)  # keep largest clique

# calculate the point in the nanobots' radii which is closest to the origin - greedy strategy - not confident as general
points = [manhattan(ORIGIN, bot) - bot[RADIUS] for bot in clique]
# furthest away point in points needed to get all bots in the clique but closest to origin
print('part 2:', max(points))