package lc

import com.sksamuel.scrimage._
import java.sql._
import java.io._
import httpserver._
import javax.imageio._
import java.awt.image._
import org.json4s._
import org.json4s.jackson.JsonMethods._
import org.json4s.JsonDSL._
import java.util.Base64
import org.json4s.jackson.Serialization
import org.json4s.jackson.Serialization.{read, write}
import scala.Array

trait ChallengeProvider {
  val id: String
  def returnChallenge(): (Image, String)
  def checkAnswer(secret: String, answer: String): Boolean
  //TODO: def configure(): Unit
}

class Captcha {
  val con: Connection = DriverManager.getConnection("jdbc:h2:./captcha", "sa", "")
  val stmt: Statement = con.createStatement()
  stmt.execute("CREATE TABLE IF NOT EXISTS challenge(token varchar, id varchar, secret varchar, image blob)")
  val insertPstmt: PreparedStatement = con.prepareStatement("INSERT INTO challenge(token, id, secret, image) VALUES (?, ?, ?, ?)")
  val selectPstmt: PreparedStatement = con.prepareStatement("SELECT secret FROM challenge WHERE token = ?")
  val imagePstmt: PreparedStatement = con.prepareStatement("SELECT image FROM challenge WHERE token = ?")

  def getCaptcha(id: Id): Array[Byte] = {
  	imagePstmt.setString(1, id.id)
  	val rs: ResultSet = imagePstmt.executeQuery()
  	rs.next()
  	val blob = rs.getBlob("image")
  	var image :Array[Byte] = null
  	if(blob != null)
  		image =  blob.getBytes(1, blob.length().toInt)
  	image
  }

  def convertImage(image: Image): ByteArrayInputStream = {
  	val output = new ByteArrayOutputStream()
  	image.output(new File("Captcha.png"))
  	val img = ImageIO.read(new File("Captcha.png"))
  	ImageIO.write(img,"png",output)
  	val byte_array = output.toByteArray()
  	val blob = new ByteArrayInputStream(byte_array)
  	blob
  }
  
  def getChallenge(param: Parameters): Id = {
  	val provider = new FilterChallenge
    val (image, secret) = provider.returnChallenge()
    val blob = convertImage(image)
    val token = scala.util.Random.nextInt(10000).toString
    val id = Id(token)
    insertPstmt.setString(1, token)
    insertPstmt.setString(2, provider.id)
    insertPstmt.setString(3, secret)
    insertPstmt.setBlob(4, blob)
    insertPstmt.executeUpdate()
    id
  }

  def getAnswer(token: String, answer: String, provider: ChallengeProvider): Boolean = {
    selectPstmt.setString(1, token)
    val rs: ResultSet = selectPstmt.executeQuery()
    rs.next()
    val secret = rs.getString("secret")
    provider.checkAnswer(secret, answer)
  }

  def display(): Unit = {
    val rs: ResultSet = stmt.executeQuery("SELECT * FROM challenge")
    println("token\t\tid\t\tsecret\t\timage")
    while(rs.next()) {
      val token = rs.getString("token")
      val id = rs.getString("id")
      val secret = rs.getString("secret")
      val image = rs.getString("image")
      println(s"${token}\t\t${id}\t\t${secret}\t\t${image}")
    }
  }
  
  def closeConnection(): Unit = {
	  con.close()
  } 
}

case class Size(height: Int, width: Int)
case class Parameters(level: String, media: String, input_type: String, size: Option[Size])
case class Id(id: String)

object LCFramework{
  def main(args: scala.Array[String]) {
  	val port = 8888
    val captcha = new Captcha
    val server = new HTTPServer(port)
    val host = server.getVirtualHost(null)

    implicit val formats = DefaultFormats
    
    host.addContext("/v1/captcha",(req, resp) => {
    	val body = req.getJson()
    	val json = parse(body)
    	val param = json.extract[Parameters]
    	val id = captcha.getChallenge(param)
    	resp.getHeaders().add("Content-Type","application/json")
    	resp.send(200, write(id))
    	0
    })

    host.addContext("/v1/media",(req, resp) => {
    	val body = req.getJson()
    	val json = parse(body)
    	val id = json.extract[Id]
    	val image = captcha.getCaptcha(id)
    	println(image.toString())
    	println(image)
    	resp.getHeaders().add("Content-Type","image/png")
    	resp.send(200, image)
    	0
    })
    server.start()
  } 
}