package redis

import (
	"fmt"
	"strconv"
	"time"

	"github.com/garyburd/redigo/redis"
)

var (
	m_host string
	m_pool *redis.Pool
)

func InitRedis(host string) error {
	m_host = host
	m_pool = &redis.Pool{
		MaxIdle:     64,
		IdleTimeout: 60 * time.Second,
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")

			return err
		},
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", host)
			if err != nil {
				return nil, err
			}

			//_, err = c.Do("SELECT", config.RedisDb)

			return c, err
		},
	}

	_, err := redis.Dial("tcp", host)
	if err != nil {
		return err
	}
	return nil
}

// 写入值永不过期
func SetString(key, value string) error {
	conn := m_pool.Get()
	defer conn.Close()
	_, err := conn.Do("SET", key, value)
	if err != nil {
		fmt.Println("redis set failed:", err)
	}
	return err
}

func SetExString(key, value string, expireTime int /* seconds*/) error {
	conn := m_pool.Get()
	defer conn.Close()
	expStr := strconv.Itoa(expireTime)
	_, err := conn.Do("SETEX", key, expStr, value)
	if err != nil {
		//fmt.Println("redis set failed:", err)
	}
	return err
}

func Delete(key string) error {
	conn := m_pool.Get()
	defer conn.Close()
	_, err := conn.Do("DEL", key)
	if err != nil {
		//fmt.Println("redis set failed:", err)
	}
	return err
}

func Expire(key string, expireTime int /* seconds*/) error {
	conn := m_pool.Get()
	defer conn.Close()
	expStr := strconv.Itoa(expireTime)
	_, err := conn.Do("EXPIRE", key, expStr)
	if err != nil {
		//fmt.Println("redis set failed:", err)
	}
	return err
}

func GetString(key string) (string, error) {
	conn := m_pool.Get()
	defer conn.Close()
	value, err := redis.String(conn.Do("GET", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func GetInt64(key string) (int64, error) {
	conn := m_pool.Get()
	defer conn.Close()
	value, err := redis.Int64(conn.Do("GET", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func GetUint64(key string) (uint64, error) {
	conn := m_pool.Get()
	defer conn.Close()
	value, err := redis.Uint64(conn.Do("GET", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func GetInt(key string) (int, error) {
	conn := m_pool.Get()
	defer conn.Close()
	value, err := redis.Int(conn.Do("GET", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func GetBool(key string) (bool, error) {
	conn := m_pool.Get()
	defer conn.Close()
	value, err := redis.Bool(conn.Do("GET", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func Exists(key string) (bool, error) {
	conn := m_pool.Get()
	defer conn.Close()
	value, err := redis.Bool(conn.Do("EXISTS", key))
	if err != nil {
		//fmt.Println("redis get failed:", err)
	}
	return value, err
}

func HMSet(key string, data interface{}) error {
	conn := m_pool.Get()
	defer conn.Close()
	if _, err := conn.Do("HMSET", redis.Args{}.Add(key).AddFlat(data)...); err != nil {
		//fmt.Println("redis set failed:", err)
		return err
	}

	return nil
}

func HSet(key, field, value string) error {
	conn := m_pool.Get()
	defer conn.Close()
	if _, err := conn.Do("HSET", key, field, value); err != nil {
		//fmt.Println("redis set failed:", err)
		return err
	}

	return nil
}

func HMGet(key string, data interface{}) error {
	conn := m_pool.Get()
	defer conn.Close()
	v, err := redis.Values(conn.Do("HGETALL", key))
	if err != nil {
		//fmt.Println("redis set failed:", err)
		return err
	}
	if err := redis.ScanStruct(v, data); err != nil {
		return err
	}
	return nil
}

func LPop(listName string) (string, error) {
	conn := m_pool.Get()
	defer conn.Close()
	value, err := redis.String(conn.Do("LPOP", listName))
	if err != nil {
		//fmt.Println("LPop redis get failed:", err)
	}
	return value, err
}

func RPush(listName string, element string) error {
	conn := m_pool.Get()
	defer conn.Close()
	_, err := redis.Int(conn.Do("RPUSH", listName, element))
	if err != nil {
		//fmt.Println("RPush redis get failed:", err)
	}
	return err
}

func LLen(listName string) (int, error) {
	conn := m_pool.Get()
	defer conn.Close()
	value, err := redis.Int(conn.Do("LLEN", listName))
	if err != nil {
		//fmt.Println("LLEN redis get failed:", err)
	}
	return value, err
}

func LIndex(listName string, index int) (string, error) {
	conn := m_pool.Get()
	defer conn.Close()
	idxStr := strconv.Itoa(index)
	value, err := redis.String(conn.Do("LINDEX", listName, idxStr))
	if err != nil {
		//fmt.Println("LIndex redis get failed:", err)
	}
	return value, err
}

func Incr(key string) (uint64, error) {
	conn := m_pool.Get()
	defer conn.Close()
	value, err := redis.Uint64(conn.Do("INCR", key))
	if err != nil {
		//fmt.Println("RPush redis get failed:", err)
	}
	return value, err
}

func IncrBy(key string, incr int) (uint64, error) {
	conn := m_pool.Get()
	defer conn.Close()
	value, err := redis.Uint64(conn.Do("INCRBY", key, incr))
	if err != nil {
		fmt.Println("redis get failed:", err)
	}
	return value, err
}

func reConnect() {
	//1. first close old connection

	//2. create a new connection
	for {
		_, err := redis.Dial("tcp", m_host)
		if err == nil {
			return
		}
		time.Sleep(time.Second)
		//logger.DEBUG("reConnecting\n")
	}
}

func TestRedis() {
	/*id := "1001"
	SetString(id, "room 1001")
	id = "1002"
	SetString(id, "room 1002")
	id = "1001"
	v, _ := GetString(id)
	glog.Info("id=", id, ", value:", v)

	id = "1002"
	v, _ = GetString(id)
	glog.Info("id=", id, ", value:", v)
	*/
}
