/*
 * ********************************************************************************************************************
 *
 *               iFHS7.
 *              ;BBMBMBMc                  rZMBMBR              BMB
 *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
 *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
 *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
 *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
 *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
 *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
 *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
 *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
 *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
 *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
 *         i0BM: SWKHKGO  MBDv
 *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
 *              ,  XMW  ..
 *                  r                                                                     All rights reserved.
 *
 * ********************************************************************************************************************
 */

package com.sybotan.android.database

import android.util.Log
import com.sybotan.android.database.SDatabase.use
import com.sybotan.base.exceptions.SFieldNotFoundException
import com.sybotan.base.extensions.*
import com.sybotan.database.SAbstractDao
import com.sybotan.database.SFilter
import com.sybotan.database.SQueryBuilder
import com.sybotan.database.enums.SOps
import com.sybotan.database.enums.SDirection
import com.sybotan.database.extensions.keyColumnList
import org.jetbrains.anko.db.*
import java.io.Serializable
import javax.persistence.Column

/**
 * 数据库访问对象
 *
 * @author  庞利祥(sybotan@126.com)
 */
abstract class SAndroidDao<ENTITY: Any>(entityClazz: Class<ENTITY>) : SAbstractDao<ENTITY>(entityClazz) {
    companion object {
        val TAG = SAndroidDao::class.java.name
    } // Companion object

    /** 行解析器 */
    private val parser = SRowParser<ENTITY>(entityClass)

    /**
     * 在数据库中插入实体
     *
     * @param   entity      插入数据库的实体
     * @return  返回实体。如果主键为数据库生成时，该实体主键将被填充。
     */
    override fun insert(entity: ENTITY): Boolean {
        use {
            val listPair= buildInsertValues(entity)
            insert("`${tableName()}`", *Array(listPair.size){i-> listPair[i]})
        }
        return true
    } // Function insert()

    /**
     * 在数据库中插入实体
     *
     * @param   entity      插入数据库的实体
     * @return  手入是否成功
     */
    override fun replace(entity: ENTITY): Boolean {
        use {
            val listPair= buildInsertValues(entity)
            replace("`${tableName()}`", *Array(listPair.size){i-> listPair[i]})
        }
        return true
    } // Function replace()

    /**
     * 根据条件删除
     *
     * @param   args        删除条件
     */
    override fun delete(args: List<SFilter>): Boolean {
        use {
            val whereArgs = ArrayList<Pair<String, Any>>()
            val clause = buildWhereArgList(args, whereArgs)
            delete("`${tableName()}`", clause, *Array(whereArgs.size){i-> whereArgs[i]})
        }
        return true
    } // Function delete()

    /**
     * 删除所有
     */
    override fun deleteAll(): Boolean {
        use {
            delete(tableName())
        }
        return true
    } // Function deleteAll()

    override fun update(entity: ENTITY, projection: ArrayList<String>?, args: List<SFilter>?): Boolean {
                use {
            val whereArgs = ArrayList<Pair<String, Any>>()
            val clause = buildWhereArgList(args!!, whereArgs)
            val valueList = buildUpdateValues(entity, projection)
            update("`${tableName()}`", *Array(valueList.size){ i-> valueList[i]})
                    .whereArgs(clause, *Array(whereArgs.size){i-> whereArgs[i]}).exec()
        }
        return true
    }



    /**
     * 根据条件更新
     *
     * @param   projection  将被更新的字段列表（以逗号分隔），为空表示更新所有非空字段
     * @param   args        删除条件
     * @return  更新是否成功
     */
//    override fun update(entity: ENTITY, projection: ArrayList<String>?, args: List<SFilter>): Boolean {
//        use {
//            val whereArgs = ArrayList<Pair<String, Any>>()
//            val clause = buildWhereArgList(args, whereArgs)
//            val valueList = buildUpdateValues(entity, projection)
//            update("`${tableName()}`", *Array(valueList.size){ i-> valueList[i]})
//                    .whereArgs(clause, *Array(whereArgs.size){i-> whereArgs[i]}).exec()
//        }
//        return true
//    } // Function update()




