coba analasi aplikasi aku ini : 
import geopandas as gpd
import pandas as pd
from tqdm import tqdm
import time
import os
import sys
import random
from prettytable import PrettyTable
from colorama import init, Fore, Back, Style
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter, landscape
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT

# Initialize colorama
init()

# Define color scheme
class Colors:
    HEADER = Fore.LIGHTRED_EX
    MENU = Fore.LIGHTCYAN_EX
    SUCCESS = Fore.LIGHTGREEN_EX
    WARNING = Fore.LIGHTYELLOW_EX
    ERROR = Fore.LIGHTMAGENTA_EX
    RESET = Style.RESET_ALL


GEOPACKAGE_FOLDER = "Geopackage"
SHAPEFILE_FOLDER = "Shapefiles"
DOCUMENTS_FOLDER = "Documents"

def check_pdf_files_existence():
    pdf_files = [f for f in os.listdir(DOCUMENTS_FOLDER) if f.lower().endswith('.pdf')]
    if not pdf_files:
        return False, "Tidak ada file PDF ditemukan di folder Documents."
    return True, pdf_files

def check_pdf_files(gdf, file_type, pdf_files):
    pdf_issues = []
    pdf_files_lower = set(f.lower() for f in pdf_files)

    for index, row in gdf.iterrows():
        pdf_name = row['pdf_name']
        if pd.isna(pdf_name):
            continue

        pdf_name_lower = pdf_name.lower()
        if pdf_name_lower not in pdf_files_lower:
            if file_type == "Site Allocation":
                site_id = row['site_reference']
            elif file_type == "SHLAA":
                site_id = row['site_id']
            else:  # ShapeFile
                site_id = row['id']

            pdf_issues.append({
                'Site ID': site_id,
                'Site Name': row['site_name'] if 'site_name' in row else row['local_name'],
                'Issues': f"PDF file '{pdf_name}' not found in Documents folder"
            })

            # Return only the first issue found
            if pdf_issues:
                return pdf_issues[:1]

    return pdf_issues

def scan_folder(folder, extension):
    if not os.path.exists(folder):
        print(f"{Colors.ERROR}âŒ Folder '{folder}' tidak ditemukan.{Colors.RESET}")
        return []
    
    files = [f for f in os.listdir(folder) if f.lower().endswith(extension.lower())]
    if not files:
        print(f"{Colors.WARNING}âš ï¸ Tidak ada file dengan ekstensi '{extension}' ditemukan di folder '{folder}'.{Colors.RESET}")
        print(f"{Colors.WARNING}File yang ada di folder: {os.listdir(folder)}{Colors.RESET}")
    return files



def check_gpkg_file(file_path, file_type):
    try:
        gdf = gpd.read_file(file_path)
        issues = []

        if file_type == "Site Allocation":
            site_id_column = 'site_reference'
        elif file_type == "SHLAA":
            site_id_column = 'site_id'
        else:
            raise ValueError("Invalid file type for GPKG")

        # Get the most common non-null values for pdf_name and external_url
        common_pdf_name = gdf['pdf_name'].dropna().mode().iloc[0] if not gdf['pdf_name'].dropna().empty else None
        common_external_url = gdf['external_url'].dropna().mode().iloc[0] if not gdf['external_url'].dropna().empty else None

        for index, row in gdf.iterrows():
            row_issues = []

            # Check for empty values
            for column in ['pdf_name', 'pdf_page', 'external_url']:
                if pd.isna(row[column]):
                    row_issues.append(f"{column}: null")

            # Check for inconsistent pdf_name and external_url only if they are not null
            if not pd.isna(row['pdf_name']) and common_pdf_name and row['pdf_name'] != common_pdf_name:
                row_issues.append(f"pdf_name: inconsistent ('{row['pdf_name']}' != '{common_pdf_name}')")
            if not pd.isna(row['external_url']) and common_external_url and row['external_url'] != common_external_url:
                row_issues.append(f"external_url: inconsistent ('{row['external_url']}' != '{common_external_url}')")

            # If there are any issues, add them to the issues list
            if row_issues:
                issues.append({
                    'Site ID': row['site_id'],
                    'Site Name': row['site_name'],
                    'Issues': '; '.join(row_issues)
                })

        # Check for duplicate rows
        duplicate_ids = gdf[gdf['site_id'].duplicated(keep=False)]
        for _, row in duplicate_ids.iterrows():
            issues.append({
                'Site ID': row['site_id'],
                'Site Name': row['site_name'],
                'Issues': 'Duplicate ID'
            })

        return gdf, issues
    except Exception as e:
        return None, [{'Site ID': 'N/A', 'Site Name': 'N/A', 'Issues': str(e)}]

