-- declaring extension as widget, followed by identifier
widget com.livecode.widget.svgpath
--

-- dependency declarations
use com.livecode.canvas
use com.livecode.widget
use com.livecode.engine
use com.livecode.library.iconSVG

-- adding metadata to ensure the extension displays correctly in livecode
metadata title is "SVG Icon"
metadata author is "Trevor DeVore / Benjamin Beaumont"
metadata version is "1.0.2"
metadata svgicon is "M61.8,0H4C1.8,0,0,1.8,0,4v27.8c0,2.2,1.8,4,4,4h57.8c2.2,0,4-1.8,4-4V4C65.8,1.8,64,0,61.8,0z M20.9,25.3c-1.2,0.9-2.8,1.4-4.9,1.4c-0.8,0-1.6-0.1-2.2-0.2c-0.7-0.1-1.3-0.3-1.9-0.5c-0.1,0.2-0.3,0.4-0.5,0.5s-0.5,0.2-0.8,0.2c-0.6,0-1-0.1-1.2-0.3c-0.2-0.2-0.4-0.6-0.4-1.2l-0.1-3.2v-0.2c0-0.6,0.1-1,0.3-1.3c0.2-0.2,0.6-0.4,1.2-0.4c0.6,0,1.1,0.4,1.5,1.3c0.1,0.2,0.2,0.4,0.2,0.5c0.4,0.7,0.9,1.2,1.5,1.6c0.6,0.4,1.4,0.5,2.4,0.5c0.8,0,1.5-0.2,2-0.5c0.5-0.4,0.7-0.8,0.7-1.5c0-1-1.1-1.7-3.2-2.2c-0.6-0.1-1.1-0.3-1.5-0.4c-1.8-0.5-3-1.1-3.7-1.8c-0.7-0.7-1-1.7-1-2.9c0-1.6,0.6-2.9,1.8-3.9c1.2-1,2.7-1.5,4.6-1.5c0.6,0,1.2,0.1,1.8,0.2c0.6,0.1,1.2,0.3,1.8,0.6c0.1-0.3,0.3-0.5,0.5-0.6c0.2-0.1,0.4-0.2,0.8-0.2c0.5,0,0.9,0.1,1,0.3c0.2,0.2,0.2,0.6,0.3,1.2l0.1,2.7v0.2c0,0.5-0.1,0.9-0.3,1.1c-0.2,0.2-0.6,0.3-1.1,0.3c-0.7,0-1.1-0.4-1.5-1.1c0-0.1-0.1-0.2-0.1-0.2c-0.3-0.6-0.7-1-1.2-1.3c-0.5-0.3-1.1-0.4-1.7-0.4c-0.8,0-1.5,0.2-2,0.6s-0.8,0.9-0.8,1.5c0,0.8,1.2,1.5,3.5,2c0.4,0.1,0.7,0.1,0.9,0.2c1.8,0.4,3.1,1,3.8,1.8c0.8,0.8,1.2,1.9,1.2,3.2C22.6,23.1,22,24.4,20.9,25.3zM40.9,12.5c-0.3,0.3-0.6,0.4-1.1,0.4h-0.2l-4.2,12c-0.2,0.6-0.4,1-0.6,1.1s-0.6,0.2-1.1,0.2h-1.7c-0.8,0-1.4-0.4-1.7-1.3l0-0.1l-4.3-12h-0.2c-0.5,0-0.8-0.1-1.1-0.4s-0.4-0.6-0.4-1.1c0-0.6,0.1-0.9,0.4-1.2s0.7-0.4,1.3-0.4H30c0.6,0,1,0.1,1.3,0.4c0.3,0.2,0.4,0.6,0.4,1.2c0,0.5-0.1,0.9-0.4,1.2c-0.3,0.2-0.7,0.4-1.3,0.4h-0.4l3.3,9.8l3.4-9.8H36c-0.6,0-1-0.1-1.3-0.4c-0.3-0.2-0.4-0.6-0.4-1.2c0-0.5,0.1-0.9,0.4-1.2c0.3-0.2,0.7-0.4,1.3-0.4h3.5c0.6,0,1.1,0.1,1.3,0.4c0.3,0.2,0.4,0.6,0.4,1.2C41.3,11.9,41.2,12.2,40.9,12.5z M57.8,20.6c-0.3,0.3-0.6,0.4-1.1,0.4h-0.2v3.3v0.2c0,0.3,0,0.5-0.1,0.6s-0.2,0.2-0.4,0.4c-0.5,0.3-1.4,0.6-2.5,0.8c-1.1,0.2-2.3,0.4-3.5,0.4c-2.4,0-4.4-0.8-5.9-2.4c-1.5-1.6-2.2-3.7-2.2-6.2c0-2.5,0.8-4.5,2.3-6.2c1.5-1.6,3.4-2.5,5.8-2.5c0.6,0,1.3,0.1,1.9,0.2s1.3,0.4,2,0.7c0.2-0.3,0.4-0.6,0.6-0.7c0.2-0.1,0.4-0.2,0.7-0.2c0.5,0,0.8,0.1,1,0.4c0.2,0.2,0.3,0.6,0.3,1.2l0,3.6c0,0.6-0.1,1.1-0.3,1.3c-0.2,0.2-0.6,0.3-1.1,0.3c-0.4,0-0.7-0.1-0.9-0.3c-0.2-0.2-0.4-0.5-0.5-1C53.3,14,53,13.4,52.5,13s-1.2-0.6-2.2-0.6c-1.4,0-2.5,0.5-3.3,1.4c-0.8,1-1.1,2.3-1.1,4.2s0.4,3.2,1.2,4.2c0.8,1,1.9,1.5,3.4,1.5c0.3,0,0.7,0,1.1-0.1c0.4-0.1,0.9-0.2,1.5-0.4V21h-1.6c-0.6,0-1.1-0.1-1.3-0.3c-0.3-0.2-0.4-0.6-0.4-1.1c0-0.6,0.1-0.9,0.4-1.2s0.7-0.4,1.3-0.4h5c0.6,0,1.1,0.1,1.4,0.3s0.4,0.6,0.4,1.2C58.2,20,58.1,20.4,57.8,20.6z"

