package rimg

import (
	"bytes"
	"image"
	_ "image/gif"
	"image/jpeg"
	_ "image/jpeg"
	_ "image/png"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"strconv"

	"github.com/nfnt/resize"

	"git.trj.tw/golang/mtfosbot/module/config"
	"git.trj.tw/golang/mtfosbot/module/context"
	"git.trj.tw/golang/mtfosbot/module/utils"
)

func resizeImage(fio io.Reader, size int, wgth bool) (img image.Image, err error) {
	img, _, err = image.Decode(fio)
	w := 0
	h := 0
	if wgth {
		w = size
	} else {
		h = size
	}

	img = resize.Resize(uint(w), uint(h), img, resize.Lanczos3)
	return
}

// GetOriginImage -
func GetOriginImage(c *context.Context) {
	// max 1024
	fname := c.Param("imgname")
	conf := config.GetConf()
	imgP := utils.ParsePath(conf.ImageRoot)
	exists := utils.CheckExists(imgP, true)
	if !exists {
		c.NotFound("image path not found")
		return
	}

	subd := c.DefaultQuery("d", "")
	if len(subd) > 0 {
		imgP = path.Join(imgP, subd)
		exists = utils.CheckExists(imgP, true)
		if !exists {
			c.NotFound("image path not found")
			return
		}
	}

	newP := path.Join(imgP, fname)
	exists = utils.CheckExists(newP, false)
	if !exists {
		c.NotFound("image file not found")
		return
	}

	fileBuf, err := os.Open(newP)
	if err != nil {
		c.ServerError(nil)
		return
	}
	defer fileBuf.Close()

	imgf, format, err := image.DecodeConfig(fileBuf)
	if err != nil {
		c.ServerError(nil)
		return
	}

	// reset pointer to 0
	_, err = fileBuf.Seek(0, 0)
	if err != nil {
		c.ServerError(nil)
		return
	}

	if imgf.Height > 1024 || imgf.Width > 1024 {
		img, err := resizeImage(fileBuf, 1024, imgf.Width > imgf.Height)

		buf := new(bytes.Buffer)

		err = jpeg.Encode(buf, img, nil)
		if err != nil {
			c.ServerError(nil)
			return
		}
		c.Writer.Header().Set("Content-Type", "image/jpeg")
		c.Writer.Header().Set("Content-Length", strconv.Itoa(buf.Len()))
		_, err = c.Writer.Write(buf.Bytes())
		if err != nil {
			c.ServerError(nil)
		}
		return
	}

	// check type
	if format != "jpeg" {
		img, _, err := image.Decode(fileBuf)
		if err != nil {
			c.ServerError(nil)
			return
		}
		buf := new(bytes.Buffer)
		err = jpeg.Encode(buf, img, nil)
		if err != nil {
			c.ServerError(nil)
			return
		}

		c.Writer.Header().Set("Content-Type", "image/jpeg")
		io.Copy(c.Writer, buf)
		return
	}

	c.Writer.Header().Set("Content-Type", "image/jpeg")
	io.Copy(c.Writer, fileBuf)
}

