package rui

const (
	// Perspective is the name of the SizeUnit property that determines the distance between the z = 0 plane
	// and the user in order to give a 3D-positioned element some perspective. Each 3D element
	// with z > 0 becomes larger; each 3D-element with z < 0 becomes smaller.
	// The default value is 0 (no 3D effects).
	Perspective = "perspective"
	// PerspectiveOriginX is the name of the SizeUnit property that determines the x-coordinate of the position
	// at which the viewer is looking. It is used as the vanishing point by the Perspective property.
	// The default value is 50%.
	PerspectiveOriginX = "perspective-origin-x"
	// PerspectiveOriginY is the name of the SizeUnit property that determines the y-coordinate of the position
	// at which the viewer is looking. It is used as the vanishing point by the Perspective property.
	// The default value is 50%.
	PerspectiveOriginY = "perspective-origin-y"
	// BackfaceVisible is the name of the bool property that sets whether the back face of an element is
	// visible when turned towards the user. Values:
	// true - the back face is visible when turned towards the user (default value);
	// false - the back face is hidden, effectively making the element invisible when turned away from the user.
	BackfaceVisible = "backface-visibility"
	// OriginX is the name of the SizeUnit property that determines the x-coordinate of the point around which
	// a view transformation is applied.
	// The default value is 50%.
	OriginX = "origin-x"
	// OriginY is the name of the SizeUnit property that determines the y-coordinate of the point around which
	// a view transformation is applied.
	// The default value is 50%.
	OriginY = "origin-y"
	// OriginZ is the name of the SizeUnit property that determines the z-coordinate of the point around which
	// a view transformation is applied.
	// The default value is 50%.
	OriginZ = "origin-z"
	// TranslateX is the name of the SizeUnit property that specify the x-axis translation value
	// of a 2D/3D translation
	TranslateX = "translate-x"
	// TranslateY is the name of the SizeUnit property that specify the y-axis translation value
	// of a 2D/3D translation
	TranslateY = "translate-y"
	// TranslateZ is the name of the SizeUnit property that specify the z-axis translation value
	// of a 3D translation
	TranslateZ = "translate-z"
	// ScaleX is the name of the float property that specify the x-axis scaling value of a 2D/3D scale
	// The default value is 1.
	ScaleX = "scale-x"
	// ScaleY is the name of the float property that specify the y-axis scaling value of a 2D/3D scale
	// The default value is 1.
	ScaleY = "scale-y"
	// ScaleZ is the name of the float property that specify the z-axis scaling value of a 3D scale
	// The default value is 1.
	ScaleZ = "scale-z"
	// Rotate is the name of the AngleUnit property that determines the angle of the view rotation.
	// A positive angle denotes a clockwise rotation, a negative angle a counter-clockwise one.
	Rotate = "rotate"
	// RotateX is the name of the float property that determines the x-coordinate of the vector denoting
	// the axis of rotation which could between 0 and 1.
	RotateX = "rotate-x"
	// RotateY is the name of the float property that determines the y-coordinate of the vector denoting
	// the axis of rotation which could between 0 and 1.
	RotateY = "rotate-y"
	// RotateZ is the name of the float property that determines the z-coordinate of the vector denoting
	// the axis of rotation which could between 0 and 1.
	RotateZ = "rotate-z"
	// SkewX is the name of the AngleUnit property that representing the angle to use to distort
	// the element along the abscissa. The default value is 0.
	SkewX = "skew-x"
	// SkewY is the name of the AngleUnit property that representing the angle to use to distort
	// the element along the ordinate. The default value is 0.
	SkewY = "skew-y"
)

func getTransform3D(style Properties, session Session) bool {
	perspective, ok := sizeProperty(style, Perspective, session)
	return ok && perspective.Type != Auto && perspective.Value != 0
}

func getPerspectiveOrigin(style Properties, session Session) (SizeUnit, SizeUnit) {
	x, _ := sizeProperty(style, PerspectiveOriginX, session)
	y, _ := sizeProperty(style, PerspectiveOriginY, session)
	return x, y
}

func getOrigin(style Properties, session Session) (SizeUnit, SizeUnit, SizeUnit) {
	x, _ := sizeProperty(style, OriginX, session)
	y, _ := sizeProperty(style, OriginY, session)
	z, _ := sizeProperty(style, OriginZ, session)
	return x, y, z
}

func getSkew(style Properties, session Session) (AngleUnit, AngleUnit, bool) {
	skewX, okX := angleProperty(style, SkewX, session)
	skewY, okY := angleProperty(style, SkewY, session)
	return skewX, skewY, okX || okY
}

func getTranslate(style Properties, session Session) (SizeUnit, SizeUnit, SizeUnit) {
	x, _ := sizeProperty(style, TranslateX, session)
	y, _ := sizeProperty(style, TranslateY, session)
	z, _ := sizeProperty(style, TranslateZ, session)
	return x, y, z
}