def generate_pdf_report(file_path, file_type, issues, statistics):
    doc = SimpleDocTemplate(f"{file_path}_report.pdf", pagesize=landscape(letter))
    elements = []
    styles = getSampleStyleSheet()
    
    # Check if 'BodyText' style already exists, if not, add it
    if 'BodyText' not in styles:
        styles.add(ParagraphStyle(name='BodyText', parent=styles['Normal'], alignment=TA_LEFT, wordWrap='CJK'))

    # Title
    elements.append(Paragraph(f"Analysis Report for {file_type}: {os.path.basename(file_path)}", styles['Title']))
    elements.append(Spacer(1, 12))

    # Statistics
    elements.append(Paragraph("File Statistics:", styles['Heading2']))
    stats_data = [["Total features", str(statistics['total_features'])],
                  ["Geometry type", statistics['geometry_type']],
                  ["CRS", statistics['crs']]]
    stats_table = Table(stats_data, colWidths=[100, 400])
    stats_table.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
        ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
        ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
        ('FONTSIZE', (0, 0), (-1, 0), 12),
        ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
        ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
        ('TEXTCOLOR', (0, 1), (-1, -1), colors.black),
        ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
        ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
        ('FONTSIZE', (0, 1), (-1, -1), 10),
        ('TOPPADDING', (0, 1), (-1, -1), 6),
        ('BOTTOMPADDING', (0, 1), (-1, -1), 6),
        ('GRID', (0, 0), (-1, -1), 1, colors.black)
    ]))
    elements.append(stats_table)
    elements.append(Spacer(1, 12))

    # Issues
    if issues:
        elements.append(Paragraph(f"Issues Found ({len(issues)}):", styles['Heading2']))
        issues_data = [["No.", "Site ID", "Site Name", "Issues"]]
        for i, issue in enumerate(issues, 1):
            issues_data.append([
                str(i),
                Paragraph(issue['Site ID'], styles['BodyText']),
                Paragraph(issue['Site Name'], styles['BodyText']),
                Paragraph(issue['Issues'], styles['BodyText'])
            ])
        issues_table = Table(issues_data, colWidths=[30, 70, 100, 500])
        issues_table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
            ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
            ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
            ('FONTSIZE', (0, 0), (-1, 0), 12),
            ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
            ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
            ('TEXTCOLOR', (0, 1), (-1, -1), colors.black),
            ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
            ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
            ('FONTSIZE', (0, 1), (-1, -1), 10),
            ('TOPPADDING', (0, 1), (-1, -1), 6),
            ('BOTTOMPADDING', (0, 1), (-1, -1), 6),
            ('GRID', (0, 0), (-1, -1), 1, colors.black),
            ('VALIGN', (0, 0), (-1, -1), 'TOP')
        ]))
        elements.append(issues_table)
    else:
        elements.append(Paragraph("No issues found.", styles['Normal']))

    # Tambahkan tanda tangan
    signature = "KandoOculus by KandoAlchemist"
    elements.append(Spacer(1, 50))  # Beri ruang sebelum tanda tangan
    elements.append(Table([[signature]], hAlign='CENTER'))

    doc.build(elements)
    print(f"{Colors.SUCCESS}PDF report generated: {file_path}_report.pdf{Colors.RESET}")    

