import jp.sf.amateras.mirage.scala.{SqlManager, Sql, Session}
import org.specs.Specification
import jp.sf.amateras.mirage.util.IOUtil
import jp.sf.amateras.mirage.annotation.PrimaryKey.GenerationType
import jp.sf.amateras.mirage.annotation.{Table, PrimaryKey}

class SqlManagerTest extends Specification {

  val SQL_PREFIX: String = "jp/sf/amateras/mirage/scala/";

  val session: Session = Session.get
  val sqlManager: SqlManager = session.sqlManager

  def setup(){
    session.begin()
    executeMultipleStatement(SQL_PREFIX + "SqlManagerImplTest_setUp.sql")

    sqlManager.executeUpdate(Sql("""
    INSERT INTO BOOK (BOOK_ID, BOOK_NAME, AUTHOR, PRICE)
    VALUES (1, 'Mirage in Action', 'Naoki Takezoe', 4800)
    """))

    val entity = new Book
    entity.bookName = "現場で使えるJavaライブラリ"
    entity.author = "Naoki Takezoe"
    entity.price = 3600
    sqlManager.insertEntity(entity)
  }

  def tearDown(){
    executeMultipleStatement(SQL_PREFIX + "SqlManagerImplTest_tearDown.sql")
    session.rollback
    session.release
  }

  def executeMultipleStatement(sqlPath: String) {
    val cl: ClassLoader = Thread.currentThread().getContextClassLoader()
    val bytes: Array[Byte] = IOUtil.readStream(cl.getResourceAsStream(sqlPath))
    val sql: String = new String(bytes, "UTF-8")
    for(statement: String <- sql.split(";")){
      if(statement.trim().length() > 0){
        sqlManager.executeUpdate(Sql(statement))
      }
    }
  }

  "getResultList()" should {
    setup.before
    tearDown.after
    "return correct results with JavaBean as a parameter" in {
      val resultList = sqlManager.getResultList(classOf[Book], Sql("""
       SELECT BOOK_ID, BOOK_NAME, AUTHOR, PRICE
       FROM BOOK
       /*IF author != null*/
         WHERE AUTHOR=/*author*/'Naoki Takezoe'
       /*END*/
      """), new BookParam("Naoki Takezoe"))

      resultList.size mustBe 2
      resultList(0).bookName mustEqual "Mirage in Action"
      resultList(1).bookName mustEqual "現場で使えるJavaライブラリ"
      resultList.foreach({book => println(book.bookName + ", " + book.price)})
    }

    "return correct results with Map as a parameter" in {
      val resultList = sqlManager.getResultList(classOf[Map[String, _]], Sql("""
       SELECT BOOK_ID, BOOK_NAME, AUTHOR, PRICE
       FROM BOOK
       /*IF bookName != null*/
         WHERE BOOK_NAME=/*bookName*/'Mirage in Action'
       /*END*/
      """), Map("bookName" -> "Mirage in Action"))

      resultList.size mustBe 1
      resultList(0)("bookName").asInstanceOf[String] mustEqual "Mirage in Action"
      resultList.foreach({book => println(book("bookName") + ", " + book("price"))})
    }
  }

  "getCount()" should {
    setup.before
    tearDown.after
    "return a correct count" in {
      val count = sqlManager.getCount(Sql("SELECT BOOK_ID FROM BOOK"))
      count mustBe 2
      println("count = " + count)
    }
  }

  "findEntity()" should {
    setup.before
    tearDown.after
    "return an entity that has a given primary key" in {
      val clazz = classOf[Book]
      val book = sqlManager.findEntity(classOf[Book], 1)
      book.get.bookName mustEqual "Mirage in Action"
    }
  }

  "iterate()" should {
    setup.before
    tearDown.after
    "invokes the callback method for each row" in {
      val result = sqlManager.iterate(classOf[Book],
        Sql("SELECT BOOK_ID, BOOK_NAME, AUTHOR, PRICE FROM BOOK"), 0)
        { (book, sum) => sum + book.price.intValue }

      result mustEqual 8400
    }
  }

  "insertBatch()" should {
    setup.before
    tearDown.after
    "insert given entities and return a inserted row count" in {
      val result = sqlManager.insertBatch(
          new Book("Programming Scala", "Dean Wampler, Alex Payne", 3669),
          new Book("Programming in Scala", "Martin Odersky, Lex Spoon, Bill Venners", 4482))
      result mustEqual 2

//      val book1 = sqlManager.getSingleResult(classOf[Book],
//          Sql("SELECT BOOK_ID, BOOK_NAME, AUTHOR, PRICE FROM BOOK WHERE BOOK_NAME=/*bookName*/"),
//          Map("bookName" -> "Programming Scala"))
//
//      book1.get.author mustEqual "Dean Wampler, Alex Payne"
//      book1.get.price mustEqual 3669
//
//      val book2 = sqlManager.getSingleResult(classOf[Book],
//          Sql("SELECT BOOK_ID, BOOK_NAME, AUTHOR, PRICE FROM BOOK WHERE BOOK_NAME=/*bookName*/"),
//          Map("bookName" -> "Programming in Scala"))
//
//      book2.get.author mustEqual "Martin Odersky, Lex Spoon, Bill Venners"
//      book2.get.price mustEqual 4482
    }
  }

}

/**
 * The entity class for the BOOK table.
 */
@Table(name="BOOK")
class Book {

  @PrimaryKey(generationType = GenerationType.IDENTITY)
  var bookId: java.lang.Long = null
  var bookName: String = null
  var author: String = null
  var price: java.lang.Integer = null

  def this(bookName: String, author: String, price: java.lang.Integer) = {
    this()
    this.bookName = bookName
    this.author = author
    this.price = price
  }

}

/**
 * The parameter class for searching by author.
 */
class BookParam(val author: String)
