"""
-------------------------------------------------------------------------
File: data_loader_tif.py
Description: 
 GeoTIFF 데이터 로더 (DeepLabV3+ 학습용)
 4-band GeoTIFF (R, G, B, MNDWI) + 단일밴드 마스크를 로드하여
 정규화, 리사이즈, augmentation, tf.data.Dataset 파이프라인 생성
Author: 원캉린, 소지안 프로
Created: 2026-02-02
Last Modified: 2026-02-02
-------------------------------------------------------------------------
"""

import os
import glob
import numpy as np
import rasterio
import tensorflow as tf
from tqdm import tqdm

from .config import IMAGE_SIZE, BATCH_SIZE, SHUFFLE_BUFFER


def _load_image(path: str, image_size: int = IMAGE_SIZE) -> np.ndarray:
    """4밴드 GeoTIFF 로드 → (H, W, 4) float32, 정규화 및 리사이즈 적용"""
    with rasterio.open(path) as src:
        image = src.read().transpose(1, 2, 0).astype(np.float32)

    # 밴드별 min-max 정규화 (0~1)
    for c in range(image.shape[2]):
        band = image[:, :, c]
        b_min, b_max = band.min(), band.max()
        if b_max > b_min:
            image[:, :, c] = (band - b_min) / (b_max - b_min)
        else:
            image[:, :, c] = 0.0

    # 리사이즈
    image = tf.image.resize(image, (image_size, image_size)).numpy()

    return image


def _load_mask(path: str, image_size: int = IMAGE_SIZE) -> np.ndarray:
    """단일밴드 마스크 GeoTIFF 로드 → (H, W, 1) float32, 이진화 및 리사이즈 적용"""
    with rasterio.open(path) as src:
        mask = src.read(1).astype(np.float32)

    mask = mask[..., np.newaxis]  # (H, W, 1)

    # 리사이즈
    mask = tf.image.resize(mask, (image_size, image_size), method='nearest').numpy()

    # 이진화 (0 또는 1)
    mask = (mask > 0.5).astype(np.float32)

    return mask


def load_dataset(dataset_dir: str, image_size: int = IMAGE_SIZE, max_samples: int = None):
    """
    데이터셋 디렉토리에서 이미지/마스크 로드

    Args:
        dataset_dir: 'dataset/{scene_id}/' (image/, mask/ 폴더 포함)
        image_size: 리사이즈 크기
        max_samples: 최대 로드 개수 (None이면 전체)

    Returns:
        images: (N, H, W, 4) float32, 정규화됨
        masks:  (N, H, W, 1) float32, 이진 마스크
    """
    image_paths = sorted(glob.glob(os.path.join(dataset_dir, "image", "*.tif")))
    mask_paths = sorted(glob.glob(os.path.join(dataset_dir, "mask", "*.tif")))

    assert len(image_paths) == len(mask_paths), \
        f"이미지({len(image_paths)})와 마스크({len(mask_paths)}) 개수 불일치"
    assert len(image_paths) > 0, f"이미지를 찾을 수 없음: {dataset_dir}/image/"

    if max_samples is not None:
        image_paths = image_paths[:max_samples]
        mask_paths = mask_paths[:max_samples]

    n = len(image_paths)
    images = np.zeros((n, image_size, image_size, 4), dtype=np.float32)
    masks = np.zeros((n, image_size, image_size, 1), dtype=np.float32)

    for i, (img_path, msk_path) in tqdm(
        enumerate(zip(image_paths, mask_paths)), total=n, desc="Loading TIF"
    ):
        images[i] = _load_image(img_path, image_size)
        masks[i] = _load_mask(msk_path, image_size)

    return images, masks


def augment(image, mask):
    """Data augmentation (소규모 데이터셋용)"""
    # 좌우 반전
    if tf.random.uniform(()) > 0.5:
        image = tf.image.flip_left_right(image)
        mask = tf.image.flip_left_right(mask)

    # 상하 반전
    if tf.random.uniform(()) > 0.5:
        image = tf.image.flip_up_down(image)
        mask = tf.image.flip_up_down(mask)

    # 90도 회전 (0~3회)
    k = tf.random.uniform((), minval=0, maxval=4, dtype=tf.int32)
    image = tf.image.rot90(image, k)
    mask = tf.image.rot90(mask, k)

    # 밝기 조절 (이미지만)
    image = tf.image.random_brightness(image, max_delta=0.1)
    image = tf.clip_by_value(image, 0.0, 1.0)

    return image, mask


def create_dataset(dataset_dir: str, image_size: int = IMAGE_SIZE,
                   batch_size: int = BATCH_SIZE, use_augmentation: bool = True,
                   max_samples: int = None, return_arrays: bool = False):
    """
    TIF 데이터셋을 로드하여 tf.data.Dataset 파이프라인 생성

    Args:
        dataset_dir: 데이터셋 경로
        image_size: 이미지 크기
        batch_size: 배치 크기
        use_augmentation: augmentation 적용 여부
        return_arrays: True면 (dataset, images, masks) 반환

    Returns:
        tf.data.Dataset 또는 (dataset, images, masks) 튜플
    """
    images, masks = load_dataset(dataset_dir, image_size, max_samples=max_samples)

    dataset = tf.data.Dataset.from_tensor_slices((images, masks))

    if use_augmentation:
        dataset = dataset.map(augment, num_parallel_calls=tf.data.AUTOTUNE)

    dataset = dataset.shuffle(SHUFFLE_BUFFER)
    dataset = dataset.batch(batch_size, drop_remainder=True)
    dataset = dataset.prefetch(tf.data.AUTOTUNE)

    if return_arrays:
        return dataset, images, masks
    return dataset


if __name__ == "__main__":
    dataset_dir = "dataset_256x256/S2B_MSIL2A_20231001T020659_R103_T52SCD_20241020T075116_tif"

    images, masks = load_dataset(dataset_dir)
    print(f"Images: {images.shape}, dtype={images.dtype}")
    print(f"Masks:  {masks.shape}, dtype={masks.dtype}")
    print(f"Image value range: [{images.min():.3f}, {images.max():.3f}]")
    print(f"Mask unique values: {np.unique(masks)}")

    dataset = create_dataset(dataset_dir)
    for batch_img, batch_msk in dataset.take(1):
        print(f"Batch images: {batch_img.shape}")
        print(f"Batch masks:  {batch_msk.shape}")
