Coord = {}
Coord.__index = Coord

function Coord.new(x, y)
    local v = { }
    v.x = x
    v.y = y
    setmetatable(v, Coord)
    return v
end

function Coord.rand4dir()
    if math.random(2) == 2 then
	return Coord.new(0, (math.random(2)-1)*2-1)
    end
    return Coord.new((math.random(2)-1)*2-1, 0)
end

function Coord.allDir()
    local angle = 0
    return function ()
	    angle = angle + 1
	    if angle > 4 then return nil end
	    return Coord.angleToDir(angle)
	end
end

function Coord.all8Dir()
    local angle = 0
    return function ()
	    angle = angle + 1
	    if angle > 8 then return nil end
	    return Coord.angleTo8Dir(angle)
	end
end

function Coord:allAdj()
    local angle = 0
    return function ()
	    angle = angle + 1
	    if angle > 4 then return nil end
	    return self + Coord.angleToDir(angle)
	end
end

function Coord:all8Adj()
    local angle = 0
    return function ()
	    angle = angle + 1
	    if angle > 8 then return nil end
	    return self + Coord.angleTo8Dir(angle)
	end
end

function Coord.angleToDir(angle)
    if bit.band(angle, 1) == 1 then
	if bit.band(angle, 2) == 2 then
	    return Coord.new(-1, 0)
	else
	    return Coord.new(1, 0)
	end
    else
	if bit.band(angle, 2) == 2 then
	    return Coord.new(0, -1)
	else
	    return Coord.new(0, 1)
	end
    end
end

function Coord.angleTo8Dir(angle)
    local a = bit.band(angle, 7)
        if a == 0 then return Coord.new(1, 0) 
    elseif a == 1 then return Coord.new(1, -1) 
    elseif a == 2 then return Coord.new(0, -1) 
    elseif a == 3 then return Coord.new(-1, -1) 
    elseif a == 4 then return Coord.new(-1, 0) 
    elseif a == 5 then return Coord.new(-1, 1) 
    elseif a == 6 then return Coord.new(0, 1) 
    elseif a == 7 then return Coord.new(1, 1) 
    end
end

function Coord.mix(a, b, alpha)
    return a + (b - a) * alpha
end

function Coord:manDist(a)
    return math.abs(a.x-self.x) + math.abs(a.y-self.y)
end

function Coord.left()
    return Coord.new(-1, 0)
end
function Coord.right()
    return Coord.new(1, 0)
end
function Coord.up()
    return Coord.new(0, -1)
end
function Coord.down()
    return Coord.new(0, 1)
end

function Coord.componentWise(a, b, scalar_op)
    if getmetatable(a) == Coord then
	if getmetatable(b) == Coord then
	    return Coord.new(scalar_op(a.x, b.x), scalar_op(a.y, b.y))
	else
	    return Coord.new(scalar_op(a.x, b), scalar_op(a.y, b))
	end
    else
	if getmetatable(b) == Coord then
	    return Coord.new(scalar_op(a, b.x), scalar_op(a, b.y))
	else
	    error("I have completely misunderstood Lua overloads")
	end
    end
end

function Coord.__add(a, b)
    return Coord.componentWise(a, b, function (p,q) return p + q end)
end

function Coord.__sub(a, b)
    return Coord.componentWise(a, b, function (p,q) return p - q end)
end

function Coord.__mul(a, b)
    return Coord.componentWise(a, b, function (p,q) return p * q end)
end

function Coord.__div(a, b)
    return Coord.componentWise(a, b, function (p,q) return p / q end)
end

function Coord.__eq(a, b)
    return a.x == b.x and a.y == b.y
end

Rect = {}
Rect.__index = Rect

function Rect.new(x, y, w, h)
    local v = { }
    v.x = x
    v.y = y
    v.w = w
    v.h = h
    setmetatable(v, Rect)
    return v
end

function Rect.__eq(a, b)
    return a.x == b.x and a.y == b.y and a.w == b.w and a.h == b.h
end

function Rect:clone()
    return Rect.new(self.x, self.y, self.w, self.h)
end

function Rect:center()
    return Coord.new(self.x+self.w/2, self.y + self.h/2)
end

function Rect:size()
    return Coord.new(self.w, self.h)
end

function Rect:xy()
    return Coord.new(self.x, self.y)
end

function Rect:rt()
    return Coord.new(self.x+self.w-1, self.y+self.h-1)
end

function Rect:random(border)
    border = border or 0

    return Coord.new(math.random(self.x+border, self.x+self.w-1-border),
		     math.random(self.y+border, self.y+self.h-1-border))
end

function Rect:translate(xy)
    self.x = self.x + xy.x
    self.y = self.y + xy.y
end

function Rect:contains(xy)
    if getmetatable(xy) == Rect then
	return self:contains(xy:xy(), xy:rt())
    end
    if xy.x < self.x then return false end
    if xy.x >= self.x + self.w then return false end
    if xy.y < self.y then return false end
    if xy.y >= self.y + self.h then return false end
    return true
end

function Rect:overlaps(other, border)
    border = border or 0

    if self.x >= other.x + other.w + border then return false end
    if self.y >= other.y + other.h + border then return false end

    if other.x >= self.x + self.w + border then return false end
    if other.y >= self.y + self.h + border then return false end
	
    return true
end

function Rect:unionWith(other)
    other = other or self

    local x = math.min(self.x, other.x)
    local xw = math.max(self.x+self.w, other.x+other.w)
    local y = math.min(self.y, other.y)
    local yh = math.max(self.y+self.h, other.y+other.h)

    self.x = x
    self.y = y
    self.w = xw - x
    self.h = yh - y
end

function Rect:overlaps_list(others, border)
    for i=1,#others do
	if self:overlaps(others[i], border) then return true end
    end
    return false
end


