package middleware

import (
	"bytes"
	"github.com/gogf/gf/net/ghttp"
	"github.com/nfnt/resize"
	"gxt-file-server/app/agent"
	"gxt-file-server/app/errors"
	"gxt-file-server/pkg/gplus"
	"gxt-file-server/pkg/logger"
	"image"
	"image/jpeg"
	"image/png"
	"net/url"
	"path"
	"strconv"
	"strings"
)

// FileMiddleWare 文件服务中间件
func FileMiddleWare(skippers ...SkipperFunc) ghttp.HandlerFunc {
	return func(r *ghttp.Request) {
		if len(skippers) > 0 && skippers[0](r) {
			r.Middleware.Next()
			return
		}
		ctx := gplus.NewContext(r)
		filePath, err := url.PathUnescape(r.Request.URL.Path)
		if err != nil {
			logger.Errorf(ctx, err.Error())
			gplus.ResError(r, err)
		}
		var (
			thumb string
			w     uint
			h     uint
		)
		suffix := path.Ext(filePath)
		contentType := getContentType(suffix)
		fileData, _, err := agent.DefaultAgent().Get(ctx, filePath)
		if err != nil {
			gplus.ResError(r, errors.ErrFileNotFound)
		}
		thumb = r.GetString("thumb")
		if thumb == "1" && (contentType == "image/png" || contentType == "image/jpeg") {
			w = r.GetUint("w")
			h = r.GetUint("h")
			if w > 0 || h > 0 {
				img, format, err := image.Decode(bytes.NewBuffer(fileData))
				if err != nil {
					return
				}

				if origWidth := uint(img.Bounds().Dx()); w > origWidth {
					w = origWidth
				}
				if origHeight := uint(img.Bounds().Dy()); h > origHeight {
					h = origHeight
				}

				img = resize.Resize(w, h, img, resize.Lanczos3)
				buf := new(bytes.Buffer)
				if format == "png" {
					png.Encode(buf, img)
				} else {
					jpeg.Encode(buf, img, nil)
				}

				if l := buf.Len(); l > 0 {
					fileData = buf.Bytes()
				}
			}
		}

		if strings.HasPrefix(contentType, "text/html") ||
			strings.HasPrefix(contentType, "application/javascript") {
			contentType = "text/plain; charset=utf-8"
		}
		rangeParam := r.Request.Header.Get("Range")
		if rangeParam != "" {
			byteRanges := strings.Split(rangeParam, "=")
			if len(byteRanges) == 2 {
				ranges := strings.Split(byteRanges[1], "-")
				if len(ranges) == 2 {
					start, _ := strconv.Atoi(ranges[0])
					end, _ := strconv.Atoi(ranges[1])
					length := end - start + 1
					if start >= 0 || end > start {
						fileData = fileData[start:(start + length)]
					}
				}
			}
		}

		r.Response.Header().Set("Content-Type", contentType)
		r.Response.Header().Set("Cache-Control", "max-age=31536000")
		r.Response.Header().Set("Content-Length", strconv.FormatInt(int64(len(fileData)), 10))
		r.Response.Write(fileData)
	}
}

func getContentType(suffix string) string {
	fileContentType := make(map[string]string)
	fileContentType[".jpg"] = "image/jpeg"
	fileContentType[".jpeg"] = "image/jpeg"
	fileContentType[".gif"] = "image/gif"
	fileContentType[".png"] = "image/png"
	fileContentType[".mp4"] = "video/mp4"
	fileContentType[".mpg4"] = "video/mp4"
	fileContentType[".pdf"] = "application/pdf"
	fileContentType[".svg"] = "image/svg+xml"
	if r, ok := fileContentType[suffix]; ok {
		return r
	}
	return "application/octet-stream"
}