private variable mState        as String
private variable mColor        as Color
private variable mHiliteColor  as Color
private variable  mPath    		as String
private variable mHilited      as Boolean
private variable mToggleHilite as Boolean
private variable mMaintainAspectRatio as Boolean
private variable mFlipVertically     as Boolean // some SVG Paths need to be flipped (all font awesome SVG paths do, Illustrator SVG does not)
private variable mAngle     as Real
variable mPathName as String
property iconColor            get getColor          set setColor
metadata iconColor.editor is "com.livecode.pi.color"
property iconHiliteColor      get getHiliteColor    set setHiliteColor
metadata iconHiliteColor.editor is "com.livecode.pi.color"
property iconPathPreset            get getPathPreset           set setPathPreset
metadata iconPathPreset.editor is "com.livecode.pi.enum"
metadata iconPathPreset.options is "execute: get iconNames()"
metadata iconPathPreset.default is "Star"
property iconPath       get mPath           set setPath
metadata iconPath.editor is "com.livecode.pi.text"
property isHilited            get mHilited          set setIsHilited
property toggleHilite         get mToggleHilite     set mToggleHilite
property maintainAspectRatio  get mMaintainAspectRatio  set setMaintainAspectRatio
property flipVertically       get mFlipVertically       set setFlipVertically
property iconAngle       get mAngle       set setAngle
--

----------
-- called when widget is created
public handler OnCreate()
	put "up" into mState
	
	put color [0,0,0] into mColor
	put stringToColor("255,0,100") into mHiliteColor
	put 0 into mAngle
	
	put false into mHilited
	put false into mToggleHilite
	put true into mMaintainAspectRatio
	put false into mFlipVertically

	variable tArray as Array
	put the empty array into tArray
	setPathPreset("Star")	
end handler
----------

----------
-- this handler is called when the widget is saved
public handler OnSave(out rProperties as Array)
	put the empty array into rProperties
	
	put mPath into rProperties["svg path"]
	put colorToString(mColor, true) into rProperties["color"]
	put colorToString(mHiliteColor, true) into rProperties["hilite color"]
	put mHilited into rProperties["hilited"]
	put mToggleHilite into rProperties["toggle hilite"]
	put mMaintainAspectRatio into rProperties["maintain aspect ratio"]
	put mFlipVertically into rProperties["flip vertically"]
	put mAngle into rProperties["angle"]
	
	return rProperties
end handler
----------

----------
-- this handler is called when the widget is loaded
public handler OnLoad(in pProperties as Array)
	put pProperties["svg path"] into mPath
	put stringToColor(pProperties["color"]) into mColor
	put stringToColor(pProperties["hilite color"]) into mHiliteColor
	put pProperties["hilited"] into mHilited
	put pProperties["toggle hilite"] into mToggleHilite
	put pProperties["maintain aspect ratio"] into mMaintainAspectRatio
	put pProperties["flip vertically"] into mFlipVertically
	put pProperties["angle"] into mAngle
end handler
----------

----------
-- the following handlers deal with mouse events
public handler OnMouseUp()		
  if the current mouse position is within my bounds then
  	if mToggleHilite then
	  	put not mHilited into mHilited
  	end if
		post "mouseUp" to my script object with [FormatInt(the click button)] -- formatint needed until dp-2
	end if
  put "up" into mState
	redraw all
end handler


public handler OnMouseCancel()
	put "up" into mState
	redraw all
end handler


public handler OnMouseDown()
	put "down" into mState
	redraw all
	
	post "mouseDown" to my script object with [FormatInt(the click button)]
end handler
----------


public handler setIsHilited(in pBoolean as Boolean)
	put pBoolean into mHilited
	redraw all
end handler


public handler setMaintainAspectRatio(in pBoolean as Boolean)
	put pBoolean into mMaintainAspectRatio
	redraw all
end handler


public handler setFlipVertically(in pBoolean as Boolean)
	put pBoolean into mFlipVertically
	redraw all
end handler


public handler setPath(in pPath as String) returns nothing
	put pPath into mPath
	redraw all