def check_shapefile(file_path):
    try:
        gdf = gpd.read_file(file_path)
        issues = []
        
        # Check for duplicate IDs
        duplicate_ids = gdf[gdf['id'].duplicated(keep=False)]
        for _, row in duplicate_ids.iterrows():
            issues.append({
                'Site ID': row['id'],
                'Site Name': row['local_name'],
                'Issues': 'Duplicate ID'
            })

        # Check authority consistency and null values
        null_auth = gdf['authority'].isnull()
        if null_auth.any():
            null_auth_ids = gdf[null_auth]['id'].tolist()
            issues.extend([{'Site ID': id, 'Site Name': gdf.loc[gdf['id'] == id, 'local_name'].iloc[0], 'Issues': 'Authority is null'} for id in null_auth_ids])
        
        non_null_auth = gdf[~null_auth]
        if non_null_auth['authority'].nunique() > 1:
            inconsistent_auth_ids = non_null_auth[non_null_auth['authority'] != non_null_auth['authority'].mode().iloc[0]]['id'].tolist()
            issues.extend([{'Site ID': id, 'Site Name': gdf.loc[gdf['id'] == id, 'local_name'].iloc[0], 'Issues': 'Inconsistent authority'} for id in inconsistent_auth_ids])

        # Check pdf_name consistency and null values
        null_pdf = gdf['pdf_name'].isnull()
        if null_pdf.any():
            null_pdf_ids = gdf[null_pdf]['id'].tolist()
            issues.extend([{'Site ID': id, 'Site Name': gdf.loc[gdf['id'] == id, 'local_name'].iloc[0], 'Issues': 'PDF name is null'} for id in null_pdf_ids])
        
        non_null_pdf = gdf[~null_pdf]
        if non_null_pdf['pdf_name'].nunique() > 1:
            inconsistent_pdf_ids = non_null_pdf[non_null_pdf['pdf_name'] != non_null_pdf['pdf_name'].mode().iloc[0]]['id'].tolist()
            issues.extend([{'Site ID': id, 'Site Name': gdf.loc[gdf['id'] == id, 'local_name'].iloc[0], 'Issues': 'Inconsistent PDF name'} for id in inconsistent_pdf_ids])

        # Check local_name and category consistency
        grouped = gdf.groupby('local_name')
        for name, group in grouped:
            if group['category'].nunique() > 1:
                main_category = group['category'].mode().iloc[0]
                inconsistent_rows = group[group['category'] != main_category]
                for _, row in inconsistent_rows.iterrows():
                    issues.append({
                        'Site ID': row['id'],
                        'Site Name': name,
                        'Issues': f"Inconsistent category: '{row['category']}' (expected '{main_category}')"
                    })

        return gdf, issues
    except Exception as e:
        print(f"âŒ Error reading ShapeFile: {str(e)}")
        return None, []

def check_file_validity(file_path, file_type):
    if not os.path.exists(file_path):
        return False, f"{Colors.ERROR}Error: File tidak ditemukan.{Colors.RESET}"
    
    try:
        gdf = gpd.read_file(file_path)
    except Exception:
        return False, f"{Colors.ERROR}Error: File tidak dapat dibaca sebagai file geografis.{Colors.RESET}"
    
    if file_type == "SHLAA" and 'site_id' not in gdf.columns:
        return False, f"{Colors.ERROR}Error: This is not a SHLAA file bro, please choose the correct file.{Colors.RESET}"
    elif file_type == "Site Allocation" and 'site_reference' not in gdf.columns:
        return False, f"{Colors.ERROR}Error: This is not a Site Allocation file bro, please choose the correct file.{Colors.RESET}"
    
    return True, gdf


