#!/usr/bin/env python3
"""
Decodificador de dados do Apple Maps
Extrai informações legíveis de Protocol Buffers
"""

import sqlite3
import json
import re
from pathlib import Path

MAPS_DB = Path.home() / "Library/Containers/com.apple.geod/Data/Library/Caches/com.apple.geod/Vault/PDPlaceCache.db"

def extract_text_from_binary(binary_data):
    """Extrai strings legíveis de dados binários"""
    if isinstance(binary_data, str):
        return []

    # Converter bytes para string, ignorando caracteres não-printable
    try:
        text = binary_data.decode('utf-8', errors='ignore')
    except:
        text = str(binary_data)

    # Extrair strings legíveis (min 3 caracteres)
    readable = re.findall(r'[a-zA-Z0-9\s\-,\.]{3,}', text)

    # Filtrar resultados
    results = []
    for item in readable:
        item = item.strip()
        if len(item) >= 3:
            # Filtrar keywords técnicos
            skip_keywords = ['apple', 'revgeo', 'ARP', 'bmid', 'en-AR', 'appl']
            if not any(kw in item for kw in skip_keywords):
                results.append(item)

    return list(set(results))  # Remover duplicatas

def parse_coordinates(binary_data):
    """Tenta extrair coordenadas dos dados binários"""
    coords = []
    try:
        # Procurar por padrões de coordenadas (latitude: -90 a 90, longitude: -180 a 180)
        text = binary_data.decode('utf-8', errors='ignore')

        # Procurar números decimais que parecem coordenadas
        numbers = re.findall(r'-?\d+\.\d+', text)

        for num in numbers:
            val = float(num)
            if -90 <= val <= 90 or -180 <= val <= 180:
                coords.append(val)
    except:
        pass

    return coords

def extract_places():
    """Extrai lugares salvos do banco de dados"""
    print("=" * 70)
    print("📍 DECODIFICADOR DE LUGARES SALVOS DO APPLE MAPS")
    print("=" * 70)

    if not MAPS_DB.exists():
        print(f"\n❌ Banco não encontrado: {MAPS_DB}")
        return

    try:
        conn = sqlite3.connect(str(MAPS_DB))
        cursor = conn.cursor()

        # Buscar lugares na tabela pdplaces
        cursor.execute("SELECT pdplacehash, pdplace FROM pdplaces;")
        places = cursor.fetchall()

        print(f"\n✅ Encontrados {len(places)} lugares no cache\n")

        all_results = []

        for i, (place_hash, place_data) in enumerate(places, 1):
            print(f"{'=' * 70}")
            print(f"📍 LUGAR #{i}")
            print(f"{'=' * 70}")
            print(f"ID: {place_hash}")

            # Extrair texto legível
            texts = extract_text_from_binary(place_data)

            # Extrair coordenadas
            coords = parse_coordinates(place_data)

            # Buscar por padrões específicos
            place_info = {
                'id': place_hash,
                'texts_found': texts,
                'coordinates': coords,
                'raw_preview': str(place_data[:200]) if place_data else None
            }

            if texts:
                print(f"\n📝 Textos encontrados:")
                for text in texts[:10]:  # Mostrar até 10 textos
                    print(f"   • {text}")

            if coords:
                print(f"\n🌍 Coordenadas possíveis:")
                # Agrupar em pares lat/lng
                for j in range(0, len(coords) - 1, 2):
                    lat, lng = coords[j], coords[j+1]
                    print(f"   • Lat: {lat}, Lng: {lng}")
                    place_info.setdefault('coord_pairs', []).append({
                        'latitude': lat,
                        'longitude': lng
                    })

            print()
            all_results.append(place_info)

        # Salvar resultados decodificados
        output_file = Path.home() / 'maps_decoded_places.json'
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(all_results, f, indent=2, ensure_ascii=False)

        print(f"{'=' * 70}")
        print(f"\n✅ Dados decodificados salvos em: {output_file}")

        # Resumo
        print(f"\n📊 RESUMO:")
        print(f"   Total de lugares: {len(all_results)}")

        total_texts = sum(len(p.get('texts_found', [])) for p in all_results)
        total_coords = sum(len(p.get('coord_pairs', [])) for p in all_results)

        print(f"   Textos extraídos: {total_texts}")
        print(f"   Coordenadas encontradas: {total_coords}")

        print(f"\n{'=' * 70}")

        conn.close()

    except sqlite3.OperationalError as e:
        if "locked" in str(e):
            print("\n❌ Banco de dados bloqueado!")
            print("   Feche o Maps e processos relacionados:")
            print("   $ killall -9 Maps mapssyncd com.apple.geod")
        else:
            print(f"\n❌ Erro: {e}")

def check_maps_url_table():
    """Verifica tabela maps_url que pode conter links salvos"""
    try:
        conn = sqlite3.connect(str(MAPS_DB))
        cursor = conn.cursor()

        cursor.execute("SELECT * FROM maps_url;")
        urls = cursor.fetchall()

        if urls:
            print("\n🔗 URLs ENCONTRADAS:")
            print("=" * 70)
            for url in urls:
                print(f"   {url}")

        conn.close()
    except:
        pass

if __name__ == '__main__':
    # Fechar Maps antes
    import os
    os.system('killall -9 Maps mapssyncd com.apple.geod 2>/dev/null')

    import time
    time.sleep(2)

    extract_places()
    check_maps_url_table()
