package cache

import (
	. "code.hoteas.com/golang/hotime/common"
	"database/sql"
	"encoding/json"
	"strings"
	"time"
)

type HoTimeDBInterface interface {
	GetPrefix() string
	Query(query string, args ...interface{}) []Map
	Exec(query string, args ...interface{}) (sql.Result, *Error)
	Get(table string, qu ...interface{}) Map
	Select(table string, qu ...interface{}) []Map
	Delete(table string, data map[string]interface{}) int64
	Update(table string, data Map, where Map) int64
	Insert(table string, data map[string]interface{}) int64
	GetType() string
}

type CacheDb struct {
	TimeOut    int64
	DbSet      bool
	SessionSet bool
	Db         HoTimeDBInterface
	*Error
	ContextBase
	isInit bool
}

func (that *CacheDb) GetError() *Error {

	return that.Error

}

func (that *CacheDb) SetError(err *Error) {
	that.Error = err
}

func (that *CacheDb) initDbTable() {
	if that.isInit {
		return
	}
	if that.Db.GetType() == "mysql" {

		dbNames := that.Db.Query("SELECT DATABASE()")

		if len(dbNames) == 0 {
			return
		}
		dbName := dbNames[0].GetString("DATABASE()")
		res := that.Db.Query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='" + dbName + "' AND TABLE_NAME='" + that.Db.GetPrefix() + "cached'")
		if len(res) != 0 {
			that.isInit = true
			return
		}

		_, e := that.Db.Exec("CREATE TABLE `" + that.Db.GetPrefix() + "cached` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `key` varchar(60) DEFAULT NULL, `value` varchar(2000) DEFAULT NULL, `time` bigint(20) DEFAULT NULL, `endtime` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=198740 DEFAULT CHARSET=utf8")
		if e.GetError() == nil {
			that.isInit = true
		}

	}

	if that.Db.GetType() == "sqlite" {
		res := that.Db.Query(`select * from sqlite_master where type = 'table' and name = '` + that.Db.GetPrefix() + `cached'`)

		if len(res) != 0 {
			that.isInit = true
			return
		}
		_, e := that.Db.Exec(`CREATE TABLE "` + that.Db.GetPrefix() + `cached" (
			"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
				"key" TEXT(60),
				"value" TEXT(2000),
				"time" integer,
				"endtime" integer
			);`)
		if e.GetError() == nil {
			that.isInit = true
		}

	}

}

//获取Cache键只能为string类型
func (that *CacheDb) get(key string) interface{} {

	cached := that.Db.Get("cached", "*", Map{"key": key})

	if cached == nil {
		return nil
	}
	//data:=cacheMap[key];
	if cached.GetInt64("endtime") <= time.Now().Unix() {

		that.Db.Delete("cached", Map{"id": cached.GetString("id")})
		return nil
	}

	data := Map{}
	data.JsonToMap(cached.GetString("value"))

	return data.Get("data")
}

//key value ,时间为时间戳
func (that *CacheDb) set(key string, value interface{}, tim int64) {

	bte, _ := json.Marshal(Map{"data": value})

	num := that.Db.Update("cached", Map{"value": string(bte), "time": time.Now().UnixNano(), "endtime": tim}, Map{"key": key})
	if num == int64(0) {
		that.Db.Insert("cached", Map{"value": string(bte), "time": time.Now().UnixNano(), "endtime": tim, "key": key})
	}

	//随机执行删除命令
	if Rand(1000) > 950 {
		that.Db.Delete("cached", Map{"endtime[<]": time.Now().Unix()})
	}
}

func (that *CacheDb) delete(key string) {

	del := strings.Index(key, "*")
	//如果通配删除
	if del != -1 {
		key = Substr(key, 0, del)
		that.Db.Delete("cached", Map{"key": key + "%"})

	} else {
		that.Db.Delete("cached", Map{"key": key})
	}
}

func (that *CacheDb) Cache(key string, data ...interface{}) *Obj {

	that.initDbTable()

	if len(data) == 0 {
		return &Obj{Data: that.get(key)}
	}
	tim := time.Now().Unix()

	if len(data) == 1 && data[0] == nil {
		that.delete(key)
		return &Obj{Data: nil}
	}

	if len(data) == 1 {
		if that.TimeOut == 0 {
			//that.Time = Config.GetInt64("cacheLongTime")
		}
		tim += that.TimeOut
	}
	if len(data) == 2 {
		that.SetError(nil)
		tempt := ObjToInt64(data[1], that.Error)

		if tempt > tim {
			tim = tempt
		} else if that.GetError() == nil {

			tim = tim + tempt
		}
	}
	that.set(key, data[0], tim)
	return &Obj{Data: nil}
}