def initial_loading_screen():
    clear_screen()
    quote = '"And be patient, for indeed, the promise of Allah is truth." (QS. Ar-Rum: 60)'
    print(f"\n{Colors.MENU}Assalamu'alaikum Brother!{Colors.RESET}")
    print(f"{Colors.MENU}{quote}{Colors.RESET}\n")
    pikachu_art = """
    â €â €â €â£€â¡€â €â €â €â €â €â €â €â €â €â €â €â €â €â¡ â¡„â €â €
    â¢°â ’â ’â¢»â£¿â£¶â¡’â ’â ’â ’â ’â ’â ’â ’â ’â ’â¡¶â Šâ£°â£“â¡’â¡†
    â¢¸â¢¸â¢»â£­â¡™â¢¿â£¿â£â¡‰â ‰â¡‡â£¯â ‰â ‰â£©â ‹â¢€â£”â •â¢«â¡‡â¡‡
    â¢¸â¢¸â£ˆâ¡»â£¿â£¶â£½â¡¸â£¿â£¦â¡‡â£§â  â Šâ£¸â¢¶â ‹â¢â¡¤â §â¡§â¡‡
    â¢¸â¢¸â »â£¿â£¶â£â ›â£¿â£®â¢»â Ÿâ£â£ â žâ â£¼â¡¶â ‹â¢€â£´â¡‡â¡‡
    â¢¸â¢¸â£¿â£¶â£â »â ¼â£®â¡•â¢â¡¤â¢¿â¢â¡´â Šâ£¸â£µâ žâ ‹â¢ â¡‡â¡‡
    â¢¸â¢˜â£›â¡»â£¿â£§â¢³â£¿â£§â Žâ¢€â£¾â ‹â¡ â žâ¢±â¢‡â£ â¡´â Ÿâ¡‡â¡‡
    â¢¸â¢¸â ¹â£¿â£·â£Žâ£‰â£»â¢â¡”â¢â¢¿â¡â¢€â£¤â¢¾â¡Ÿâ â£€â£Žâ¡‡â¡‡
    â¢¸â¢¸â ²â£¶â£­â¡›â šâ¢¿â¢‹â¡”â¢â£¼â Ÿâ¢‹â£ â£¼â –â ‹â¢â Žâ¡‡â¡‡
    â¢¸â¢¸â¢¤â£¬â£›â ¿â žâ£¿â¢‹â ”â£‰â£¾â –â ‹â¢â£¯â¡´â žâ¢ƒâ ‚â¡‡â¡‡
    â¢¸â¢¸â €â¢™â£»â¢¿â£§â£¾â¡µâ šâ£‰â£¯â ¶â ›â£¹â£§â ¤â¢®â â €â¡‡â¡‡
    â ¸â£˜â ¢â£„â ™â ¿â¢·â¡¡â –â£‹â£½â ¥â ’â£©â£Ÿâ£¤â£”â£â¡¤â –â£ƒâ ‡
    â €â €â ™â ¢â¢â£»â¡¿â ’â¢‰â£´â£—â£šâ£½â£‹â£€â£¤â£Šâ ¥â ’â ‰â €â €
    â €â €â €â¢€â£”â ¥â ’â¢®â£™â ¾â €â ·â£šâ¡­â žâ ‰â ›â ¦â£€â €â €â €
    â €â €â €â ‰â €â €â €â €â ˆâ ‘â ’â ‹â â €â €â €â €â €â €â €â €â €
    """
    
    loading_text = "KandoOculus Evo-0 Loading"
    dots = ["", ".", "..", "..."]
    
    for _ in range(1):  # Repeat animation 10 times
        for dot in dots:
            clear_screen()
            print(f"\n{Colors.MENU}Assalamu'alaikum Brother!{Colors.RESET}")
            print(f"{Colors.MENU}{quote}{Colors.RESET}\n")
            print(f"{Colors.HEADER}{pikachu_art}{Colors.RESET}")
            print(f"\n{Colors.HEADER}{loading_text}{dot}{Colors.RESET}")
            time.sleep(0.3)

    clear_screen()
    print(f"\n{Colors.SUCCESS}Assalamu'alaikum Brother!{Colors.RESET}")
    print(f"{Colors.SUCCESS}{quote}{Colors.RESET}\n")
    print(f"{Colors.SUCCESS}{pikachu_art}{Colors.RESET}")
    print(f"\n{Colors.SUCCESS}KandoOculus Evo-0 is ready!{Colors.RESET}")
    time.sleep(2)
    clear_screen()



