import cv2
import numpy as np
from pdf2image import convert_from_path

def pdf_to_image(pdf_path):
    # Convert PDF to list of PIL Images
    pages = convert_from_path(pdf_path, dpi=300)  # dpi controls resolution

    # Take the first page (you can loop if needed)
    pil_image = pages[0]

    # Convert PIL Image to OpenCV format
    cv_image = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)

    # Show the image
    # cv2.imshow("PDF Page", cv_image)
    # cv2.waitKey(0)
    # cv2.destroyAllWindows()

    # Save if needed
    return cv_image

import cv2
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import re
from io import BytesIO
import math
import requests

def utmToLatLng(zone, easting, northing, northernHemisphere=True):
    if not northernHemisphere:
        northing = 10000000 - northing

    a = 6378137
    e = 0.081819191
    e1sq = 0.006739497
    k0 = 0.9996

    arc = northing / k0
    mu = arc / (a * (1 - math.pow(e, 2) / 4.0 - 3 * math.pow(e, 4) / 64.0 - 5 * math.pow(e, 6) / 256.0))

    ei = (1 - math.pow((1 - e * e), (1 / 2.0))) / (1 + math.pow((1 - e * e), (1 / 2.0)))

    ca = 3 * ei / 2 - 27 * math.pow(ei, 3) / 32.0

    cb = 21 * math.pow(ei, 2) / 16 - 55 * math.pow(ei, 4) / 32
    cc = 151 * math.pow(ei, 3) / 96
    cd = 1097 * math.pow(ei, 4) / 512
    phi1 = mu + ca * math.sin(2 * mu) + cb * math.sin(4 * mu) + cc * math.sin(6 * mu) + cd * math.sin(8 * mu)

    n0 = a / math.pow((1 - math.pow((e * math.sin(phi1)), 2)), (1 / 2.0))

    r0 = a * (1 - e * e) / math.pow((1 - math.pow((e * math.sin(phi1)), 2)), (3 / 2.0))
    fact1 = n0 * math.tan(phi1) / r0

    _a1 = 500000 - easting
    dd0 = _a1 / (n0 * k0)
    fact2 = dd0 * dd0 / 2

    t0 = math.pow(math.tan(phi1), 2)
    Q0 = e1sq * math.pow(math.cos(phi1), 2)
    fact3 = (5 + 3 * t0 + 10 * Q0 - 4 * Q0 * Q0 - 9 * e1sq) * math.pow(dd0, 4) / 24

    fact4 = (61 + 90 * t0 + 298 * Q0 + 45 * t0 * t0 - 252 * e1sq - 3 * Q0 * Q0) * math.pow(dd0, 6) / 720

    lof1 = _a1 / (n0 * k0)
    lof2 = (1 + 2 * t0 + Q0) * math.pow(dd0, 3) / 6.0
    lof3 = (5 - 2 * Q0 + 28 * t0 - 3 * math.pow(Q0, 2) + 8 * e1sq + 24 * math.pow(t0, 2)) * math.pow(dd0, 5) / 120
    _a2 = (lof1 - lof2 + lof3) / math.cos(phi1)
    _a3 = _a2 * 180 / math.pi

    latitude = 180 * (phi1 - fact1 * (fact2 + fact3 + fact4)) / math.pi

    if not northernHemisphere:
        latitude = -latitude

    longitude = ((zone > 0) and (6 * zone - 183.0) or 3.0) - _a3

    return (latitude, longitude)

def latlon_to_mercator(lat, lon):
    scale = 6378137
    x = scale * math.radians(lon)
    y = scale * math.log(math.tan(math.pi / 4 + math.radians(lat) / 2))
    return x, y

def mercator_to_latlon(mx, my):
    scale = 6378137
    lon = math.degrees(mx / scale)
    lat = math.degrees(2 * math.atan(math.exp(my / scale)) - math.pi / 2)
    return lat, lon

def deg2num(lat_deg, lon_deg, zoom):
    lat_rad = math.radians(lat_deg)
    n = 2.0 ** zoom
    xtile = int((lon_deg + 180.0) / 360.0 * n)
    ytile = int((1.0 - math.asinh(math.tan(lat_rad)) / math.pi) / 2.0 * n)
    return xtile, ytile

