import numpy as np

import matplotlib.pyplot as plt

import cv2

from google.colab import files

from scipy.interpolate import UnivariateSpline

import warnings


# 경고 무시

warnings.filterwarnings('ignore')


def generate_geogebra_code():

    print("1. 이미지를 업로드하세요. (흰 배경에 검은 선, 혹은 그 반대)")

    uploaded = files.upload()

    

    if not uploaded:

        print("파일이 없습니다.")

        return


    filename = list(uploaded.keys())[0]

    file_bytes = np.frombuffer(uploaded[filename], np.uint8)

    img = cv2.imdecode(file_bytes, cv2.IMREAD_GRAYSCALE)

    

    # 1. 이미지 전처리 (이진화)

    # 배경이 흰색이고 선이 검은색이면 THRESH_BINARY_INV 사용

    # 상황에 따라 threshold 값(127)을 조절하세요.

    _, thresh = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)

    

    # 윤곽선 추출

    contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)


    print(f"\n>> 분석 시작: 총 {len(contours)}개의 파츠를 분석합니다...")

    

    geogebra_list = [] # 지오지브라 명령어 저장용 리스트

    

    # 시각화용 캔버스 설정

    plt.figure(figsize=(10, 10))

    plt.axis('equal')

    plt.title("Extracted Curves")


    count = 0

    

    for contour in contours:

        if len(contour) < 10: continue  # 너무 짧은 잡음 제거


        pts = contour.reshape(-1, 2)

        

        # --- [핵심] 단조 구간(Monotonic Segment) 나누기 ---

        # 하나의 x에 하나의 y만 대응되도록 곡선을 자릅니다.

        segments = []

        current_segment = [pts[0]]

        

        if len(pts) > 1:

            # x가 증가하는 방향인지 감소하는 방향인지 초기 설정

            direction = 1 if pts[1][0] > pts[0][0] else -1

        else:

            direction = 0


        for i in range(1, len(pts)):

            prev_pt = pts[i-1]

            curr_pt = pts[i]

            dx = curr_pt[0] - prev_pt[0]

            

            if dx == 0: # x변화가 없으면(수직선) 같은 그룹에 추가하고 패스

                current_segment.append(curr_pt)

                continue

                

            current_dir = 1 if dx > 0 else -1

            

            # 방향이 바뀌면(꺾이면) 여기서 자름

            if current_dir != direction:

                if len(current_segment) > 5: # 최소 길이 이상일 때만 저장

                    segments.append(np.array(current_segment))

                current_segment = [curr_pt] # 새로운 시작

                direction = current_dir

            else:

                current_segment.append(curr_pt)

        

        # 마지막 남은 조각 추가

        if len(current_segment) > 5:

            segments.append(np.array(current_segment))


        # --- 함수 생성 및 지오지브라 변환 ---

        for seg in segments:

            x_raw = seg[:, 0]

            y_raw = -seg[:, 1] # 이미지 좌표계(y가 아래로 증가)를 수학 좌표계로 반전


            # 중복 x값 제거 (평균화)

            unique_x = np.unique(x_raw)

            clean_y = []

            

            for ux in unique_x:

                ys = y_raw[x_raw == ux]

                clean_y.append(np.mean(ys))

            

            clean_y = np.array(clean_y)

            

            if len(unique_x) < 4: continue # 점이 너무 적으면 패스


            try:

                # 3차 다항식 피팅 (Polyfit)

                coeffs = np.polyfit(unique_x, clean_y, 3)

                

                # 그래프 그리기 (미리보기용)

                x_new = np.linspace(unique_x.min(), unique_x.max(), 50)

                p = np.poly1d(coeffs)

                y_new = p(x_new)

                plt.plot(x_new, y_new, 'r-', linewidth=1)

                

                # --- 지오지브라 문법으로 변환 ---

                # Function( <Function>, <Start x-Value>, <End x-Value> )

                # 계수 포맷팅: 1.54e-05 -> (1.54*10^(-5)) 형태로 변환해야 안전함

                

                def fmt(n): # 지오지브라용 숫자 포맷터

                    return f"({n:.5e})".replace("e", "*10^")


                func_str = f"{fmt(coeffs[0])}x^3 + {fmt(coeffs[1])}x^2 + {fmt(coeffs[2])}x + {fmt(coeffs[3])}"

                

                # 범위 지정

                xmin = unique_x.min()

                xmax = unique_x.max()

                

                geo_cmd = f"Function({func_str}, {xmin}, {xmax})"

                geogebra_list.append(geo_cmd)

                

                count += 1

                

            except Exception as e:

                continue


    plt.show() # 미리보기 출력

    

    # --- 결과 파일 생성 및 다운로드 ---

    print("\n" + "="*60)

    print(f"총 {len(geogebra_list)}개의 함수 조각이 생성되었습니다.")

    print("지오지브라용 텍스트 파일을 생성하고 다운로드합니다...")

    print("="*60)


    # 지오지브라 리스트 형태로 묶기: {Function(...), Function(...)}

    full_text = "{" + ", ".join(geogebra_list) + "}"

    

    with open("geogebra_commands.txt", "w") as f:

        f.write(full_text)

        

    files.download("geogebra_commands.txt")

    print("\n[완료] 다운로드된 'geogebra_commands.txt' 파일을 열어서 내용을 전체 복사 후,")

    print("지오지브라 입력창에 한 번에 붙여넣으세요.")


# 함수 실행

generate_geogebra_code()