#!/usr/bin/env python

# kismet2earth 1.0
# Author: Andrea Grandi <a.grandi AT gmail com>
# License: GPL2
# Copyright: Andrea Grandi 2010 - This cose is partially based on PyKismetKml (http://code.google.com/p/pykismetkml)

# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

from __future__ import annotations

import argparse
import sqlite3
from pathlib import Path
from xml.sax.saxutils import escape


KML_HEADER = """<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.0">
  <Folder><name>Wifi Networks</name>
"""

KML_FOOTER = """  </Folder></kml>
"""

def xml_text(value) -> str:
    """Convert any value to safe XML text."""
    if value is None:
        return ""
    return escape(str(value))


def iconify(encryption: str | None) -> str:
    enc = (encryption or "").upper()
    if enc == "WPA":
        href = "images/node_wpa.png"
    elif enc == "WEP":
        href = "images/node_wep.png"
    else:
        href = "images/node_open.png"

    return f"<Style><IconStyle><Icon><href>{href}</href></Icon></IconStyle></Style>"


def read_wifi(db_path: Path, *, wpa: bool, wep: bool, open_: bool, all_: bool) -> list[tuple]:
    sql = "SELECT bssid, essid, lat, lon, encryption, manufacturer, channel FROM networks"
    params: list[str] = []

    if not all_:
        wanted: list[str] = []
        if wpa:
            wanted.append("WPA")
        if wep:
            wanted.append("WEP")
        if open_:
            wanted.append("Open")

        # If user didn't pass any filter flags, default to "all"
        if wanted:
            placeholders = ",".join("?" for _ in wanted)
            sql += f" WHERE encryption IN ({placeholders})"
            params.extend(wanted)

    with sqlite3.connect(str(db_path)) as conn:
        cur = conn.execute(sql, params)
        return list(cur.fetchall())


def export_wifi(rows: list[tuple], out_path: Path) -> None:
    with out_path.open("w", encoding="utf-8", newline="\n") as f:
        f.write(KML_HEADER)

        for (bssid, essid, lat, lon, encryption, manufacturer, channel) in rows:
            # KML/XML safety: escape text fields that might contain special chars
            essid_safe = xml_text(essid)
            bssid_safe = xml_text(bssid)
            manufacturer_safe = xml_text(manufacturer)
            channel_safe = xml_text(channel)
            encryption_safe = xml_text(encryption)

            icon = iconify(encryption)

            # lat/lon in your DB are stored as TEXT; keep as-is but coerce None to empty string
            lat_s = "" if lat is None else str(lat)
            lon_s = "" if lon is None else str(lon)

            f.write(
                f"""  <Folder>
    <name>{essid_safe}</name>

    <LookAt>
      <longitude>{lon_s}</longitude>
      <latitude>{lat_s}</latitude>
      <range>100</range>
      <tilt>54</tilt>
      <heading>-35</heading>
    </LookAt>

    <description></description>
    <Placemark>
      <name>{essid_safe}</name>
      <description><![CDATA[
        BSSID: {bssid_safe}<br>
        Manufacturer: {manufacturer_safe}<br>
        Channel: {channel_safe}<br>
        Encryption: {encryption_safe}<br>
      ]]></description>
      <visibility>1</visibility>
      <open>0</open>

      <LookAt>
        <longitude>{lon_s}</longitude>
        <latitude>{lat_s}</latitude>
        <range>100</range>
        <tilt>54</tilt>
        <heading>-35</heading>
      </LookAt>

      {icon}

      <Point>
        <altitudeMode>clampedToGround</altitudeMode>
        <extrude>0</extrude>
        <tessellate>0</tessellate>
        <coordinates>{lon_s},{lat_s},0</coordinates>
      </Point>
    </Placemark>
  </Folder>

"""
            )

        f.write(KML_FOOTER)


def build_argparser() -> argparse.ArgumentParser:
    p = argparse.ArgumentParser(description="Export Wi-Fi networks from SQLite to KML (Google Earth).")
    p.add_argument("-i", "--input", dest="database", required=True, type=Path, help="Path to SQLite3 database.")
    p.add_argument("-o", "--output", dest="filename", required=True, type=Path, help="Path to .kml output file.")
    p.add_argument("-w", "--wpa", action="store_true", help="Export WPA encrypted networks.")
    p.add_argument("-p", "--wep", action="store_true", help="Export WEP encrypted networks.")
    p.add_argument("-n", "--open", dest="open_", action="store_true", help="Export Open networks.")
    p.add_argument("-a", "--all", dest="all_", action="store_true", help="Export all networks.")
    return p


def main() -> int:
    args = build_argparser().parse_args()
    rows = read_wifi(args.database, wpa=args.wpa, wep=args.wep, open_=args.open_, all_=args.all_)
    export_wifi(rows, args.filename)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())