def draw(lines_text, img1, img2, latitude, longitude):
    # Compute UTM zone and hemisphere
    zone = int((longitude + 180) / 6) + 1
    northern_hemisphere = latitude >= 0

    # Convert all coordinates to lat/lon and then to mercator
    points_latlon = {}
    points_merc = {}
    for k, v in lines_text["coordinates"].items():
        easting, northing = v[0]
        lat, lon = utmToLatLng(zone, easting, northing, northern_hemisphere)
        points_latlon[k] = (lat, lon)
        mx, my = latlon_to_mercator(lat, lon)
        points_merc[k] = [mx, my]

    # Find bounds in mercator
    all_mx = [p[0] for p in points_merc.values()]
    all_my = [p[1] for p in points_merc.values()]
    min_mx, max_mx = min(all_mx), max(all_mx)
    min_my, max_my = min(all_my), max(all_my)

    # Add margin in mercator space
    width = max_mx - min_mx
    height = max_my - min_my
    margin_x = 0.05 * width
    margin_y = 0.05 * height
    expanded_min_mx = min_mx - margin_x
    expanded_max_mx = max_mx + margin_x
    expanded_min_my = min_my - margin_y
    expanded_max_my = max_my + margin_y

    # Convert expanded mercator bounds to lat/lon
    expanded_min_lat, _ = mercator_to_latlon(0, expanded_min_my)
    expanded_max_lat, _ = mercator_to_latlon(0, expanded_max_my)
    _, expanded_min_lon = mercator_to_latlon(expanded_min_mx, 0)
    _, expanded_max_lon = mercator_to_latlon(expanded_max_mx, 0)

    # Choose zoom level
    zoom = 18

    # Find tile range with padding
    min_xtile, min_ytile = deg2num(expanded_max_lat, expanded_min_lon, zoom)
    max_xtile, max_ytile = deg2num(expanded_min_lat, expanded_max_lon, zoom)
    min_xtile -= 1
    max_xtile += 1
    min_ytile -= 1
    max_ytile += 1

    num_x = max_xtile - min_xtile + 1
    num_y = max_ytile - min_ytile + 1

    # Create big image
    tile_px = 256
    big_image = Image.new('RGB', (tile_px * num_x, tile_px * num_y))

    for xt in range(min_xtile, max_xtile + 1):
        for yt in range(min_ytile, max_ytile + 1):
            # Use custom tiles
            url = f"https://s3.ap-south-2.amazonaws.com/prod-assets.mypropertyqr.in/survey_border/{xt}/{yt}.png"
            try:
                resp = requests.get(url)
                resp.raise_for_status()
                tile_img = Image.open(BytesIO(resp.content))
                x_pos = (xt - min_xtile) * tile_px
                y_pos = (yt - min_ytile) * tile_px
                big_image.paste(tile_img, (x_pos, y_pos))
            except:
                pass  # Skip if tile fetch fails

    # Compute extent in mercator
    RADIUS = 6378137
    earth_circ = 2 * math.pi * RADIUS
    tile_size = earth_circ / (2 ** zoom)
    left = -earth_circ / 2 + min_xtile * tile_size
    right = left + num_x * tile_size
    top = earth_circ / 2 - min_ytile * tile_size
    bottom = top - num_y * tile_size

    # Set figsize
    aspect = height / width if width else 1
    fig_width = 10
    fig_height = fig_width * aspect
    fig, ax = plt.subplots(figsize=(fig_width, fig_height))

    # Add background with 70% opacity
    ax.imshow(big_image, extent=[left, right, bottom, top], alpha=0.7)

    ax.set_xlim(expanded_min_mx, expanded_max_mx)
    ax.set_ylim(expanded_min_my, expanded_max_my)
    ax.set_aspect('equal')
    plt.axis('off')

    # Draw lines
    for line in lines_text["lines"]:
        coords_keys = line["coordinates"]
        if len(coords_keys) < 2:
            continue
        xs = [points_merc[k][0] for k in coords_keys]
        ys = [points_merc[k][1] for k in coords_keys]
        dash_str = line["dashes"]
        dashes_match = re.search(r'\[\s*(.*?)\s*\]', dash_str)
        if dashes_match:
            dashes = [float(n) for n in re.findall(r'\d+', dashes_match.group(1))]
        else:
            dashes = None
        linewidth = float(line["strokewidth"])
        ax.plot(xs, ys, color='black', linewidth=linewidth, dashes=dashes if dashes else None)

    # Add point labels in red
    for k, pos in points_merc.items():
        ax.text(pos[0], pos[1], k, fontsize=6, color='red', ha='center', va='center')

    # Add text from subdivision_list
    for sub_key, sub_value in lines_text["subdivision_list"].items():
        if len(sub_value) >= 1 and isinstance(sub_value[0], list) and len(sub_value[0]) == 2:
            easting, northing = sub_value[0]
            lat, lon = utmToLatLng(zone, easting, northing, northern_hemisphere)
            mx, my = latlon_to_mercator(lat, lon)
            ax.text(mx, my, sub_key, fontsize=8, ha='center', va='center', color='blue')

    fig.tight_layout(pad=0)

    # Save to BytesIO as high-res PNG
    buf = BytesIO()
    fig.savefig(buf, format='png', bbox_inches='tight', pad_inches=0, dpi=300)
    buf.seek(0)
    img_drawn = Image.open(buf)

    # Step 2: Convert cv2 images to PIL
    def cv2_to_pil(cv_img):
        if cv_img is None:
            return None
        rgb = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
        return Image.fromarray(rgb)

    img1_pil = cv2_to_pil(img1)
    img2_pil = cv2_to_pil(img2)

    # Step 3: Create A3 landscape canvas (300 DPI)
    dpi = 300
    a3_width_mm = 420
    a3_height_mm = 297
    width_px = int(a3_width_mm / 25.4 * dpi)
    height_px = int(a3_height_mm / 25.4 * dpi)
    canvas = Image.new('RGB', (width_px, height_px), (255, 255, 255))

    # Step 4: Place images side by side with different proportions
    proportions = [0.2, 0.4, 0.4]
    slot_widths = [int(p * width_px) for p in proportions]
    slot_widths[2] = width_px - slot_widths[0] - slot_widths[1]  # Adjust for rounding

    images = [img1_pil, img2_pil, img_drawn]
    current_x = 0
    for i, img in enumerate(images):
        if img is None:
            current_x += slot_widths[i]
            continue
        # Resize keeping aspect ratio to fit slot
        scale = min(slot_widths[i] / img.width, height_px / img.height)
        new_size = (int(img.width * scale), int(img.height * scale))
        resized = img.resize(new_size, Image.Resampling.LANCZOS)

        # Center in the slot
        x_offset = current_x + (slot_widths[i] - new_size[0]) // 2
        y_offset = (height_px - new_size[1]) // 2
        canvas.paste(resized, (x_offset, y_offset))
        current_x += slot_widths[i]

    # Step 5: Save as PDF
    import datetime
    timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
    canvas.save('visualise/'+timestamp+'.pdf', 'PDF', resolution=dpi)