-- Copyright startx <startx@plentyfact.org>
-- Modifications copyright mrDoctorWho <mrdoctorwho@gmail.com>
-- Published under the MIT license

-- module("captcha", package.seeall)

local M = {}

local gd = require 'gd'

local mt = { __index = {} }


function M.new()
	local cap = {}
	local f = setmetatable({ cap = cap}, mt)
	return f
end


local function urandom()
	local seed = 1
	local devurandom = io.open("/dev/urandom", "r")
	local urandom = devurandom:read(32)
	devurandom:close()

	for i=1,string.len(urandom) do
		local s = string.byte(urandom,i)
		seed = seed + s
	end
	return seed
end


local function random_char(length)
	local set, char, uid
	local set = [[1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]]
	local captcha_t = {}

	math.randomseed(urandom())
	
	for c=1,length do
		 local i = math.random(1, string.len(set))
		 table.insert(captcha_t, string.sub(set,i,i))
	end

	return captcha_t
end


local function random_angle()
	math.randomseed(urandom())
	return math.random(-20, 40) 
end


local function scribble(w,h)
	math.randomseed(urandom())
	local x1 = math.random(5, w - 5)
	local x2 = math.random(5, w - 5)
	return x1, x2
end


function mt.__index:string(s)
	self.cap.string = s
end

function mt.__index:scribble(n)
	self.cap.scribble = n or 20
end

function mt.__index:length(l)
	self.cap.length = l
end


function mt.__index:bgcolor(r,g,b)
	self.cap.bgcolor = { r = r , g = g , b = b}
end

function mt.__index:fgcolor(r,g,b)
	self.cap.fgcolor = { r = r , g = g , b = b}
end

function mt.__index:line(line)
	self.cap.line = line
end


function mt.__index:font(font)
	self.cap.font = font 
end


function mt.__index:generate()
	--local self.captcha = {}
	local captcha_t = {}

	if not self.cap.string then
		 if not self.cap.length then
			self.cap.length = 6
		 end
		 captcha_t = random_char(self.cap.length)
		 self:string(table.concat(captcha_t))
	else
		 for i=1, #self.cap.string do
			table.insert(captcha_t, string.sub(self.cap.string, i, i))
		 end
	end


	self.im = gd.createTrueColor(#captcha_t * 40, 45)
	local black = self.im:colorAllocate(0, 0, 0)
	local white = self.im:colorAllocate(255, 255, 255)
	local bgcolor
	if not self.cap.bgcolor then
		 bgcolor = white      
	else
		 bgcolor = self.im:colorAllocate(self.cap.bgcolor.r , self.cap.bgcolor.g, self.cap.bgcolor.b )
	end

	local fgcolor
	if not self.cap.fgcolor then
		fgcolor = black
	else
		fgcolor = self.im:colorAllocate(self.cap.fgcolor.r , self.cap.fgcolor.g, self.cap.fgcolor.b )
	end

	self.im:filledRectangle(0, 0, #captcha_t * 40, 45, bgcolor)
	
	local offset_left = 10

	for i=1, #captcha_t do
		local angle = random_angle()
		local llx, lly, lrx, lry, urx, ury, ulx, uly = self.im:stringFT(fgcolor, self.cap.font, 25, math.rad(angle), offset_left, 35, captcha_t[i])
		self.im:polygon({ {llx, lly}, {lrx, lry}, {urx, ury}, {ulx, uly} }, bgcolor)
		offset_left = offset_left + 40
	end

	if self.cap.line then
		self.im:line(10, 10, ( #captcha_t * 40 ) - 10  , 40, fgcolor)
		self.im:line(11, 11, ( #captcha_t * 40 ) - 11  , 41, fgcolor)
		self.im:line(12, 12, ( #captcha_t * 40 ) - 12  , 42, fgcolor)
	end


	if self.cap.scribble then
		for i=1,self.cap.scribble do
			local x1,x2 = scribble( #captcha_t * 40 , 45 )
			self.im:line(x1, 5, x2, 40, fgcolor)
		end
	end
end


-- Perhaps it's not the best solution
-- Writes the generated image to a jpeg file
function mt.__index:jpeg(outfile, quality)
	self.im:jpeg(outfile, quality)
end

-- Writes the generated image to a png file
function mt.__index:png(outfile)
	self.im:png(outfile)
end

-- Allows to get the image data in PNG format
function mt.__index:pngStr()
	return self.im:pngStr()
end

-- Allows to get the image data in JPEG format
function mt.__index:jpegStr(quality)
	return self.im:jpegStr(quality)
end

-- Allows to get the image text
function mt.__index:getStr()
	return self.cap.string
end

-- Writes the image to a file
function mt.__index:write(outfile, quality)
	if self.cap.string == nil then
		self:generate()
	end
	self:jpeg(outfile, quality)
	-- Compatibility
	return self:getStr()
end

return M