def process_file(file_path, file_type):
    print(f"\n{Colors.HEADER}ðŸš€ Analyzing {file_type} file: {file_path}{Colors.RESET}")
    print(f"{Colors.MENU}Buckle up for a geo-tastic adventure! ðŸŒâœ¨{Colors.RESET}")

    # Check PDF files existence first
    pdf_exist, pdf_result = check_pdf_files_existence()
    if not pdf_exist:
        print(f"{Colors.ERROR}{pdf_result}{Colors.RESET}")
        return



    is_valid, result = check_file_validity(file_path, file_type)
    if not is_valid:
        print(result)
        return

    gdf = result
    print(f"{Colors.SUCCESS}File was successfully loaded ðŸŽ‰{Colors.RESET}")

    pdf_issues = check_pdf_files(gdf, file_type, pdf_result)
    if pdf_issues:
        print(f"\n{Colors.ERROR}âŒ Critical issue found:{Colors.RESET}")
        issue = pdf_issues[0]
        print(f"- {issue['Issues']}")
        print(f"\n{Colors.WARNING}Process stopped due to PDF mismatch. Please resolve this issue before continuing.{Colors.RESET}")
        return  # Stop processing here

    print(f"\n{Colors.SUCCESS}âœ… All PDF files mentioned in the data are present in the Documents folder.{Colors.RESET}")

    total_steps = 100
    emojis = ["ðŸ¦„", "ðŸŒˆ", "ðŸ•", "ðŸŽ¨", "ðŸ”®", "ðŸŽ­", "ðŸŒŸ", "ðŸ‰", "ðŸŽ©", "ðŸ§™â€â™‚ï¸"]
    messages = [
        "Sprinkling magic pixie dust on the data... âœ¨",
        "Teaching electrons to dance the cha-cha... ðŸ’ƒ",
        "Unraveling the mysteries of the geoverse... ðŸŒ",
        "Polishing polygons to perfection... ðŸ’Ž",
        "Untangling spaghetti code with a fork... ðŸ",
        "Waking up sleepy coordinates... â°",
        "Herding wild geometries into shape... ðŸ‘",
        "Conducting a symphony of spatial harmonies... ðŸŽµ",
        "Brewing a potion of geospatial excellence... ðŸ§ª",
        "Knitting a cozy sweater for cold data... ðŸ§¶",
        "Teaching GPS to speak fluent emoji... ðŸ“ðŸ˜Ž",
        "Aligning ley lines for optimal data flow... ðŸ”®",
        "Tuning the quantum geolocation resonator... ðŸŽ¸",
        "Feeding the hungry database gremlins... ðŸ‘¾ðŸ”",
        "Applying sunscreen to prevent data burn..0. ðŸ§´"
    ]

    for i in range(total_steps):
        progress = (i + 1) / total_steps
        bar_length = 20
        filled_length = int(bar_length * progress)
        bar = 'â–ˆ' * filled_length + 'â–‘' * (bar_length - filled_length)

        emoji = random.choice(emojis)
        message = random.choice(messages)

        print(f"\r{emoji} [{bar}] {progress:.0%} | {message}", end='', flush=True)
        time.sleep(0.05)

    print(f"\nâœ… The file has been checked bro! ðŸš€âœ¨")

    if file_type == "ShapeFile":
        gdf, issues = check_shapefile(file_path)
    else:  # GPKG
        gdf, issues = check_gpkg_file(file_path, file_type)

    if gdf is None:
        print(f"âŒ Oops! We've hit a snag: {issues[0]['Issues']}")
    if issues:
        print(f"\nâ— Found {len(issues)} issues feature(s):")
        table = PrettyTable()
        table.field_names = ["No.", "Site ID", "Site Name", "Issues"]
        table.align["Issues"] = "l"  # Left-align the Issues column
        table.max_width["Issues"] = 50  # Set max width for Issues column
        for index, issue in enumerate(issues, start=1):
            table.add_row([
                index,
                issue['Site ID'],
                issue['Site Name'],
                issue['Issues']
            ])
        print(table)
        
        # Add option to export PDF
        export_choice = input(f"\n{Colors.MENU}Do you want to export the issues to a PDF? (y/n): {Colors.RESET}")
        if export_choice.lower() == 'y':
            generate_pdf_report(file_path, file_type, issues, {
                'total_features': len(gdf),
                'geometry_type': str(gdf.geometry.geom_type.value_counts().to_dict()),
                'crs': str(gdf.crs)
            })
    else:
        print(f"\nðŸŽ‰ Alhamdulillah! Your {file_type} file has been successfully checked and there are no problems with it! ðŸ†")
        print("\nðŸ“Š File Statistics:")
        print(f"Total features: {len(gdf)}")
        print(f"Geometry type: {gdf.geometry.geom_type.value_counts().to_dict()}")
        print(f"Coordinate Reference System (CRS): {gdf.crs}")

