package api

import (
	"crypto/md5"
	"encoding/hex"
	"errors"
	"fmt"
	"github.com/axetroy/go-fs"
	"github.com/gin-gonic/gin"
	"../../pkg/app"
	"../../pkg/e"
	"../../pkg/logging"
	"../../pkg/setting"
	"../../routers/restful"
	"github.com/nfnt/resize"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
)

// 支持的图片后缀名
var supportImageExtNames = []string{".jpg", ".jpeg", ".png", ".ico", ".svg", ".bmp", ".gif"}

/**
check a file is a image or not
*/
func isImage(extName string) bool {
	for i := 0; i < len(supportImageExtNames); i++ {
		if supportImageExtNames[i] == extName {
			return true
		}
	}
	return false
}

/**
Handler the parse error
*/
func parseFormFail(context *gin.Context) {
	context.JSON(http.StatusBadRequest, gin.H{
		"message": "Can not parse form",
	})
}


/**
Upload file handler
*/
func UploadFile(context *gin.Context) {
	appG := app.Gin{C: context}
	dictId := context.Param("id")
	dictName := context.Param("name")
	logging.Info("UploadFile ",context.Keys,dictId,dictName)
	var (
		isSupportFile bool
		maxUploadSize = setting.AppSetting.FileMaxSize  // 最大上传大小
		allowTypes    = setting.AppSetting.FileAllowType // 可上传的文件类型
		distPath      string                  // 最终的输出目录
		err           error
		file          *multipart.FileHeader
		src           multipart.File
		dist          *os.File
	)
	// Source
	if file, err = context.FormFile("file"); err != nil {
		parseFormFail(context)
		return
	}
	sqlname := context.GetHeader("optname")
	extname := path.Ext(file.Filename)
	if len(allowTypes) != 0 {
		for i := 0; i < len(allowTypes); i++ {
			if allowTypes[i] == extname {
				isSupportFile = true
				break
			}
		}

		if isSupportFile == false {
			context.JSON(http.StatusBadRequest, gin.H{
				"message": "不支持的文件类型: " + extname,
			})
			return
		}
	}

	if file.Size > int64(maxUploadSize) {
		context.JSON(http.StatusBadRequest, gin.H{
			"message": "上传文件太大, 最大限制为(byte): " + strconv.Itoa(int(maxUploadSize)),
		})
		return
	}

	if src, err = file.Open(); err != nil {
		// open the file fail...
	}
	defer src.Close()

	hash := md5.New()

	io.Copy(hash, src)

	md5string := hex.EncodeToString(hash.Sum([]byte("")))

	fileName := md5string + extname
	// Destination

	filePath := setting.CurrentPath+setting.AppSetting.FileSavePath+dictName+"/"
	err = PathCheck(filePath)  //检查路径并创建
	if err != nil {
		fmt.Println("PathCheck err",err)
	}
	distPath = path.Join(filePath, fileName)
	if dist, err = os.Create(distPath); err != nil {
		fmt.Println("Create err",err)
	}
	defer dist.Close()
	//distPath = setting.CurrentPath + setting.AppSetting.FileSavePath +"/"+ fileName

	if dist, err = os.Create(distPath); err != nil {
		// create dist file fail...
	}
	defer dist.Close()

	// FIXME: open 2 times
	if src, err = file.Open(); err != nil {
		//
	}

	// Copy
	io.Copy(dist, src)
	var execresult interface{}

	params := context.Request.Form

	if sqlname != "" {
		sql, p := restful.GetSqlByNameDB(sqlname)//todo insertcustomdoc
		if sql != ""{
			s_params := make([]interface{}, 0)
			paramslist := strings.Split(p,",")

			if len(paramslist)>0 && p!="" {
				for _, value := range paramslist {
					fmt.Println("s_params value",s_params,value)
					if (strings.ToLower(strings.Trim(value," "))=="username"){//picpath, picname, username, newpicname
						tempv := params.Get("jwt_username")
						s_params = append(s_params, tempv)
					}else if (strings.ToLower(strings.Trim(value," "))=="docname"){
						s_params = append(s_params, file.Filename)
					}else if (strings.ToLower(strings.Trim(value," "))=="newdocname"){
						s_params = append(s_params, fileName)
					}else if (strings.ToLower(strings.Trim(value," "))=="docpath"){
						s_params = append(s_params, dictName) //
					}else if (strings.ToLower(strings.Trim(value," "))=="dictid"){
						s_params = append(s_params,dictId) //
						fmt.Println("s_params",s_params,dictId)
					}else if (strings.ToLower(strings.Trim(value," "))=="filesize"){
						s_params = append(s_params, file.Size) //
					}else{
						s_params = append(s_params, params.Get(strings.Trim(value," ")))
					}
				}
			}
			execresult, err = execDataBySql(sql, s_params)
			if err != nil{
				fmt.Println("execDataBySql err",err)
				appG.Response(http.StatusOK, e.ERROR, err.Error())
			}
		}
	}
	context.JSON(http.StatusOK, gin.H{
		"hash":     md5string,
		"filename": fileName,
		"origin":   file.Filename,
		"size":     file.Size,
		"execresult":     execresult,
	})

}