    /**
     * 根据条件查询记录
     *
     * @param   args        查询条件
     * @return  查询到的记录
     */
    override fun select(args: List<SFilter>): SQueryBuilder<ENTITY> {
        // 以下循环解决 “SingletonList cannot be cast to java.util.ArrayList” 错误。
        val filterList = ArrayList<SFilter>()
        args.forEach {
            filterList.add(it)
        }
        return SQueryBuilder(this, filterList)
    } // Function select()

    /**
     * 查询所有记录
     *
     * @return  查询到的记录
     */
    override fun selectAll(): SQueryBuilder<ENTITY> {
        return SQueryBuilder(this, ArrayList())
    } // Function selectAll()

    /**
     * 执行查询操作
     *
     * @param   builder     查询查询构造器
     * @return  查询到的记录
     */
    override fun execQuery(builder: SQueryBuilder<ENTITY>): ArrayList<ENTITY> {
        var list = ArrayList<ENTITY>()
        use {
            val queryBuilder = select("`${builder.tableName ?: tableName()}`")
            // 设置查询条件
            if (builder.filterList.isNotEmpty()) {       // 有查询条件
                val whereArgs = ArrayList<Pair<String, Any>>()
                val clause = buildWhereArgList(builder.filterList, whereArgs)
                Log.d(TAG, "clause=$clause")
                Log.d(TAG, "whereArgs=${whereArgs.toJson()}")
                queryBuilder.whereArgs(clause, *Array(whereArgs.size) { i -> whereArgs[i] })
            }

            //queryBuilder.column(buildColumns(gennericClass()!!, builder.fieldList))

            // 设置排序条件
            for (order in builder.orderArgs) {       // 依次取出排序条件
                if (order.direction == SDirection.ASC) {
                    queryBuilder.orderBy("`${this@SAndroidDao.columnName(order.name)!!}`")
                } else {
                    queryBuilder.orderBy("`${this@SAndroidDao.columnName(order.name)!!}`", SqlOrderDirection.DESC)
                }
            }

            if (builder.isDistinct) {       // 如果去重
                queryBuilder.distinct()
            }

            if (builder.count != null) {        // 限定从指定记录开始取出的记录数与
                if (builder.offset != null) {
                    queryBuilder.limit(builder.offset, builder.count!!)
                } else {
                    queryBuilder.limit(builder.count!!)
                }
            }

            // 执行查询
            queryBuilder.exec {
                list = this.parseList(parser) as ArrayList<ENTITY>
            }
        }
        return list
    } // Function exec()

    /**
     * 执行查询操作
     *
     * @param   builder     查询查询构造器
     * @return  查询到的记录
     */
    override fun execCount(builder: SQueryBuilder<ENTITY>): Long {
        var result = 0L
        var list = ArrayList<Serializable>()
        use {
            val queryBuilder = select("`${builder.tableName ?: tableName()}`")
            // 设置查询条件
            if (builder.filterList.isNotEmpty()) {       // 有查询条件
                val whereArgs = ArrayList<Pair<String, Any>>()
                val clause = "count(1)"
                Log.d(TAG, "clause=$clause")
                Log.d(TAG, "whereArgs=${whereArgs.toJson()}")
                queryBuilder.whereArgs(clause, *Array(whereArgs.size) { i -> whereArgs[i] })
            }

            // 执行查询
            queryBuilder.exec {
                result = this.parseSingle(object:RowParser<Long>{
                    override fun parseRow(columns: Array<Any?>): Long {
                        return columns[0]!!.toLong() ?: 0
                    }
                })
            }
        }
        return result
    } // Function exec()

    /**
     * 获得列名
     *
     * @param   name    属性名
     * @return  对应的数据表字段名
     */
    private fun columnName(name: String): String? {
        return try {
            val clz = gennericClass()
            val field = clz!!.getAllDeclaredField(name, true, true)
            val anno = field?.getAnnotation(Column::class.java)
            anno!!.name
        } catch (e: Exception) {
            null
        }
    } // Function columnName()

