import '../support/point' as Point
import '../support/transform' as : Transform && [object [transformPoint tp] [untransform utp] inverse]
import [mix linreg clamp fallback] from '../support/utils'

# Parameter generation
export : define [calculateMetrics para] : begin
	define UPM 1000

	# Key metrics
	define WIDTH para.width
	define SB para.sb
	define CAP para.cap
	define XH para.xheight
	define DESCENDER : fallback para.descender (XH - CAP)
	define CONTRAST : fallback para.contrast 1
	# Key metrics for symbols
	define parenMid : fallback para.parenMid (XH * 0.65)
	define parenTop (parenMid + (CAP - XH) * 2.6)
	define parenBot (parenMid - (CAP - XH) * 2.6)
	define operTop : mix parenMid parenTop 0.8
	define operBot : mix parenMid parenBot 0.8
	define plusTop : parenMid + (WIDTH - SB * 2) * 0.55
	define plusBot : parenMid - (WIDTH - SB * 2) * 0.55
	define operMid parenMid

	# Transform constructors
	define [Italify angle shift] : begin
		local slope [Math.tan ([fallback angle para.slantAngle] / 180 * Math.PI)]
		return : new Transform 1 slope 0 1 [fallback shift : -slope * parenMid] 0
	define [Upright angle shift] [Italify angle shift :.inverse]
	define [Scale sx sy] : new Transform sx 0 0 [fallback sy sx] 0 0
	define [Translate x y] : new Transform 1 0 0 1 x y
	define [Rotate angle] : new Transform [Math.cos angle] (-[Math.sin angle]) [Math.sin angle] [Math.cos angle] 0 0

	define globalTransform : Italify para.slantAngle
	define TANSLANT   globalTransform.yx
	define SINSLANT : Math.sin (para.slantAngle / 180 * Math.PI)
	define COSSLANT : Math.cos (para.slantAngle / 180 * Math.PI)
	define HVCONTRAST : CONTRAST * COSSLANT + SINSLANT * TANSLANT

	# Orient parameters
	define UPWARD    : new Point (-HVCONTRAST)  0
	define DOWNWARD  : new Point HVCONTRAST  0
	define RIGHTWARD : new Point TANSLANT  1
	define LEFTWARD  : new Point (- TANSLANT) (-1)

	# Style parameters
	define O para.overshoot
	define OX para.overshootx
	define OXHOOK para.oxhook
	define HOOK para.hook
	define AHOOK para.ahook
	define SHOOK para.shook
	define RHOOK para.rhook
	define JHOOK para.jhook
	define FHOOK para.fhook
	define HOOKX para.hookx
	define SMOOTH para.smooth
	define SMALLSMOOTH para.smallsmooth
	define STROKE para.stroke
	define DOTSIZE : fallback para.dotsize STROKE
	define PERIODSIZE : fallback para.periodsize DOTSIZE
	define BARPOS : fallback para.barpos 0.5
	define GBARPOS : fallback para.gbarpos 0.5
	define PBARPOS : fallback para.pbarpos 0.5
	define EBARPOS : fallback para.ebarpos BARPOS
	define OVERLAYPOS para.overlaypos
	define FIVEBARPOS para.fivebarpos
	define LONGJUT para.longjut
	define JUT para.jut
	define VJUT para.vjut
	define ACCENT para.accent
	define ACCENTX para.accentx

	define CTHIN : fallback para.cthin 0.75
	define CTHINB : fallback para.cthinb 0.5

	define SLAB para.slab

	define TAILADJX : WIDTH * 0.2
	define TAILADJY : XH * 0.25
	define LBALANCE : LONGJUT * 0.04
	define IBALANCE : fallback para.ibalance (LONGJUT * 0.04)
	define LBALANCE2 : LONGJUT * 0.14
	define IBALANCE2 : fallback para.ibalance (LONGJUT * 0.14)
	define JBALANCE : fallback para.jbalance 0
	define JBALANCE2 : fallback para.jbalance2 (STROKE * 0.25 + LBALANCE)
	define TBALANCE : fallback para.tbalance JBALANCE
	define TBALANCE2 : fallback para.tbalance2 TBALANCE
	define RBALANCE : fallback para.rbalance (JBALANCE * 0.3)
	define RBALANCE2 : fallback para.rbalance2 0
	define FBALANCE : fallback para.fbalance 0
	define ONEBALANCE : fallback para.onebalance 0

	# derived metrics
	define FULLWIDTH : if (para.spacing >= 2) 1000 WIDTH
	define FULLWIDTH1 : if (para.spacing >= 1) 1000 WIDTH
	define FULLWIDTH2 : if (para.spacing >= 2) 1000 WIDTH
	define FULLWIDTH3 : if (para.spacing >= 3) 1000 WIDTH

	define OXE : OX - O
	define ESS : STROKE * [fallback para.essx CONTRAST]
	define ESSQUESTION : STROKE * [fallback para.essxq CONTRAST]
	define XO : XH - O
	define CAPO : CAP - O
	define HALFSTROKE : STROKE / 2
	define RIGHTSB : WIDTH - SB
	define FWRSB : FULLWIDTH - SB
	define MIDDLE : WIDTH / 2
	define FWMIDDLE : FULLWIDTH / 2
	define CAPMIDDLE : CAP / 2
	define CAP_SMOOTH : CAP - SMOOTH
	define DOTRADIUS : DOTSIZE / 2
	define PERIODRADIUS : PERIODSIZE / 2
	define SIDEJUT : JUT - HALFSTROKE * HVCONTRAST

	define SMOOTHA : SMOOTH - TANSLANT * para.smoothadjust
	define SMOOTHB : SMOOTH + TANSLANT * para.smoothadjust
	define SMALLSMOOTHA : SMALLSMOOTH - TANSLANT * para.smoothadjust
	define SMALLSMOOTHB : SMALLSMOOTH + TANSLANT * para.smoothadjust

	define CORRECTION_OMIDX : TANSLANT * [linreg 18 1.3 126 0.9 STROKE]
	define CORRECTION_OMIDS : STROKE * CORRECTION_OMIDX

	# Blackness parameters
	# We will estimate blackness using lower-case 'e'
	define WHITENESS : ((XH - STROKE * 3) * (RIGHTSB - SB) * (1 / 3)) / (XH * (RIGHTSB - SB))
	define [adviceBlackness crowdedness] : Math.min STROKE ((RIGHTSB - SB) * (1 - WHITENESS) / (crowdedness * HVCONTRAST))
	define MVERTSTROKE : adviceBlackness : fallback para.lllcrowdedness (3 + 1 / 3)
	define OVERLAYSTROKE : adviceBlackness 3.75
	define OPERATORSTROKE : adviceBlackness 3.2
	define SHOULDERFINE : if para.shoulderfine (STROKE * para.shoulderfine) [adviceBlackness 10]

	define SUPERNESS : fallback para.superness 2
	define [superxy x] : Math.pow (1 - [Math.pow x SUPERNESS]) (1 / SUPERNESS)

	define [adviceSSmooth y sign] : begin
		local ss : y * 0.21 + STROKE * 0.22 * [clamp 1 2 : linreg 126 1 137 1.025 STROKE] + 0.035 * (RIGHTSB - SB)
		return : ss + sign * TANSLANT * para.smoothadjust * (ss / SMALLSMOOTH)
	define [adviceGlottalStopSmooth y sign] : ((y - STROKE) * 0.24 + STROKE * 0.625) + sign * TANSLANT * para.smoothadjust
	define [shoulderMidSlope _fine _stroke _dir] : 0.5 * HVCONTRAST * ([fallback _stroke STROKE] - [fallback _fine SHOULDERFINE]) / [fallback _stroke STROKE] + [fallback _dir 1] * TANSLANT

	return [object
		UPM WIDTH SB CAP XH DESCENDER CONTRAST
		parenMid parenTop parenBot operTop operBot plusTop plusBot operMid Italify Upright Scale Translate Rotate globalTransform
		TANSLANT SINSLANT COSSLANT HVCONTRAST UPWARD DOWNWARD RIGHTWARD LEFTWARD
		O OX OXHOOK HOOK AHOOK SHOOK RHOOK JHOOK FHOOK HOOKX SMOOTH SMALLSMOOTH STROKE DOTSIZE PERIODSIZE
		BARPOS GBARPOS PBARPOS EBARPOS OVERLAYPOS FIVEBARPOS LONGJUT JUT VJUT ACCENT ACCENTX CTHIN CTHINB SLAB
		TAILADJX TAILADJY LBALANCE IBALANCE LBALANCE2 IBALANCE2 JBALANCE JBALANCE2 TBALANCE TBALANCE2 RBALANCE RBALANCE2
		FBALANCE ONEBALANCE FULLWIDTH FULLWIDTH1 FULLWIDTH2 FULLWIDTH3 OXE ESS ESSQUESTION XO CAPO HALFSTROKE RIGHTSB FWRSB
		MIDDLE FWMIDDLE CAPMIDDLE CAP_SMOOTH DOTRADIUS PERIODRADIUS SIDEJUT SMOOTHA SMOOTHB SMALLSMOOTHA SMALLSMOOTHB CORRECTION_OMIDX CORRECTION_OMIDS
		WHITENESS adviceBlackness MVERTSTROKE OVERLAYSTROKE OPERATORSTROKE SHOULDERFINE SUPERNESS superxy
		adviceSSmooth adviceGlottalStopSmooth shoulderMidSlope]