def clear_screen():
    os.system('cls' if os.name == 'nt' else 'clear')

def print_cute_menu():
    clear_screen()
    print(f"""{Colors.HEADER}
  ðŸ”¥Welcome to KandoOculus Evo-0ðŸ”¥
          
â¬œâ¬œâ¬œâ¬œâ¬œâ¬œâ¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬œâ¬œâ¬œâ¬œâ¬œâ¬œ
â¬œâ¬œâ¬œâ¬œâ¬›â¬›ðŸŸ¥â¬›â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬›â¬œâ¬œâ¬œâ¬œ
â¬œâ¬œâ¬œâ¬›ðŸŸ¥ðŸŸ¥â¬›â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬œâ¬œâ¬œ
â¬œâ¬œâ¬›ðŸŸ¥ðŸŸ¥â¬›â¬›â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬œâ¬œ
â¬œâ¬›ðŸŸ¥ðŸŸ¥â¬›â¬›â¬›â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬œ
â¬œâ¬›ðŸŸ¥ðŸŸ¥â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬œ
â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›
â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›
â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›ðŸŸ¥ðŸŸ¥â¬›
â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬›â¬›â¬›â¬›ðŸŸ¥ðŸŸ¥â¬›â¬›â¬›â¬›â¬›â¬›ðŸŸ¥â¬›
â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬›â¬›â¬›â¬›ðŸŸ¥ðŸŸ¥â¬›â¬›â¬›â¬›â¬›â¬›ðŸŸ¥â¬›
â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›ðŸŸ¥â¬›
â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›ðŸŸ¥ðŸŸ¥â¬›ðŸŸ¥â¬›
â¬œâ¬›â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›ðŸŸ¥ðŸŸ¥â¬›â¬›â¬œ
â¬œâ¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬œ
â¬œâ¬œâ¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬œâ¬œ
â¬œâ¬œâ¬œâ¬›ðŸŸ¥ðŸŸ¥â¬›â¬›â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬œâ¬œâ¬œ
â¬œâ¬œâ¬œâ¬œâ¬›â¬›ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥ðŸŸ¥â¬›â¬›â¬œâ¬œâ¬œâ¬œ
â¬œâ¬œâ¬œâ¬œâ¬œâ¬œâ¬›â¬›â¬›â¬›â¬›â¬›â¬›â¬œâ¬œâ¬œâ¬œâ¬œâ¬œ
{Colors.RESET}""")
    print(f"{Colors.MENU}")
    print("1. ðŸ“œ Scan .gpkg Site Allocation")
    print("2. ðŸ“œ Scan .gpkg SHLAA ")
    print("3. ðŸ“œ Scan .shp Local Policy")
    print("4. ðŸš« Exit")
    print(f"{Colors.RESET}")

def animate_loading():
    frames = ["ðŸŒ", "ðŸŒŽ", "ðŸŒ"]

    for _ in range(5):
        for frame in frames:
            print(f"\r{Colors.MENU}Preparing your geo-adventure {frame}{Colors.RESET}", end="", flush=True)
            time.sleep(0.1)
    print(f"\r{Colors.SUCCESS}Ready to explore! ðŸš€           {Colors.RESET}")

