package http

import (
	"encoding/json"
	"net"
	"net/http"
	"os"
	"path"
	"strings"
)

var (
	binder = &DefaultBinder{}
)

type Context struct {
	request  *http.Request
	response http.ResponseWriter
	params   map[string]string
}

func (c *Context) Reset(r *http.Request, w http.ResponseWriter, params map[string]string) {
	c.request = r
	c.response = w
	c.params = params
}

func (c *Context) Request() *http.Request {
	return c.request
}

func (c *Context) Response() http.ResponseWriter {
	return c.response
}

func (c *Context) FormValue(name string) string {
	return c.request.FormValue(name)
}

func (c *Context) Bind(i interface{}) (err error) {
	return binder.Bind(i, c.Request())
}

func (c *Context) Success(val interface{}) (err error) {
	return c.JSON(&Response{Result: val})
}

func (c *Context) Redirect(url string, code int) {
	http.Redirect(c.Response(), c.Request(), url, code)
}

func (c *Context) SetCookie(cookie *http.Cookie) {
	http.SetCookie(c.Response(), cookie)
}

func (c *Context) GetRealIp() string {
	if ip := c.Request().Header.Get("X-Forwarded-For"); ip != "" {
		i := strings.IndexAny(ip, ",")
		if i > 0 {
			return strings.TrimSpace(ip[:i])
		}
		return ip
	}
	if ip := c.Request().Header.Get("X-Real-IP"); ip != "" {
		return ip
	}
	ra, _, _ := net.SplitHostPort(c.Request().RemoteAddr)
	return ra
}

func (c *Context) Error(code int, message string) (err error) {
	return c.JSON(&Response{Code: code, Message: message})
}

func (c *Context) JSON(v interface{}) (err error) {
	c.response.Header().Set("Content-Type", "application/json")
	enc := json.NewEncoder(c.response)
	if strings.Contains(c.request.Header.Get("User-Agent"), "curl") {
		enc.SetIndent("", "\t")
	}
	if err = enc.Encode(v); err != nil {
		c.response.WriteHeader(http.StatusBadGateway)
	}
	return
}

func (c *Context) SendFile(filename string) (err error) {
	var (
		fi os.FileInfo
		fp *os.File
	)
	if fi, err = os.Stat(filename); err == nil {
		if fp, err = os.Open(filename); err == nil {
			http.ServeContent(c.Response(), c.Request(), path.Base(filename), fi.ModTime(), fp)
			err = fp.Close()
		}
	}
	return
}

func (c *Context) SetParam(name string, value string) {
	if c.params == nil {
		c.params = make(map[string]string)
	}
	c.params[name] = value
}

func (c *Context) ParamValue(name string) string {
	if c.params != nil {
		return c.params[name]
	}
	return ""
}