/**
Upload image handler
*/
func UploaderImage(context *gin.Context) {
	logging.Info("UploaderImage ",context.Keys)
	var (
		maxUploadSize = setting.AppSetting.ImageMaxSize // 最大上传大小
		distPath      string                 // 最终的输出目录
		err           error
		file          *multipart.FileHeader
		src           multipart.File
		dist          *os.File
	)
	// Source
	if file, err = context.FormFile("file"); err != nil {
		parseFormFail(context)
		return
	}

	sqlname := context.GetHeader("optname")

	extname := strings.ToLower(path.Ext(file.Filename))

	if isImage(extname) == false {
		context.JSON(http.StatusBadRequest, gin.H{
			"message": "不支持的上传文件类型: " + extname,
		})
		return
	}
	if file.Size > int64(maxUploadSize) {
		context.JSON(http.StatusBadRequest, gin.H{
			"message": "上传文件太大, 最大文件大小限制为(byte): " + strconv.Itoa(int(maxUploadSize)),
		})
		return
	}

	if src, err = file.Open(); err != nil {

	}
	defer src.Close()

	hash := md5.New()

	io.Copy(hash, src)

	md5string := hex.EncodeToString(hash.Sum([]byte("")))

	fileName := md5string + extname


	//savePathDir := setting.CurrentPath+setting.AppSetting.ImageSavePath+fileName+"/"
	//_,err = os.Stat(savePathDir)
	//if err == nil {
	//	fmt.Println(err)//todo 错误处理
	//}
	//if os.IsNotExist(err) {
	//	err:=os.Mkdir(setting.CurrentPath+setting.AppSetting.ImageSavePath+fileName,os.ModePerm)
	//	if err!=nil{
	//		fmt.Println(err)
	//	}//目录不存在则创建
	//}
	// Destination
	picPath := setting.CurrentPath+setting.AppSetting.ImageSavePath+sqlname+"/"
	err = PathCheck(picPath)  //检查路径并创建
	if err != nil {
		fmt.Println("PathCheck err",err)
	}
	distPath = path.Join(picPath, fileName)
	if dist, err = os.Create(distPath); err != nil {
		fmt.Println("Create err",err)
	}
	defer dist.Close()

	// FIXME: open 2 times
	if src, err = file.Open(); err != nil {
		//
	}
	defer src.Close()

	// Copy
	io.Copy(dist, src)

	// 压缩缩略图
	// 不管成功与否,都会进行下一步的返回
	if _, err := thumbnailify(distPath,sqlname); err != nil {
		logging.Error("thumbnailify_err",err)
		picThumbnailPath := setting.CurrentPath+setting.AppSetting.ThumbnailSavePath+sqlname+"/"
		println(picThumbnailPath)
		err = PathCheck(picThumbnailPath)  //检查路径并创建
		distThumbnailPath := path.Join(picThumbnailPath, fileName)
		println(distThumbnailPath)
		distThumbnail, err := os.Create(distThumbnailPath)
		if  err != nil {
			fmt.Println("CreateThumbnail err",err)
		}
		srcThumbnail, err := file.Open()
		if  err != nil {
			//
		}
		io.Copy(distThumbnail, srcThumbnail)
		distThumbnail.Close()
		src.Close()
	}
	var execresult interface{}

	params := context.Request.Form

	if sqlname != "" {
		sql, p := restful.GetSqlByNameDB(sqlname)
		if sql != ""{
			s_params := make([]interface{}, 0)
			paramslist := strings.Split(p,",")
			if len(paramslist)>0 && p!="" {
				for _, value := range paramslist {
					if (strings.ToLower(strings.Trim(value," "))=="username"){//picpath, picname, username, newpicname
						tempv := params.Get("jwt_username")
						s_params = append(s_params, tempv)
					}else if (strings.ToLower(strings.Trim(value," "))=="picname"){
						s_params = append(s_params, file.Filename)
					}else if (strings.ToLower(strings.Trim(value," "))=="newpicname"){
						s_params = append(s_params, fileName)
					}else if (strings.ToLower(strings.Trim(value," "))=="picpath"){
						s_params = append(s_params, sqlname) //全路径加文件名
					} else{
						s_params = append(s_params, params.Get(strings.Trim(value," ")))
					}
				}
			}
			execresult, err = execDataBySql(sql, s_params)
			if err != nil{
				fmt.Println("execDataBySql err",err)
			}
		}
	}
	context.JSON(http.StatusOK, gin.H{
		"hash":     md5string,
		"filename": fileName,
		"origin":   file.Filename,
		"size":     file.Size,
		"execresult":     execresult,
	})
}