def main():

    initial_loading_screen() 

    while True:
        print_cute_menu()
        choice = input(f"{Colors.MENU}Enter your choice (1-4): {Colors.RESET}")
        
        if choice in ['1', '2']:
            clear_screen()
            file_type = "Site Allocation" if choice == '1' else "SHLAA"
            print(f"{Colors.HEADER}You selected: Check {file_type} GPKG files{Colors.RESET}")
            animate_loading()
            
            gpkg_files = scan_folder(GEOPACKAGE_FOLDER, '.gpkg')

            if not gpkg_files:
                print(f"{Colors.ERROR}âŒ No GPKG files found in '{GEOPACKAGE_FOLDER}' folder.{Colors.RESET}")
                input(f"\n{Colors.MENU}Press Enter to return to the main menu...{Colors.RESET}")
                continue

            print(f"\n{Colors.SUCCESS}Found {len(gpkg_files)} GPKG files:{Colors.RESET}")
            for i, file in enumerate(gpkg_files, 1):
                print(f"{Colors.MENU}{i}. {file}{Colors.RESET}")

            file_choice = input(f"\n{Colors.MENU}Enter the number of the file to check (or 'all' to check all files): {Colors.RESET}")

            if file_choice.lower() == 'all':
                for file in gpkg_files:
                    process_file(os.path.join(GEOPACKAGE_FOLDER, file), file_type)
            elif file_choice.isdigit() and 1 <= int(file_choice) <= len(gpkg_files):
                selected_file = gpkg_files[int(file_choice) - 1]
                process_file(os.path.join(GEOPACKAGE_FOLDER, selected_file), file_type)
            else:
                print(f"{Colors.ERROR}Invalid choice. Please try again.{Colors.RESET}")
            
            input(f"\n{Colors.MENU}Press Enter to return to the main menu...{Colors.RESET}")
        
        elif choice == '3':
            clear_screen()
            print(f"{Colors.HEADER}You selected: Check Local Policy ShapeFiles{Colors.RESET}")
            animate_loading()
            
            shp_files = scan_folder(SHAPEFILE_FOLDER, '.shp')

            if not shp_files:
                print(f"{Colors.ERROR}âŒ No ShapeFiles found in '{SHAPEFILE_FOLDER}' folder.{Colors.RESET}")
                input(f"\n{Colors.MENU}Press Enter to return to the main menu...{Colors.RESET}")
                continue

            print(f"\n{Colors.SUCCESS}Found {len(shp_files)} ShapeFiles:{Colors.RESET}")
            for i, file in enumerate(shp_files, 1):
                print(f"{Colors.MENU}{i}. {file}{Colors.RESET}")

            file_choice = input(f"\n{Colors.MENU}Enter the number of the file to check (or 'all' to check all files): {Colors.RESET}")

            if file_choice.lower() == 'all':
                for file in shp_files:
                    process_file(os.path.join(SHAPEFILE_FOLDER, file), "ShapeFile")
            elif file_choice.isdigit() and 1 <= int(file_choice) <= len(shp_files):
                selected_file = shp_files[int(file_choice) - 1]
                process_file(os.path.join(SHAPEFILE_FOLDER, selected_file), "ShapeFile")
            else:
                print(f"{Colors.ERROR}Invalid choice. Please try again.{Colors.RESET}")
            
            input(f"\n{Colors.MENU}Press Enter to return to the main menu...{Colors.RESET}")
        
        elif choice == '4':
            print(f"{Colors.SUCCESS}Thank you for using the KandoOculus Evo-0. Wassalamua'laikum! ðŸ‘‹{Colors.RESET}")
            break
        else:
            print(f"{Colors.ERROR}Invalid choice. Please try again.{Colors.RESET}")
            time.sleep(2)

if __name__ == "__main__":
    main()