func (style *viewStyle) transform(session Session) string {

	buffer := allocStringBuilder()
	defer freeStringBuilder(buffer)

	skewX, skewY, skewOK := getSkew(style, session)
	if skewOK {
		buffer.WriteString(`skew(`)
		buffer.WriteString(skewX.cssString())
		buffer.WriteRune(',')
		buffer.WriteString(skewY.cssString())
		buffer.WriteRune(')')
	}

	x, y, z := getTranslate(style, session)

	scaleX, okScaleX := floatTextProperty(style, ScaleX, session, 1)
	scaleY, okScaleY := floatTextProperty(style, ScaleY, session, 1)

	if getTransform3D(style, session) {
		if x.Type != Auto || y.Type != Auto || z.Type != Auto {
			if buffer.Len() > 0 {
				buffer.WriteRune(' ')
			}
			buffer.WriteString(`translate3d(`)
			buffer.WriteString(x.cssString("0", session))
			buffer.WriteRune(',')
			buffer.WriteString(y.cssString("0", session))
			buffer.WriteRune(',')
			buffer.WriteString(z.cssString("0", session))
			buffer.WriteRune(')')
		}

		scaleZ, okScaleZ := floatTextProperty(style, ScaleZ, session, 1)
		if okScaleX || okScaleY || okScaleZ {
			if buffer.Len() > 0 {
				buffer.WriteRune(' ')
			}
			buffer.WriteString(`scale3d(`)
			buffer.WriteString(scaleX)
			buffer.WriteRune(',')
			buffer.WriteString(scaleY)
			buffer.WriteRune(',')
			buffer.WriteString(scaleZ)
			buffer.WriteRune(')')
		}

		if angle, ok := angleProperty(style, Rotate, session); ok {
			rotateX, _ := floatTextProperty(style, RotateX, session, 1)
			rotateY, _ := floatTextProperty(style, RotateY, session, 1)
			rotateZ, _ := floatTextProperty(style, RotateZ, session, 1)

			if buffer.Len() > 0 {
				buffer.WriteRune(' ')
			}
			buffer.WriteString(`rotate3d(`)
			buffer.WriteString(rotateX)
			buffer.WriteRune(',')
			buffer.WriteString(rotateY)
			buffer.WriteRune(',')
			buffer.WriteString(rotateZ)
			buffer.WriteRune(',')
			buffer.WriteString(angle.cssString())
			buffer.WriteRune(')')
		}

	} else {
		if x.Type != Auto || y.Type != Auto {
			if buffer.Len() > 0 {
				buffer.WriteRune(' ')
			}
			buffer.WriteString(`translate(`)
			buffer.WriteString(x.cssString("0", session))
			buffer.WriteRune(',')
			buffer.WriteString(y.cssString("0", session))
			buffer.WriteRune(')')
		}

		if okScaleX || okScaleY {
			if buffer.Len() > 0 {
				buffer.WriteRune(' ')
			}
			buffer.WriteString(`scale(`)
			buffer.WriteString(scaleX)
			buffer.WriteRune(',')
			buffer.WriteString(scaleY)
			buffer.WriteRune(')')
		}

		if angle, ok := angleProperty(style, Rotate, session); ok {
			if buffer.Len() > 0 {
				buffer.WriteRune(' ')
			}
			buffer.WriteString(`rotate(`)
			buffer.WriteString(angle.cssString())
			buffer.WriteRune(')')
		}
	}

	return buffer.String()
}

func (style *viewStyle) writeViewTransformCSS(builder cssBuilder, session Session) {
	if getTransform3D(style, session) {
		if perspective, ok := sizeProperty(style, Perspective, session); ok && perspective.Type != Auto && perspective.Value != 0 {
			builder.add(`perspective`, perspective.cssString("0", session))
		}

		x, y := getPerspectiveOrigin(style, session)
		if x.Type != Auto || y.Type != Auto {
			builder.addValues(`perspective-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session))
		}

		if backfaceVisible, ok := boolProperty(style, BackfaceVisible, session); ok {
			if backfaceVisible {
				builder.add(`backface-visibility`, `visible`)
			} else {
				builder.add(`backface-visibility`, `hidden`)
			}
		}

		x, y, z := getOrigin(style, session)
		if x.Type != Auto || y.Type != Auto || z.Type != Auto {
			builder.addValues(`transform-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session), z.cssString("0", session))
		}
	} else {
		x, y, _ := getOrigin(style, session)
		if x.Type != Auto || y.Type != Auto {
			builder.addValues(`transform-origin`, ` `, x.cssString("50%", session), y.cssString("50%", session))
		}
	}

	builder.add(`transform`, style.transform(session))
}

func (view *viewData) updateTransformProperty(tag string) bool {
	htmlID := view.htmlID()
	session := view.session

	switch tag {
	case Perspective:
		updateCSSStyle(htmlID, session)

	case PerspectiveOriginX, PerspectiveOriginY:
		if getTransform3D(view, session) {
			x, y := GetPerspectiveOrigin(view)
			value := ""
			if x.Type != Auto || y.Type != Auto {
				value = x.cssString("50%", session) + " " + y.cssString("50%", session)
			}
			updateCSSProperty(htmlID, "perspective-origin", value, session)
		}

	case BackfaceVisible:
		if getTransform3D(view, session) {
			if GetBackfaceVisible(view) {
				updateCSSProperty(htmlID, BackfaceVisible, "visible", session)
			} else {
				updateCSSProperty(htmlID, BackfaceVisible, "hidden", session)
			}
		}

	case OriginX, OriginY, OriginZ:
		x, y, z := getOrigin(view, session)
		value := ""
		if getTransform3D(view, session) {
			if x.Type != Auto || y.Type != Auto || z.Type != Auto {
				value = x.cssString("50%", session) + " " + y.cssString("50%", session) + " " + z.cssString("50%", session)
			}
		} else {
			if x.Type != Auto || y.Type != Auto {
				value = x.cssString("50%", session) + " " + y.cssString("50%", session)
			}
		}
		updateCSSProperty(htmlID, "transform-origin", value, session)

	case SkewX, SkewY, TranslateX, TranslateY, TranslateZ, ScaleX, ScaleY, ScaleZ, Rotate, RotateX, RotateY, RotateZ:
		updateCSSProperty(htmlID, "transform", view.transform(session), session)

	default:
		return false
	}

	return true
}