$$include '../meta/macros.ptl'

import [mix linreg clamp fallback] from "@iosevka/util"
import [Cv AnyCv AnyDerivingCv Dotless Zero SvInheritableRelations PseudoCvDecompose getGrMesh] from "@iosevka/glyph/relation"

extern Map

glyph-module

glyph-block Common-Derivatives : begin
	define [ApplyCv g shapeFrom primaryFollow allFollows para] : begin
		foreach follow [items-of allFollows] : foreach { kPrime prime } para.variants.primes
			foreach pv [prime.variants.values] : begin
				local suffix : pv.resolveFor para follow
				if suffix : begin
					local dstName : shapeFrom + '.' + suffix
					local dstGlyph : query-glyph dstName
					if dstGlyph : g.addVariantForRecursiveBuild dstGlyph

					if (follow === primaryFollow && para.enableCvSs && pv.tag && pv.rank) : begin
						pv.set g dstName
						if pv.nonDeriving : pv.setPreventDeriving g

	glyph-block-export select-variant
	define [select-variant] : with-params [name unicode [shapeFrom name] [follow name]] : begin
		if [not : glyph-is-needed name] : return nothing

		local variant para.variantSelector.([resolveMainFollow follow])
		if [not variant] : begin
			throw : new Error "Variant for \(name) (selector: \(shapeFrom)) is not assigned."

		create-glyph name unicode : glyph-proc
			local fromGlyphName : shapeFrom + '.' + variant
			local fromGlyph : query-glyph fromGlyphName
			if [not fromGlyph] : throw : new Error "Cannot find glyph '\(fromGlyphName)'"

			include fromGlyph AS_BASE ALSO_METRICS
			currentGlyph.cloneRankFromGlyph fromGlyph

			ApplyCv currentGlyph shapeFrom [resolveMainFollow follow] [resolveAllFollow follow] para

			foreach gr [items-of SvInheritableRelations] : begin
				local v : gr.get fromGlyph
				if v : gr.set currentGlyph v

	define [resolveMainFollow follow] : begin
		if ([typeof follow] === 'string') : return follow
		return : follow.resolveMain
	define [resolveAllFollow follow] : begin
		if ([typeof follow] === 'string') : return { follow }
		return : follow.resolveAll

	glyph-block-export conditional-follow
	define [conditional-follow c f1 f2] : new CConditionalFollow c f1 f2
	class CConditionalFollow
		public [new c f1 f2] : begin
			set this.c c
			set this.f1 f1
			set this.f2 f2
		public [resolveMain] : return : if this.c this.f1 this.f2
		public [resolveAll] : return { this.f1 this.f2 }

	glyph-block-export orthographic-italic
	define [orthographic-italic name unicode] : if para.isItalic
		alias name unicode (name + '.italic')
		alias name unicode (name + '.upright')

	glyph-block-export orthographic-slanted
	define [orthographic-slanted name unicode] : if para.slopeAngle
		alias name unicode (name + '.slanted')
		alias name unicode (name + '.upright')

	glyph-block-export query-glyph
	define [query-glyph id] : begin
		if [not id] : return nothing
		return : glyphStore.queryByName id

	glyph-block-export refer-glyph
	define [refer-glyph id] : new ReferGlyph id

	class ReferGlyph
		public [new id] : begin
			set this.id id
		public [applyToGlyph target copyAnchors copyWidth] : begin
			local goal : query-glyph this.id
			if [not goal] : throw : new Error "Cannot find glyph '\(this.id)'"
			target.includeGlyph goal copyAnchors copyWidth

	glyph-block-export DeriveMeshT
	define [DeriveMeshT gnSources Query Fn FnLink] : begin
		local linksGnMap : new Map

		# Build principal
		local gndSource : Fn gnSources null null
		local gSource : query-glyph gndSource
		linksGnMap.set [keyOfGnList gnSources] gndSource

		local mesh : getGrMesh gnSources Query query-glyph

		# Build related derivatives
		foreach { gr from to } [items-of mesh] : begin
			local key : keyOfGnList to
			if [not : linksGnMap.has key] : begin
				local gnDerivedTo : Fn to gr from
				linksGnMap.set key gnDerivedTo

				local gDerivedTo : query-glyph gnDerivedTo
				if (gSource && gDerivedTo) : gSource.addVariantForRecursiveBuild gDerivedTo

		# Link related derivatives
		foreach { gr from to } [items-of mesh] : begin
			local gndFrom : linksGnMap.get [keyOfGnList from]
			local gndTo   : linksGnMap.get [keyOfGnList to]
			if (gndFrom && gndTo && [query-glyph gndFrom] && [query-glyph gndTo]) : begin
				gr.set [query-glyph gndFrom] gndTo
				if FnLink : FnLink gndFrom gndTo from to

		return gndSource
	define [keyOfGnList gns] ".deriveKey{\[gns.join '}{']}"

	glyph-block-export derive-glyphs
	define [derive-glyphs dstGid dstUnicode srcGid Fn] : begin
		if [not : glyph-is-needed dstGid] : return nothing

		DeriveMeshT { srcGid } AnyDerivingCv : function [gns gr] : begin
			local gn gns.0
			local gnDst : if [not gr] dstGid "\(dstGid)/derive{\(gn)}"
			local unicodeDst : if [not gr] dstUnicode null
			create-glyph gnDst unicodeDst : Fn gn gr
			return gnDst

	glyph-block-export derive-multi-part-glyphs
	define [derive-multi-part-glyphs dstGid dstUnicode gnSoruces Fn] : begin
		if [not : glyph-is-needed dstGid] : return nothing

		DeriveMeshT gnSoruces AnyDerivingCv : function [gns gr] : begin
			local gnDst : if [not gr] dstGid "\(dstGid)/derive{\[gns.join '}{']}"
			local unicodeDst : if [not gr] dstUnicode null
			create-glyph gnDst unicodeDst : Fn gns gr
			return gnDst

	glyph-block-export alias
	define [alias dstGid dstUnicode srcGid] : begin
		if [not dstGid] : throw : new Error "Target ID not specified"
		if [not : glyph-is-needed srcGid] : return nothing

		local glyphSrc : glyphStore.queryByNameEnsured srcGid
		local glyphDst : create-glyph dstGid dstUnicode : glyph-proc
			include [refer-glyph srcGid] AS_BASE ALSO_METRICS

		if glyphDst : begin
			foreach [gr : items-of : AnyCv.query glyphSrc] : begin
				gr.set glyphDst [gr.get glyphSrc]
				if [gr.getPreventDeriving glyphSrc] : gr.setPreventDeriving glyphDst
			if [Zero.get glyphSrc] : Zero.set glyphDst [Zero.get glyphSrc]
			if [Dotless.get glyphSrc] : Dotless.set glyphDst [Dotless.get glyphSrc]

	glyph-block-export turned
	define [turned newid unicode id _x _y mark] : begin
		if [not newid] : throw : new Error "Target ID not specified"
		derive-glyphs newid unicode id
			lambda [src gr] : glyph-proc
				include [refer-glyph src] AS_BASE ALSO_METRICS
				define x : if (_x <@ Function) [_x.call currentGlyph] _x
				define y : if (_y <@ Function) [_y.call currentGlyph] _y
				if mark : include mark

				# This is [FlipAround], but we cannot use it because common-shapes depends on this
				# glyph block
				include [currentGlyph.gizmo.inverse]
				include : Translate (-x) (-y)
				include : Scale (-1) (-1)
				include : Translate x y
				include currentGlyph.gizmo

	glyph-block-export link-reduced-variant
	define [link-reduced-variant] : with-params [dstGid srcGid gr follow shapeFrom] : begin
		if [not : query-glyph dstGid] : select-variant dstGid (shapeFrom -- [fallback shapeFrom srcGid]) (follow -- [fallback follow dstGid])
		if (gr && [query-glyph srcGid]) : gr.set [query-glyph srcGid] dstGid

	glyph-block-export alias-reduced-variant
	define [alias-reduced-variant dstGid srcGid shapeGid gr] : begin
		alias dstGid null shapeGid
		if (gr && [query-glyph srcGid]) : gr.set [query-glyph srcGid] dstGid

	glyph-block-export HalfAdvance
	define [HalfAdvance] : this.advanceWidth / 2

	# Dual derivatives
	glyph-block-export HCombine
	define [HCombine newid unicode id1 id2 spacing] : begin
		if [not newid] : throw : new Error "Target ID not specified"
		create-glyph newid unicode : glyph-proc
			include [refer-glyph id1] AS_BASE ALSO_METRICS
			include : Translate (-spacing) 0
			include [refer-glyph id2]
			include : Translate (spacing / 2) 0

	glyph-block-export HDual
	define [HDual newid unicode id spacing] : begin
		if [not newid] : throw : new Error "Target ID not specified"
		return : HCombine newid unicode id id spacing

	glyph-block-export VCombine
	define [VCombine newid unicode id1 id2 spacing] : begin
		if [not newid] : throw : new Error "Target ID not specified"
		create-glyph newid unicode : glyph-proc
			include [refer-glyph id2] AS_BASE ALSO_METRICS
			include : Translate (-spacing * TanSlope) (-spacing)
			include [refer-glyph id1]
			include : Translate (spacing * TanSlope / 2) (spacing / 2)

	glyph-block-export VDual
	define [VDual newid unicode id spacing] : begin
		if [not newid] : throw : new Error "Target ID not specified"
		return : VCombine newid unicode id id spacing

	glyph-block-export derive-composites
	define [derive-composites dstGid dstUnicode srcGid] : begin
		if [not : glyph-is-needed dstGid] : return nothing
		local components : {}.slice.call arguments 2
		local refs {}
		foreach part [items-of components] : if ([typeof part] === 'string')
			then : refs.push part

		return : derive-multi-part-glyphs dstGid dstUnicode refs : function [srcs] : glyph-proc
			local rid 0
			foreach j [range 0 components.length] : begin
				local asBase      : if (j === 0) AS_BASE      nothing
				local alsoMetrics : if (j === 0) ALSO_METRICS nothing
				local part components.(j)

				if ([typeof part] === 'string')
				: then : begin
					include [refer-glyph srcs.(rid)] asBase alsoMetrics
					set rid : rid + 1
				: else : include part asBase alsoMetrics

	glyph-block-export link-gr
	define [link-gr Gr fromGid toGid] : begin
		if [glyph-is-needed toGid] : begin
			local fromGlyph : query-glyph fromGid
			local toGlyph   : query-glyph toGid
			if (fromGlyph && toGlyph) : Gr.set fromGlyph toGid
