function scale_text_delta(template, delta)
    template = template.slide
    for i = 1, #template do
        local paint = template[i].paint
        paint:setTextSize(paint:getTextSize() + delta)
    end
end

function slide_transition(prev, next, is_forward)
    local rec = {
        proc = function(self, canvas, drawSlideProc)
            if self:isDone() then
                drawSlideProc(canvas)
                return nil
            end
            self.prevDrawable:draw(canvas, self.curr_x, 0)
            self.nextDrawable:draw(canvas, self.curr_x + 640, 0)
            self.curr_x = self.curr_x + self.step_x
            return self
        end
    }
    if is_forward then
        rec.prevDrawable = prev
        rec.nextDrawable = next
        rec.curr_x = 0
        rec.step_x = -15
        rec.isDone = function (self) return self.curr_x <= -640 end
    else
        rec.prevDrawable = next
        rec.nextDrawable = prev
        rec.curr_x = -640
        rec.step_x = 15
        rec.isDone = function (self) return self.curr_x >= 0 end
    end
    return rec
end

function sqr(value) return value * value end

function set_blur(paint, alpha)
    local sigma = sqr(1 - alpha) * 20
    if gUseBlurInTransitions then
        paint:setImageFilter(Sk.newBlurImageFilter(sigma, sigma))
    end
    paint:setAlpha(alpha)
end

function fade_slide_transition(prev, next, is_forward)
    local rec = {
        paint = Sk.newPaint(),
        prevDrawable = prev,
        nextDrawable = next,
        proc = function(self, canvas, drawSlideProc)
            if self:isDone() then
                drawSlideProc(canvas)
                return nil
            end

            set_blur(self.paint, self.prev_a)
            self.prevDrawable:draw(canvas, self.prev_x, 0, self.paint)

            set_blur(self.paint, self.next_a)
            self.nextDrawable:draw(canvas, self.next_x, 0, self.paint)
            self:step()
            return self
        end
    }
    if is_forward then
        rec.prev_x = 0
        rec.prev_a = 1
        rec.next_x = 640
        rec.next_a = 0
        rec.isDone = function (self) return self.next_x <= 0 end
        rec.step = function (self)
            self.next_x = self.next_x - 20
            self.next_a = (640 - self.next_x) / 640
            self.prev_a = 1 - self.next_a
        end
    else
        rec.prev_x = 0
        rec.prev_a = 1
        rec.next_x = 0
        rec.next_a = 0
        rec.isDone = function (self) return self.prev_x >= 640 end
        rec.step = function (self)
            self.prev_x = self.prev_x + 20
            self.prev_a = (640 - self.prev_x) / 640
            self.next_a = 1 - self.prev_a
        end
    end
    return rec
end

function fade_transition(prev, next, is_forward)
    local rec = {
        paint = Sk.newPaint(),
        prevDrawable = prev,
        nextDrawable = next,
        proc = function(self, canvas, drawSlideProc)
            if self:isDone() then
                drawSlideProc(canvas)
                return nil
            end

            set_blur(self.paint, self.prev_a)
            self.prevDrawable:draw(canvas, 0, 0, self.paint)

            set_blur(self.paint, self.next_a)
            self.nextDrawable:draw(canvas, 0, 0, self.paint)
            self:step()
            return self
        end
    }
    rec.prev_a = 1
    rec.next_a = 0
    rec.isDone = function (self) return self.next_a >= 1 end
    rec.step = function (self)
        self.prev_a = math.max(self.prev_a - 0.025, 0)
        self.next_a = 1 - self.prev_a
    end

    return rec
end

function rotate_transition(prev, next, is_forward)
    local rec = {
        angle = 0,
        prevDrawable = prev,
        nextDrawable = next,
        activeDrawable = prev,
        proc = function(self, canvas, drawSlideProc)
            if self:isDone() then
                drawSlideProc(canvas)
                return nil
            end

            canvas:save()
            canvas:translate(320, 240)
            canvas:rotate(self.angle)
            canvas:translate(-320, -240)
            self.activeDrawable:draw(canvas, 0, 0)
            self:step()
            return self
        end,
        isDone = function (self) return self.angle >= 360 or self.angle <= -360 end
    }
    if is_forward then
        rec.step = function (self)
            self.angle = self.angle + 10
            if self.angle >= 180 then
                self.activeDrawable = self.nextDrawable
            end
        end
    else
        rec.step = function (self)
            self.angle = self.angle - 10
            if self.angle <= -180 then
                self.activeDrawable = self.nextDrawable
            end
        end
    end
    return rec
end

function zoom_transition(prev, next, is_forward)
    local rec = {
        scale = 1,
        scale_delta = .95,
        scale_limit = 0.2,
        pivot_x = 320,
        pivot_y = 240,
        prevDrawable = prev,
        nextDrawable = next,
        activeDrawable = prev,
        proc = function(self, canvas, drawSlideProc)
            if self:isDone() then
                drawSlideProc(canvas)
                return nil
            end

            canvas:translate(self.pivot_x, self.pivot_y)
            canvas:scale(self.scale, self.scale)
            canvas:translate(-self.pivot_x, -self.pivot_y)
            self.activeDrawable:draw(canvas, 0, 0)
            self:step()
            return self
        end,
        isDone = function (self) return self.scale > 1 end,
        step = function (self)
            if self.scale < self.scale_limit then
                self.scale = self.scale_limit
                self.scale_delta = 1 / self.scale_delta
                self.activeDrawable = self.nextDrawable
            end
            self.scale = self.scale * self.scale_delta
        end
    }
    return rec
end

gTransitionTable = {
    fade = fade_transition,
    slide = slide_transition,
    fade_slide = fade_slide_transition,
    rotate = rotate_transition,
    zoom = zoom_transition,
}