/**
Get file raw
*/
func GetFileRaw(context *gin.Context) {
	filename := context.Param("filename")
	logging.Info("GetFileRaw ",context.Keys,filename)

	filePath := path.Join(setting.CurrentPath, setting.AppSetting.FileSavePath, filename)
	if isExistFile := fs.PathExists(filePath); isExistFile == false {
		// if the path not found
		http.NotFound(context.Writer, context.Request)
		return
	}
	http.ServeFile(context.Writer, context.Request, filePath)
}

/**
Download a file
*/
func DownloadFile(context *gin.Context) {
	filename := context.Param("filename")
	logging.Info("DownloadFile ",context.Keys,filename)
	eqpic, _ := restful.Engine.SQL("SELECT * FROM eq_doc where id = ? ", filename).QueryString()
	if eqpic == nil {
		http.NotFound(context.Writer, context.Request)
		return
	}
	originFilePath := path.Join(setting.CurrentPath, setting.AppSetting.FileSavePath,eqpic[0]["docpath"],eqpic[0]["newdocname"])
	if fs.PathExists(originFilePath) == false {
		// if the path not found
		http.NotFound(context.Writer, context.Request)
		return
	}
	http.ServeFile(context.Writer, context.Request, originFilePath)

}


/**
Get Origin image
*/
func GetOriginImage(context *gin.Context) {
	//appG := app.Gin{C: context}
	filename := context.Param("filename")
	logging.Info("GetOriginImage ",context.Keys,filename)
	eqpic, _ := restful.Engine.SQL("SELECT * FROM eq_pic where id = ? ", filename).QueryString()
	if eqpic == nil {
		http.NotFound(context.Writer, context.Request)
		return
	}
	originImagePath := path.Join(setting.CurrentPath, setting.AppSetting.ImageSavePath,eqpic[0]["picpath"],eqpic[0]["newpicname"])
	if fs.PathExists(originImagePath) == false {
		// if the path not found
		http.NotFound(context.Writer, context.Request)
		return
	}
	http.ServeFile(context.Writer, context.Request, originImagePath)
	//appG.Response(http.StatusOK, e.SUCCESS, eqpic[0]["picname"])
}

