//! iOS PDF viewer — `PDFView` from PDFKit (issue #516 / iOS parity).
//! Mirrors the macOS impl. PDFKit linked from
//! `crates/perry/src/commands/compile/link.rs`.

use objc2::msg_send;
use objc2::rc::Retained;
use objc2::runtime::{AnyClass, AnyObject};
use objc2::AnyThread;
use objc2_foundation::NSString;
use objc2_ui_kit::UIView;

fn str_from_header(ptr: *const u8) -> String {
    if ptr.is_null() {
        return String::new();
    }
    unsafe {
        let header = ptr as *const perry_runtime::string::StringHeader;
        let len = (*header).byte_len as usize;
        let data = ptr.add(std::mem::size_of::<perry_runtime::string::StringHeader>());
        std::str::from_utf8_unchecked(std::slice::from_raw_parts(data, len)).to_string()
    }
}

pub fn create(width: f64, height: f64) -> i64 {
    unsafe {
        let Some(cls) = AnyClass::get(c"PDFView") else {
            return 0;
        };
        let alloc: *mut AnyObject = msg_send![cls, alloc];
        let frame = objc2_core_foundation::CGRect::new(
            objc2_core_foundation::CGPoint::new(0.0, 0.0),
            objc2_core_foundation::CGSize::new(width.max(40.0), height.max(40.0)),
        );
        let raw: *mut AnyObject = msg_send![alloc, initWithFrame: frame];
        let view: Retained<AnyObject> = match Retained::from_raw(raw) {
            Some(r) => r,
            None => return 0,
        };
        let _: () = msg_send![&*view, setDisplayMode: 1u64];
        let _: () = msg_send![&*view, setAutoScales: true];
        let nsview: Retained<UIView> = Retained::cast_unchecked(view);
        super::register_widget(nsview)
    }
}

pub fn load_file(handle: i64, path_ptr: *const u8) -> bool {
    let path = str_from_header(path_ptr);
    if path.is_empty() {
        return false;
    }
    let Some(view) = super::get_widget(handle) else {
        return false;
    };
    unsafe {
        let Some(url_cls) = AnyClass::get(c"NSURL") else {
            return false;
        };
        let ns_path = NSString::from_str(&path);
        let url: *mut AnyObject = msg_send![url_cls, fileURLWithPath: &*ns_path];
        if url.is_null() {
            return false;
        }
        let Some(doc_cls) = AnyClass::get(c"PDFDocument") else {
            return false;
        };
        let alloc: *mut AnyObject = msg_send![doc_cls, alloc];
        let doc: *mut AnyObject = msg_send![alloc, initWithURL: url];
        if doc.is_null() {
            return false;
        }
        let _: () = msg_send![&*view, setDocument: doc];
        true
    }
}

pub fn get_page_count(handle: i64) -> i64 {
    let Some(view) = super::get_widget(handle) else {
        return 0;
    };
    unsafe {
        let doc: *mut AnyObject = msg_send![&*view, document];
        if doc.is_null() {
            return 0;
        }
        let count: usize = msg_send![doc, pageCount];
        count as i64
    }
}

pub fn go_to_page(handle: i64, page_index: i64) {
    if page_index < 0 {
        return;
    }
    let Some(view) = super::get_widget(handle) else {
        return;
    };
    unsafe {
        let doc: *mut AnyObject = msg_send![&*view, document];
        if doc.is_null() {
            return;
        }
        let count: usize = msg_send![doc, pageCount];
        if (page_index as usize) >= count {
            return;
        }
        let page: *mut AnyObject = msg_send![doc, pageAtIndex: page_index as usize];
        if page.is_null() {
            return;
        }
        let _: () = msg_send![&*view, goToPage: page];
    }
}

pub fn get_current_page(handle: i64) -> i64 {
    let Some(view) = super::get_widget(handle) else {
        return -1;
    };
    unsafe {
        let doc: *mut AnyObject = msg_send![&*view, document];
        if doc.is_null() {
            return -1;
        }
        let cur: *mut AnyObject = msg_send![&*view, currentPage];
        if cur.is_null() {
            return -1;
        }
        let idx: i64 = msg_send![doc, indexForPage: cur];
        idx
    }
}

pub fn set_scale(handle: i64, scale: f64) {
    let Some(view) = super::get_widget(handle) else {
        return;
    };
    unsafe {
        let _: () = msg_send![&*view, setScaleFactor: scale.max(0.1)];
    }
}
