package bresenham

import (
	"lab.zaar.be/thefish/alchemyst-go/engine/types"
)

// dx > dy; x1 < x2; y1 < y2
func BresenhamDxXRYD(canvas types.Putable, From, To types.Coords, symFunc func() interface{}) {
	dx, dy := To.X - From.X, 2*(To.Y - From.Y)
	e, slope := dx, 2*dx
	for ; dx != 0; dx-- {
		canvas.Put(From.X, From.Y, symFunc())
		From.X++
		e -= dy
		if e < 0 {
			From.Y++
			e += slope
		}
	}
}

// dy > dx; x1 < x2; y1 < y2
func BresenhamDyXRYD(canvas types.Putable, From, To types.Coords, symFunc func() interface{}) {
	dx, dy := 2*(To.X - From.X), To.Y - From.Y
	e, slope := dy, 2*dy
	for ; dy != 0; dy-- {
		canvas.Put(From.X, From.Y, symFunc())
		From.Y++
		e -= dx
		if e < 0 {
			From.X++
			e += slope
		}
	}
}

// dx > dy; x1 < x2; y1 > y2
func BresenhamDxXRYU(canvas types.Putable, From, To types.Coords, symFunc func() interface{}) {
	dx, dy := To.X - From.X, 2*(From.Y-To.Y)
	e, slope := dx, 2*dx
	for ; dx != 0; dx-- {
		canvas.Put(From.X, From.Y, symFunc())
		From.X++
		e -= dy
		if e < 0 {
			From.Y--
			e += slope
		}
	}
}

func BresenhamDyXRYU(canvas types.Putable, From, To types.Coords, symFunc func() interface{}) {
	dx, dy := 2*(To.X - From.X), From.Y-To.Y
	e, slope := dy, 2*dy
	for ; dy != 0; dy-- {
		canvas.Put(From.X, From.Y, symFunc())
		From.Y--
		e -= dx
		if e < 0 {
			From.X++
			e += slope
		}
	}
}

// Generalized with integer
func Bresenham(canvas types.Putable, From, To types.Coords, symFunc func() interface{}) {
	var dx, dy, e, slope int

	// Because drawing p1 -> p2 is equivalent to draw p2 -> p1,
	// I sort points in x-axis order to handle only half of possible cases.
	if From.X > To.X {
		From.X, From.Y, To.X, To.Y = To.X, To.Y, From.X, From.Y
	}

	dx, dy = To.X - From.X, To.Y - From.Y
	// Because point is x-axis ordered, dx cannot be negative
	if dy < 0 {
		dy = -dy
	}

	switch {

	// Is line a point ?
	case From.X ==To.X && From.Y ==To.Y:
		canvas.Put(From.X, From.Y, symFunc())

		// Is line an horizontal ?
	case From.Y ==To.Y:
		for ; dx != 0; dx-- {
			canvas.Put(From.X, From.Y, symFunc())
			From.X++
		}
		canvas.Put(From.X, From.Y, symFunc())

		// Is line a vertical ?
	case From.X ==To.X:
		if From.Y > To.Y {
			From.Y, To.Y = To.Y, From.Y
		}
		for ; dy != 0; dy-- {
			canvas.Put(From.X, From.Y, symFunc())
			From.Y++
		}
		canvas.Put(From.X, From.Y, symFunc())

		// Is line a diagonal ?
	case dx == dy:
		if From.Y < To.Y {
			for ; dx != 0; dx-- {
				canvas.Put(From.X, From.Y, symFunc())
				From.X++
				From.Y++
			}
		} else {
			for ; dx != 0; dx-- {
				canvas.Put(From.X, From.Y, symFunc())
				From.X++
				From.Y--
			}
		}
		canvas.Put(From.X, From.Y, symFunc())

		// wider than high ?
	case dx > dy:
		if From.Y < To.Y {
			// BresenhamDxXRYD(img, x1, y1, x2, y2, col)
			dy, e, slope = 2*dy, dx, 2*dx
			for ; dx != 0; dx-- {
				canvas.Put(From.X, From.Y, symFunc())
				From.X++
				e -= dy
				if e < 0 {
					From.Y++
					e += slope
				}
			}
		} else {
			// BresenhamDxXRYU(img, x1, y1, x2, y2, col)
			dy, e, slope = 2*dy, dx, 2*dx
			for ; dx != 0; dx-- {
				canvas.Put(From.X, From.Y, symFunc())
				From.X++
				e -= dy
				if e < 0 {
					From.Y--
					e += slope
				}
			}
		}
		canvas.Put(To.X, To.Y, symFunc())

		// higher than wide.
	default:
		if From.Y < To.Y {
			// BresenhamDyXRYD(img, x1, y1, x2, y2, col)
			dx, e, slope = 2*dx, dy, 2*dy
			for ; dy != 0; dy-- {
				canvas.Put(From.X, From.Y, symFunc())
				From.Y++
				e -= dx
				if e < 0 {
					From.X++
					e += slope
				}
			}
		} else {
			// BresenhamDyXRYU(img, x1, y1, x2, y2, col)
			dx, e, slope = 2*dx, dy, 2*dy
			for ; dy != 0; dy-- {
				canvas.Put(From.X, From.Y, symFunc())
				From.Y--
				e -= dx
				if e < 0 {
					From.X++
					e += slope
				}
			}
		}
		canvas.Put(To.X, To.Y, symFunc())
		//img.Set(x2, y2, col)
	}
}