end handler


public handler setColor(in pColor as String) returns nothing
	put stringToColor(pColor) into mColor
	redraw all
end handler


public handler getColor() returns String
	return colorToString(mColor, false)
end handler


public handler setHiliteColor(in pColor as String) returns nothing
	put stringToColor(pColor) into mHiliteColor
	redraw all
end handler


public handler getHiliteColor() returns String
	return colorToString(mHiliteColor, false)
end handler

public handler setPathPreset(in pPresetName as String) returns nothing
	put iconSVGPathFromName(pPresetName) into mPath
	put pPresetName into mPathName
	redraw all
end handler

public handler getPathPreset() returns String
	return mPathName
end handler

public handler setAngle(in pAngle as Number) returns nothing
	put pAngle into mAngle
	redraw all
end handler


----------
-- called whenever LiveCode needs to redraw the widget
public handler OnPaint()
	variable tPath as Path
	put path mPath into tPath
	
	variable tBounds	
	if mFlipVertically then
		scale tPath by [1,-1]
		translate tPath by [0, the height of the bounding box of tPath]
	end if
	
	if mAngle > 0 then
		rotate tPath by mAngle
		log the instructions of tPath
	end if
		
	// scale to fit within widget and maintain aspect ratio
	if mMaintainAspectRatio then
		transform tPath by scaleAndMaintainAspectRatioTransform(the bounding box of tPath, my bounds)		
	else
		scale tPath by [my width / the width of the bounding box of tPath, \
			my height / the height of the bounding box of tPath]
	end if
		
	if mState is "up" and mHilited is false then
		set the paint of this canvas to solid paint with mColor
	else
		set the paint of this canvas to solid paint with mHiliteColor
	end if
	
	fill tPath on this canvas
end handler


-- Translated from some Skia code
private handler scaleAndMaintainAspectRatioTransform(in pSrcBounds as Rectangle, in pDestBounds as Rectangle) returns Transform
	// Prepare values for matrix transformation
	variable isLarger as Boolean
	variable tXScale as Number
	variable tYScale as Number
	
	put false into isLarger
	put the width of pDestBounds / the width of pSrcBounds into tXScale
	put the height of pDestBounds / the height of pSrcBounds into tYScale
	
	if tXScale > tYScale then
		put true into isLarger
		put tYScale into tXScale
	else
		put tXScale into tYScale
	end if
	
	variable tXTranslate as Number
	variable tYTranslate as Number
	
	put the left of pDestBounds - (the left of pSrcBounds*tXScale) into tXTranslate
	put the top of pDestBounds - (the top of pSrcBounds*tYScale) into tYTranslate
	
	variable tDiff as Number
	
	if isLarger then
		put my width - (the width of pSrcBounds*tYScale) into tDiff
	else
		put my height - (the height of pSrcBounds*tYScale) into tDiff
	end if
	
	// align center
	divide tDiff by 2
	
	if isLarger then
		add tDiff to tXTranslate
	else
		add tDiff to tYTranslate
	end if
	
	// create transformation matrix and apply
	variable tTransform as Transform
	put transform with matrix [tXScale, 0, 0, tYScale, tXTranslate, tYTranslate] into tTransform
	
	return tTransform
end handler


private handler colorToString(in pColor as Color, in pIncludeAlpha as Boolean) returns String
	variable tColor as String
	
	put FormatInt(the rounded of ((the red of pColor) * 255)) into tColor
	put "," & FormatInt(the rounded of ((the green of pColor) * 255)) after tColor
	put "," & FormatInt(the rounded of ((the blue of pColor) * 255)) after tColor
	
	if pIncludeAlpha then
		put "," & FormatInt(the rounded of ((the alpha of pColor) * 255)) after tColor
	end if
	
	return tColor
end handler

private handler stringToColor(in pString as String) returns Color
	if the number of chars in pString is 0 then
		return ""
		end if

	variable tRed as Real
	variable tGreen as Real
	variable tBlue as Real
	variable tAlpha as Real

	variable tComponentList as List
	split pString by "," into tComponentList

	variable tComponentCount
	put the number of elements in tComponentList into tComponentCount
	if tComponentCount is not 3 and tComponentCount is not 4 then
		// Invalid number of components detected
		throw "Invalid color"
	end if

	put (element 1 of tComponentList) parsed as number into tRed
	put (element 2 of tComponentList) parsed as number into tGreen
	put (element 3 of tComponentList) parsed as number into tBlue

	if tComponentCount is 4 then
		put (element 4 of tComponentList) parsed as number into tAlpha
	else
		put 1.0 into tAlpha
	end if

	return color [ tRed/255, tGreen/255, tBlue/255, tAlpha ]
end handler


-- Needed until dp-4
handler FormatInt(in pNumber as Number) returns String
	variable tNumberString as String

	put pNumber formatted as string into tNumberString

	if "." is in tNumberString then
		variable tDotOffset
		put the first offset of "." in tNumberString into tDotOffset
		delete char tDotOffset to (the number of chars in tNumberString) of tNumberString
	end if

	return tNumberString
end handler

end widget