// GetThumbnailImage -
func GetThumbnailImage(c *context.Context) {
	// max 240
	fname := c.Param("imgname")
	conf := config.GetConf()
	imgP := utils.ParsePath(conf.ImageRoot)
	exists := utils.CheckExists(imgP, true)
	if !exists {
		c.NotFound("image path not found")
		return
	}

	thumbDir := "thumbnail"
	subd := c.DefaultQuery("d", "")
	if exists := utils.CheckExists(path.Join(imgP, thumbDir, subd), true); !exists {
		if err := os.MkdirAll(path.Join(imgP, thumbDir, subd), 0775); err != nil {
			c.ServerError(nil)
			return
		}
	}

	thumbP := path.Join(imgP, thumbDir, subd, fname)
	genNew := false
	if !utils.CheckExists(thumbP, false) {
		genNew = true
		thumbP = path.Join(imgP, subd, fname)
		exists = utils.CheckExists(thumbP, false)
		if !exists {
			c.NotFound("image file not found")
			return
		}
	}

	filebuf, err := os.Open(thumbP)
	if err != nil {
		c.ServerError(nil)
		return
	}
	defer filebuf.Close()

	imgconf, format, err := image.DecodeConfig(filebuf)
	if err != nil {
		c.ServerError(nil)
		return
	}

	// reset file reader ptr
	_, err = filebuf.Seek(0, 0)
	if err != nil {
		c.ServerError(nil)
		return
	}

	if imgconf.Height > 240 || imgconf.Width > 240 {
		img, err := resizeImage(filebuf, 240, imgconf.Width > imgconf.Height)
		if err != nil {
			c.ServerError(nil)
			return
		}

		buf := new(bytes.Buffer)
		err = jpeg.Encode(buf, img, nil)
		if err != nil {
			c.ServerError(nil)
			return
		}
		breader := bytes.NewReader(buf.Bytes())

		if genNew {
			savep := path.Join(conf.ImageRoot, "thumbnail", subd, fname)
			err := saveNewThumbnail(breader, savep)
			if err != nil {
				c.ServerError(nil)
				return
			}
			_, err = breader.Seek(0, 0)
			if err != nil {
				c.ServerError(nil)
				return
			}
		}

		c.Writer.Header().Set("Content-Type", "image/jpeg")
		io.Copy(c.Writer, breader)
		return
	}

	if format != "jpeg" {
		img, _, err := image.Decode(filebuf)
		if err != nil {
			c.ServerError(nil)
			return
		}
		buf := new(bytes.Buffer)
		err = jpeg.Encode(buf, img, nil)
		if err != nil {
			c.ServerError(nil)
			return
		}

		breader := bytes.NewReader(buf.Bytes())

		savep := path.Join(conf.ImageRoot, "thumbnail", fname)
		err = saveNewThumbnail(breader, savep)
		if err != nil {
			c.ServerError(nil)
			return
		}

		_, err = breader.Seek(0, 0)
		if err != nil {
			c.ServerError(nil)
			return
		}

		c.Writer.Header().Set("Content-Type", "image/jpeg")
		io.Copy(c.Writer, breader)
		return
	}

	if genNew {
		savep := path.Join(conf.ImageRoot, "thumbnail", fname)
		err := saveNewThumbnail(filebuf, savep)
		if err != nil {
			c.ServerError(nil)
			return
		}
		_, err = filebuf.Seek(0, 0)
		if err != nil {
			c.ServerError(nil)
			return
		}
	}

	c.Writer.Header().Set("Content-Type", "image/jpeg")
	io.Copy(c.Writer, filebuf)
}

func saveNewThumbnail(fio io.Reader, newp string) (err error) {
	out, err := os.Create(newp)
	if err != nil {
		return err
	}
	_, err = io.Copy(out, fio)
	if err != nil {
		return err
	}

	return
}

// GetLineLogImage -
func GetLineLogImage(c *context.Context) {
	conf := config.GetConf()
	fname := c.Param("imgname")

	if !utils.CheckExists(path.Join(conf.LogImageRoot, fname), false) {
		c.NotFound("image not found")
		return
	}

	f, err := os.Open(path.Join(conf.LogImageRoot, fname))
	if err != nil {
		c.ServerError(nil)
		return
	}
	defer f.Close()

	r := io.LimitReader(f, 512)
	bytes, err := ioutil.ReadAll(r)
	if err != nil {
		c.ServerError(nil)
		return
	}
	_, err = f.Seek(0, 0)
	if err != nil {
		c.ServerError(nil)
		return
	}

	contentType := http.DetectContentType(bytes)

	c.Writer.Header().Set("Content-Type", contentType)
	io.Copy(c.Writer, f)
}