/**
Get thumbnail image
*/
func GetThumbnailImage(context *gin.Context) {
	filename := context.Param("filename")
	logging.Info("GetThumbnailImage ",context.Keys,filename)
	eqpic, _ := restful.Engine.SQL("SELECT * FROM eq_pic where id = ? ", filename).QueryString()
	if eqpic == nil {
		http.NotFound(context.Writer, context.Request)
		return
	}
	thumbnailImagePath := path.Join(setting.CurrentPath, setting.AppSetting.ThumbnailSavePath,eqpic[0]["picpath"],eqpic[0]["newpicname"])
	originImagePath := path.Join(setting.CurrentPath, setting.AppSetting.ImageSavePath, filename)

	if fs.PathExists(thumbnailImagePath) == false {
		// if thumbnail image not exist, try to get origin image
		if fs.PathExists(originImagePath) == true {
			http.ServeFile(context.Writer, context.Request, originImagePath)
			return
		}
		// if the path not found
		http.NotFound(context.Writer, context.Request)
		return
	}
	http.ServeFile(context.Writer, context.Request, thumbnailImagePath)
}

/**
Generate thumbnail
*/
func thumbnailify(imagePath,subdirectory string) (outputPath string, err error) {
	var (
		file     *os.File
		img      image.Image
	)
	Filename := strings.ToLower(path.Base(imagePath))
	extname := strings.ToLower(path.Ext(imagePath))
	outputPath = setting.CurrentPath + setting.AppSetting.ThumbnailSavePath +subdirectory+ "/" +  Filename
	err = PathCheck(setting.CurrentPath + setting.AppSetting.ThumbnailSavePath +subdirectory)
	if err !=nil{
		fmt.Println("thumbnailify PathCheck err",err)
	}
	// 读取文件
	if file, err = os.Open(imagePath); err != nil {
		return
	}

	defer file.Close()

	// decode jpeg into image.Image
	switch extname {
	case ".jpg", ".jpeg":
		img, err = jpeg.Decode(file)
		break
	case ".png":
		img, err = png.Decode(file)
		break
	case ".gif":
		img, err = gif.Decode(file)
		break
	default:
		err = errors.New("Unsupport file type" + extname)
		return
	}

	if img == nil {
		err = errors.New("Generate thumbnail fail...")
		return
	}

	m := resize.Thumbnail(uint( setting.AppSetting.ThumbnailMaxWidth), uint(setting.AppSetting.ThumbnailMaxHeight), img, resize.Lanczos3)

	out, err := os.Create(outputPath)
	if err != nil {
		return
	}
	defer out.Close()

	// write new image to file

	//decode jpeg/png/gif into image.Image
	switch extname {
	case ".jpg", ".jpeg":
		jpeg.Encode(out, m, nil)
		break
	case ".png":
		png.Encode(out, m)
		break
	case ".gif":
		gif.Encode(out, m, nil)
		break
	default:
		err = errors.New("Unsupport file type" + extname)
		return
	}

	return
}

func UploadFiles(c *gin.Context) {
	logging.Info("UploadFiles ",c.Keys)
	appG := app.Gin{C: c}
	err := c.Request.ParseMultipartForm(200000)
	if err != nil {
		appG.Response(http.StatusOK, e.ERROR_IMPORT_FAIL, err)
		return
	}
	// 获取表单
	form := c.Request.MultipartForm
	// 获取参数upload后面的多个文件名,存放到数组files里面,
	files := form.File["upload"]
	// 遍历数组,每取出一个file就拷贝一次
	for i, _ := range files {
		file, err := files[i].Open()
		defer file.Close()
		if err != nil {
			appG.Response(http.StatusOK, e.ERROR_IMPORT_FAIL, err)
			return
		}

		fileName := files[i].Filename
		fmt.Println(fileName)

		out, err := os.Create(fileName)
		defer out.Close()
		if err != nil {
			appG.Response(http.StatusOK, e.ERROR_IMPORT_FAIL, err)
			return
		}

		_, err = io.Copy(out, file)
		if err != nil {
			appG.Response(http.StatusOK, e.ERROR_IMPORT_FAIL, err)
			return
		}

		appG.Response(http.StatusOK, e.SUCCESS, "upload successful \n")
	}
}
func PathCheck(path string)(err error){
	b,err :=PathExists(path)
	if  err != nil {
		fmt.Println("exist err",err)
	}
	if !b{
		fmt.Println(path,"  目录不存在,重新创建")
		err = os.Mkdir(path, 0777)
		if  err != nil {
			fmt.Println("Mkdir err",err)
		}
	}
	return
}
func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}