    /**
     * 构造where子句参数
     *
     * @param   filterList      过滤条件
     * @param   args            子句参数列表
     * @return  返回where子句
     */
    private fun buildWhereArgList(filterList: List<SFilter>, args: ArrayList<Pair<String, Any>>): String {
        val builder = StringBuilder()
        for (filter in filterList) {
            val where = buildWhereArg(filter, args)
            if (builder.isNotEmpty()) {
                builder.append(" AND ")
            }
            builder.append(where)
        }
        return builder.toString()
    } // Function buildWhereArgList()

    /**
     * 构造where子句参数
     *
     * @param   filter          过滤条件
     * @param   args            子句参数列表
     * @return  返回where子句
     */
    fun buildWhereArg(filter: SFilter, args: ArrayList<Pair<String, Any>>): String {
        val builder = StringBuilder()
        try {
            val argName = "arg${args.size}"
            when (filter.opt) {
                SOps.EQ -> {     // 等于
                    builder.append("`${columnName(filter.first!!.toString())}` = {$argName}")
                    args.add(Pair(argName, filter.second!!))
                }
                SOps.NE -> {     // 不等于
                    builder.append("`${columnName(filter.first!!.toString())}` <> {$argName}")
                    args.add(Pair(argName, filter.second!!))
                }
                SOps.GT -> {     // 大于
                    builder.append("`${columnName(filter.first!!.toString())}` > {$argName}")
                    args.add(Pair(argName, filter.second!!))
                }
                SOps.GTE -> {    // 大于等于
                    builder.append("`${columnName(filter.first!!.toString())}` >= {$argName}")
                    args.add(Pair(argName, filter.second!!))
                }
                SOps.LT -> {     // 小于
                    builder.append("`${columnName(filter.first!!.toString())}` < {$argName}")
                    args.add(Pair(argName, filter.second!!))
                }
                SOps.LTE -> {    // 小于等行
                    builder.append("`${columnName(filter.first!!.toString())}` <= {$argName}")
                    args.add(Pair(argName, filter.second!!))
                }
                SOps.CONTAIN -> {    // 包含
                    builder.append("`${columnName(filter.first!!.toString())}` LIKE \"%\" || {$argName} || \"%\"")
                    args.add(Pair(argName, filter.second!!))
                }
                SOps.STARTWITH -> {    // 小于等行
                    builder.append("`${columnName(filter.first!!.toString())}` LIKE {$argName} || \"%\"")
                    args.add(Pair(argName, filter.second!!))
                }
                SOps.ENDWITH -> {    // 小于等行
                    builder.append("`${columnName(filter.first!!.toString())}` LIKE \"%\" || {$argName}")
                    args.add(Pair(argName, filter.second!!))
                }
                SOps.ISNULL -> {     // 为空
                    builder.append("`${columnName(filter.first!!.toString())}` ISNULL")
                }
                SOps.IN -> {         // 在列表内
                    val inArg = buidInArgs(filter, args, argName)
                    builder.append("`${columnName(filter.first!!.toString())}` IN ($inArg)")
                }
                SOps.NOT -> {        // 逻辑非
                    builder.append("NOT (${buildWhereArg(filter.first as SFilter, args)})")
                }
                SOps.AND -> {        // 逻辑与
                    builder.append("(${buildWhereArg(filter.first as SFilter, args)}) AND (${buildWhereArg(filter.second as SFilter, args)})")
                }
                SOps.OR -> {         // 逻辑或
                    builder.append("((${buildWhereArg(filter.first as SFilter, args)}) OR (${buildWhereArg(filter.second as SFilter, args)}))")
                }
                else -> {
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return builder.toString()
    } // Function buildWhereArgList()

    /**
     * 生成IN操作参数
     */
    private fun buidInArgs(filter: SFilter, args: ArrayList<Pair<String, Any>>, argName: String): String {
        val builder = StringBuilder()
        val argList = filter.second!! as List<Any>
        for ((index, item) in argList.withIndex()) {
            if (index > 0) {
                builder.append(", {${argName}_$index}")
            } else {
                builder.append("{${argName}_$index}")
            }
            args.add(Pair("${argName}_$index", item))
        }
        return builder.toString()
    } // Function buidInArgs()

    /**
     * 构建values值
     *
     * @param   entity      实体对象
     * @return  values列表
     */
    protected fun buildInsertValues(entity: ENTITY): ArrayList<Pair<String, Any?>> {
        val clz = entity::class.java
        val list = ArrayList<Pair<String, Any?>>()
        for (field in clz.declaredFields) {
            if (field.isAnnotationPresent(Column::class.java)) {
                val anno = field.getAnnotation(Column::class.java) ?: continue
                // 如果插入状态或更新状态为否，则跳过该字段
                if (!anno.insertable || !anno.updatable) {
                    continue
                }

                field.isAccessible = true
                val column = fieldToPair(entity, field)
                if (column.second != null) {
                    list.add(column)
                }
                field.isAccessible = false
            }
        }
        return list
    } // Function buildValues()

    /**
     * 构建values值
     *
     * @param   entity      实体对象
     * @param   projection  字段映射（未转换）
     * @return  values列表
     */
    protected fun buildUpdateValues(entity: ENTITY, projection: ArrayList<String>?): ArrayList<Pair<String, Any?>> {
        val clz = entity::class.java
        val fieldList = ArrayList<String>()
        try {
            // 转换属性名
            for (name in projection!!) {
                fieldList.add(propertyName(entity.javaClass, name.trim()))
            }
        } catch (e: Exception) {
            // DO NOTHING
        }

        val updateNull = fieldList.size > 0
        val list = ArrayList<Pair<String, Any?>>()
        for (field in clz.declaredFields) {
            if (field.isAnnotationPresent(Column::class.java)) {
                if (fieldList.size > 0 && !fieldList.contains(field.name)) {
                    continue
                }

                val anno = field.getAnnotation(Column::class.java) ?: continue
                // 如果更新状态为否，则跳过该字段
                if (!anno.updatable) {
                    continue
                }

                field.isAccessible = true
                val column = fieldToPair(entity, field)
                if (updateNull || column.second != null) {
                    list.add(column)
                }
                field.isAccessible = false
            }
        }
        return list
    } // Function buildValues()

    /**
     * 构建columns值
     *
     * @param   clazz       实体类类型
     * @param   fieldList   属性映射列表
     * @return  values列表
     */
    protected fun buildColumns(clazz: Class<*>, fieldList: ArrayList<String>?): String {
        val builder = StringBuilder()
        for (field in clazz.getAllDeclaredFields()) {
            if (fieldList != null && fieldList.size > 0 && !fieldList.contains(field.name)) {
                continue
            }
            val anna = field.getAnnotation(Column::class.java) ?: continue

            if (builder.isNotEmpty()) {
                builder.append(", ")
            }
            builder.append("`${anna.name}` AS `${field.name}`")
        }
        return builder.toString()
    } // Function buildColumns()

    /**
     * 获得实体类属性名
     *
     * @param   clazz   实体类类型
     * @param   name    名称
     * @return  实体类属性名
     */
    protected fun propertyName(clazz: Class<*>, name: String): String {
        try {
            val field = clazz.getAllDeclaredField(name, true, true)
            return field!!.name
        } catch (e: Exception) {
            throw SFieldNotFoundException(name)
        }
    } // Function propertyName()

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 级联操作相关
    /**
     * 查询级联关系
     *
     * @param   table       表名
     * @param   id1         对象表ID字段名
     * @param   id2         级联对象ID字段名
     * @param   self        自身级联
     * @param   idList      对象ID列表
     * @return  关系列表
     */
//    override fun relationQuery(table: String, id1: String, id2: String, self: Boolean, idList: ArrayList<Any>): ArrayList<HashMap<String, Any>> {
//        return ArrayList<HashMap<String, Any>>();
//    } // Function cascadeIdList()
} // Class SAndroidDao