export : define [setFontMetrics para metrics font] : begin
	define [object CAP DESCENDER XH WIDTH] metrics
	# Metric metadata
	# Note: we use 1000upm in design, and (1000 * upmsacle)upm in production, to avoid rounding error.
	define asc : para.leading * CAP / (CAP - DESCENDER)
	define desc : para.leading * DESCENDER / (CAP - DESCENDER)
	define descenderPad : fallback para.descenderPad 0

	set font.OS_2.xAvgCharWidth     WIDTH
	set font.head.unitsPerEm        1000
	set font.hhea.ascender          asc
	set font.OS_2.usWinAscent       asc
	set font.OS_2.sTypoAscender     asc

	set font.hhea.descender         (DESCENDER - descenderPad)
	set font.OS_2.usWinDescent      ([Math.abs desc] + descenderPad)
	set font.OS_2.sTypoDescender    (desc - descenderPad)

	set font.hhea.lineGap           (para.leading - asc + DESCENDER)
	set font.OS_2.sTypoLineGap      (para.leading - asc + desc)

	set font.OS_2.sxHeight          XH
	set font.OS_2.sCapHeight        CAP
	set font.post.italicAngle       (0 - para.slantAngle)

export : define designParameters : object
	equal_wideness 0.1
	logic_narrow_shrink 0.75
	geometric_large_x   (1 + 2 / 3)
	geometric_small_x   (3 / 5)
	arrow_size          (3 / 8)
	# ()
	parenOutside        0.2
	parenInside         0.9
	parenCurliness      0.7
	parenOvershoot      0.0375
	# []
	bracketOutside      0.1
	bracketInside       0.95
	# {}
	braceOutside        0.05
	braceInside         0.95
	braceCurlyM1        0.6
	braceCurlyM2        0.45
	braceOvershoot      0.02