use std::path::PathBuf;

use core_foundation::{
	base::{CFRange, TCFType},
	dictionary::CFDictionary,
	number::CFNumber,
	string::{CFString, CFStringRef},
};
use core_text::{
	font::{new_from_descriptor, CTFont, CTFontRef},
	font_descriptor::{
		kCTFontBoldTrait, kCTFontCondensedTrait, kCTFontExpandedTrait, kCTFontFamilyNameAttribute, kCTFontItalicTrait,
		kCTFontSymbolicTrait, kCTFontTraitsAttribute, kCTFontWeightTrait, kCTFontWidthTrait, new_from_attributes,
	},
};

pub fn font_for_text(name: &str, text: &str, weight: u16, stretch: u16, italic: bool) -> Option<(String, PathBuf)> {
	let mut symbolic = 0u32;
	if weight >= 700 {
		symbolic |= kCTFontBoldTrait;
	}
	if italic {
		symbolic |= kCTFontItalicTrait;
	}
	if stretch < 1000 {
		symbolic |= kCTFontCondensedTrait;
	} else if stretch > 1000 {
		symbolic |= kCTFontExpandedTrait;
	}
	let weight = if weight < 150 {
		-0.80
	} else if weight < 250 {
		-0.60
	} else if weight < 350 {
		-0.40
	} else if weight < 450 {
		0.0
	} else if weight < 550 {
		0.23
	} else if weight < 650 {
		0.30
	} else if weight < 750 {
		0.40
	} else if weight < 850 {
		0.56
	} else if weight < 925 {
		0.62
	} else {
		1.00
	};
	let stretch = if stretch < (500 + 625) / 2 {
		-0.5
	} else if stretch < (625 + 750) / 2 {
		-0.37
	} else if stretch < (750 + 875) / 2 {
		-0.25
	} else if stretch < (875 + 1000) / 2 {
		-0.13
	} else if stretch < (1000 + 1125) / 2 {
		0.0
	} else if stretch < (1125 + 1250) / 2 {
		0.13
	} else if stretch < (1250 + 1375) / 2 {
		0.25
	} else if stretch < (1375 + 1500) / 2 {
		0.37
	} else {
		0.5
	};
	let attributes = CFDictionary::from_CFType_pairs(&[
		(
			unsafe { CFString::wrap_under_get_rule(kCTFontFamilyNameAttribute) },
			CFString::new(name).as_CFType(),
		),
		(
			unsafe { CFString::wrap_under_get_rule(kCTFontTraitsAttribute) },
			CFDictionary::from_CFType_pairs(&[
				(
					unsafe { CFString::wrap_under_get_rule(kCTFontWeightTrait) },
					CFNumber::from(weight).as_CFType(),
				),
				(
					unsafe { CFString::wrap_under_get_rule(kCTFontWidthTrait) },
					CFNumber::from(stretch).as_CFType(),
				),
				(
					unsafe { CFString::wrap_under_get_rule(kCTFontSymbolicTrait) },
					CFNumber::from(symbolic as i32).as_CFType(),
				),
			])
			.as_CFType(),
		),
	]);
	let desc = new_from_attributes(&attributes);
	if text.is_empty() {
		return Some((desc.family_name(), desc.font_path()?));
	}
	let font = new_from_descriptor(&desc, 0.0);
	let text = CFString::new(text);
	let font = unsafe {
		CTFont::wrap_under_create_rule(CTFontCreateForString(
			font.as_concrete_TypeRef(),
			text.as_concrete_TypeRef(),
			CFRange::init(0, text.char_len()),
		))
	};
	let desc = font.copy_descriptor();
	Some((desc.family_name(), desc.font_path()?))
}

#[link(kind = "framework", name = "CoreText")]
extern "C" {
	pub fn CTFontCreateForString(currentFont: CTFontRef, string: CFStringRef, range: CFRange) -> CTFontRef;
}