/*
 * Copyright (C) 2014 ~ 2018 Deepin Technology Co., Ltd.
 *
 * Author:     jouyouyun <jouyouwen717@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package graphic

import (
	"testing"

	C "gopkg.in/check.v1"
)

const (
	originImg               = "testdata/origin_1920x1080.jpg"
	originImgWidth          = 1920
	originImgHeight         = 1080
	originImgDominantColorH = 198.6
	originImgDominantColorS = 0.40
	originImgDominantColorV = 0.43

	originImgPngSmall       = "testdata/origin_small_200x200.png"
	originImgPngIcon1       = "testdata/origin_icon_1_48x48.png"
	originImgPngIcon2       = "testdata/origin_icon_2_48x48.png"
	originIconWidth         = 48
	originIconHeight        = 48

	originImgNotImage = "testdata/origin_not_image"
)

// data uri for originImgPngIcon2
const testDataUri = `data:image/png;base64, `

// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) { C.TestingT(t) }

type testWrapper struct{}

var _ = C.Suite(&testWrapper{})

func delta(x, y float64) float64 {
	if x >= y {
		return x - y
	}
	return y - x
}

func (g *testWrapper) TestLoadImage(c *C.C) {
	_, err := LoadImage(originImg)
	if err != nil {
		c.Error(err)
	}
}

func (g *testWrapper) TestCompositeImage(c *C.C) {
	resultFile := "testdata/test_compositeimage.png"
	err := CompositeImage(originImgPngSmall, originImgPngIcon1, resultFile, 0, 0, FormatPng)
	if err != nil {
		c.Error(err)
	}
	err = CompositeImage(resultFile, originImgPngIcon2, resultFile, 24, 24, FormatPng)
	if err != nil {
		c.Error(err)
	}
}

func (g *testWrapper) TestCompositeImageSet(c *C.C) {
	resultFile := "testdata/test_compositeimageset.png"
	compInfos := []CompositeInfo{
		{originImgPngIcon1, 0, 0, 10},
		{originImgPngIcon2, 24, 24, 0},
	}
	err := CompositeImageSet(originImgPngSmall, compInfos, resultFile, FormatPng)
	if err != nil {
		c.Error(err)
	}
}

func (g *testWrapper) TestCompositeImageUri(c *C.C) {
	resultFile := "testdata/test_compositeimageuri.png"
	srcImageUri, _ := ConvertImageToDataUri(originImgPngSmall)
	compImageUri1, _ := ConvertImageToDataUri(originImgPngIcon1)
	compImageUri2, _ := ConvertImageToDataUri(originImgPngIcon2)
	resultDataUri, _ := CompositeImageUri(srcImageUri, compImageUri1, 0, 0, FormatPng)
	resultDataUri, _ = CompositeImageUri(resultDataUri, compImageUri2, 24, 24, FormatPng)
	err := ConvertDataUriToImage(resultDataUri, resultFile, FormatPng)
	if err != nil {
		c.Error(err)
	}
}

func (g *testWrapper) TestClipImage(c *C.C) {
	resultFile := "testdata/test_clipimage_100x200.png"
	err := ClipImage(originImg, resultFile, 0, 0, 100, 200, FormatPng)
	if err != nil {
		c.Error(err)
	}
	w, h, err := GetImageSize(resultFile)
	if err != nil {
		c.Error(err)
	}
	c.Check(int(w), C.Equals, 100)
	c.Check(int(h), C.Equals, 200)

	resultFile = "testdata/test_clipimage_160x160.png"
	err = ClipImage(originImg, resultFile, 40, 40, 160, 160, FormatPng)
	if err != nil {
		c.Error(err)
	}
	w, h, err = GetImageSize(resultFile)
	if err != nil {
		c.Error(err)
	}
	c.Check(int(w), C.Equals, 160)
	c.Check(int(h), C.Equals, 160)
}

func (g *testWrapper) TestConvertImage(c *C.C) {
	resultFile := "testdata/test_convertimage.png"
	err := ConvertImage(originImg, resultFile, FormatPng)
	if err != nil {
		c.Error(err)
	}
}

func (g *testWrapper) TestConvertImageToDataUri(c *C.C) {
	dataUri, err := ConvertImageToDataUri(originImgPngIcon2)
	if err != nil {
		c.Error(err)
	}
	c.Check(dataUri, C.Equals, testDataUri)
}

func (g *testWrapper) TestConvertDataUriToImage(c *C.C) {
	resultFile := "testdata/test_convertdatauri.png"
	err := ConvertDataUriToImage(testDataUri, resultFile, FormatPng)
	if err != nil {
		c.Error(err)
	}
}

func (g *testWrapper) TestLoadImageFromDataUri(c *C.C) {
	img, err := LoadImageFromDataUri(testDataUri)
	if err != nil {
		c.Error(err)
	}
	w, h := GetSize(img)
	c.Check(w, C.Equals, originIconWidth)
	c.Check(h, C.Equals, originIconHeight)
}

func (g *testWrapper) TestFillImage(c *C.C) {
	resultFile := "testdata/test_flllimage_tile_200x200.png"
	err := FillImage(originImg, resultFile, 200, 200, FillTile, FormatPng)
	if err != nil {
		c.Error(err)
	}

	resultFile = "testdata/test_flllimage_tile_1600x1000.png"
	err = FillImage(originImg, resultFile, 1600, 1000, FillTile, FormatPng)
	if err != nil {
		c.Error(err)
	}

	resultFile = "testdata/test_flllimage_center_400x400.png"
	err = FillImage(originImg, resultFile, 400, 400, FillCenter, FormatPng)
	if err != nil {
		c.Error(err)
	}

	resultFile = "testdata/test_flllimage_center_1600x1000.png"
	err = FillImage(originImg, resultFile, 1600, 1000, FillCenter, FormatPng)
	if err != nil {
		c.Error(err)
	}
}

func (g *testWrapper) TestFillImageCache(c *C.C) {
	_, _, err := FillImageCache(originImg, 1024, 768, FillTile, FormatPng)
	if err != nil {
		c.Skip("Fill image cache failed:" + err.Error())
		return
	}
	_, useCache, err := FillImageCache(originImg, 1024, 768, FillTile, FormatPng)
	if err != nil {
		c.Error(err)
	}
	c.Check(useCache, C.Equals, true)
}

func (g *testWrapper) TestFlipImageHorizontal(c *C.C) {
	resultFile := "testdata/test_flipimagehorizontal.png"
	err := FlipImageHorizontal(originImg, resultFile, FormatPng)
	if err != nil {
		c.Error(err)
	}
}

func (g *testWrapper) TestFlipImageVertical(c *C.C) {
	resultFile := "testdata/test_flipimagevertical.png"
	err := FlipImageVertical(originImg, resultFile, FormatPng)
	if err != nil {
		c.Error(err)
	}
}

func (g *testWrapper) TestGetDominantColor(c *C.C) {
	h, s, v, err := GetDominantColorOfImage(originImg)
	if err != nil {
		c.Error(err)
	}
	if delta(h, originImgDominantColorH) > 1 ||
		delta(s, originImgDominantColorS) > 0.1 ||
		delta(v, originImgDominantColorV) > 0.1 {
		c.Error("h, s, v = ", h, s, v)
	}
}

// Test that a subset of RGB space can be converted to HSV and back to within
// 1/256 tolerance.
func (g *testWrapper) TestHsv(c *C.C) {
	for r := 0; r < 255; r += 7 {
		for g := 0; g < 255; g += 5 {
			for b := 0; b < 255; b += 3 {
				r0, g0, b0 := uint8(r), uint8(g), uint8(b)
				h, s, v := Rgb2Hsv(r0, g0, b0)
				r1, g1, b1 := Hsv2Rgb(h, s, v)
				if delta(float64(r0), float64(r1)) > 1 || delta(float64(g0), float64(g1)) > 1 || delta(float64(b0), float64(b1)) > 1 {
					c.Fatalf("r0, g0, b0 = %d, %d, %d   r1, g1, b1 = %d, %d, %d", r0, g0, b0, r1, g1, b1)
				}
			}
		}
	}
}

func (g *testWrapper) TestGetImageSize(c *C.C) {
	w, h, err := GetImageSize(originImg)
	if err != nil {
		c.Error(err)
	}
	c.Check(int(w), C.Equals, originImgWidth)
	c.Check(int(h), C.Equals, originImgHeight)
}

func (g *testWrapper) TestGetImageFormat(c *C.C) {
	format, err := GetImageFormat(originImg)
	if err != nil {
		c.Error(err)
	}
	c.Check(format, C.Equals, FormatJpeg)
}

func (g *testWrapper) TestIsSupportedImage(c *C.C) {
	c.Check(IsSupportedImage(originImg), C.Equals, true)
	c.Check(IsSupportedImage(originImgNotImage), C.Equals, false)
	c.Check(IsSupportedImage("<file not exists>"), C.Equals, false)
}

func (g *testWrapper) TestScaleImage(c *C.C) {
	resultFile := "testdata/test_scaleimage_500x600.png"
	err := ScaleImage(originImg, resultFile, 500, 600, FormatPng)
	if err != nil {
		c.Error(err)
	}
	w, h, err := GetImageSize(resultFile)
	if err != nil {
		c.Error(err)
	}
	c.Check(int(w), C.Equals, 500)
	c.Check(int(h), C.Equals, 600)
}

func (g *testWrapper) TestScaleImagePrefer(c *C.C) {
	resultFile := "testdata/test_scaleimageprefer_500x600.png"
	err := ScaleImagePrefer(originImg, resultFile, 500, 600, FormatPng)
	if err != nil {
		c.Error(err)
	}
	w, h, err := GetImageSize(resultFile)
	if err != nil {
		c.Error(err)
	}
	c.Check(int(w), C.Equals, 500)
	c.Check(int(h), C.Equals, 600)
}

func (g *testWrapper) TestScaleImageCache(c *C.C) {
	_, _, err := ScaleImageCache(originImg, 200, 200, FormatPng)
	if err != nil {
		c.Skip("Scale image cache failed:" + err.Error())
		return
	}
	_, useCache, err := ScaleImageCache(originImg, 200, 200, FormatPng)
	if err != nil {
		c.Error(err)
	}
	c.Check(useCache, C.Equals, true)
}

func (g *testWrapper) TestThumbnailImage(c *C.C) {
	resultFile := "testdata/test_thumbnail.png"
	maxWidth, maxHeight := 200, 200
	err := ThumbnailImage(originImg, resultFile, maxWidth, maxHeight, FormatPng)
	if err != nil {
		c.Error(err)
	}
	w, h, _ := GetImageSize(resultFile)
	c.Check(int(w) <= maxWidth, C.Equals, true)
	c.Check(int(h) <= maxHeight, C.Equals, true)
}

func (g *testWrapper) TestThumbnailImageCache(c *C.C) {
	_, _, err := ThumbnailImageCache(originImg, 200, 200, FormatPng)
	if err != nil {
		c.Skip("Thumb image cache failed:" + err.Error())
		return
	}
	_, useCache, err := ThumbnailImageCache(originImg, 200, 200, FormatPng)
	if err != nil {
		c.Error(err)
	}
	c.Check(useCache, C.Equals, true)
}

func (g *testWrapper) TestRotateImageLeft(c *C.C) {
	resultFile := "testdata/test_rotateimageleft.png"
	err := RotateImageLeft(originImg, resultFile, FormatPng)
	if err != nil {
		c.Error(err)
	}
}

func (g *testWrapper) TestRotateImageRight(c *C.C) {
	resultFile := "testdata/test_rotateimageright.png"
	err := RotateImageRight(originImg, resultFile, FormatPng)
	if err != nil {
		c.Error(err)
	}
}

func (*testWrapper) TestGetPreferScaleClipRect(c *C.C) {
	data := []struct {
		refWidth, refHeight, imgWidth, imgHeight int
		x, y, w, h                               int
	}{
		{1024, 768, 512, 100, 189, 0, 133, 100},
		{1024, 768, 100, 384, 0, 154, 100, 75},

		{1024, 768, 512, 384, 0, 0, 512, 384},
		{1024, 768, 1024, 768, 0, 0, 1024, 768},
		{1440, 900, 1440, 900, 0, 0, 1440, 900},

		{1024, 768, 1920, 1080, 240, 0, 1440, 1080},
	}
	for _, d := range data {
		x, y, w, h, err := GetPreferScaleClipRect(d.refWidth, d.refHeight, d.imgWidth, d.imgHeight)
		c.Check(err, C.Equals, nil)
		c.Check(x, C.Equals, d.x)
		c.Check(y, C.Equals, d.y)
		c.Check(w, C.Equals, d.w)
		c.Check(h, C.Equals, d.h)
	}

	// check same clip rectangle size with original width and height
	for i := 1; i < 10000; i++ {
		x, y, w, h, _ := GetPreferScaleClipRect(i, 768, i, 768)
		c.Check(x, C.Equals, 0)
		c.Check(y, C.Equals, 0)
		c.Check(w, C.Equals, i)
		c.Check(h, C.Equals, 768)
	}

	// check exceptions
	var err error
	_, _, _, _, err = GetPreferScaleClipRect(0, 0, 100, 100)
	c.Check(err, C.NotNil)
	_, _, _, _, err = GetPreferScaleClipRect(1024, 768, 512, 0)
	c.Check(err, C.NotNil)
	_, _, _, _, err = GetPreferScaleClipRect(1024, 768, 0, 384)
	c.Check(err, C.NotNil)
	_, _, _, _, err = GetPreferScaleClipRect(1024, 768, 0, 0)
	c.Check(err, C.NotNil)
}

func (g *testWrapper) TestNewImageWithColor(c *C.C) {
	resultFile := "testdata/test_newimagewithcolor.png"
	err := NewImageWithColor(resultFile, 10, 10, uint8(12), uint8(200), uint8(12), uint8(220), FormatPng)
	if err != nil {
		c.Error(err)
	}
}

func (*testWrapper) TestGetIcons(c *C.C) {
	var datas = []struct {
		dir    string
		images map[string]string
		ret    bool
	}{
		{
			dir: "testdata/test-get_images",
			images: map[string]string{
				"testdata/test-get_images/1.png": "testdata/test-get_images/1.png",
				"testdata/test-get_images/2.png": "testdata/test-get_images/2.png",
				"testdata/test-get_images/3.png": "testdata/test-get_images/3.png",
			},
			ret: true,
		},
		{
			dir:    "testdata/test-get_images-noimage",
			images: nil,
			ret:    true,
		},
		{
			dir:    "testdata/origin_icon.txt",
			images: nil,
			ret:    false,
		},
	}

	for _, data := range datas {
		icons, err := GetImagesInDir(data.dir)
		if data.ret {
			c.Check(err, C.Equals, nil)
			c.Check(len(icons), C.Equals, len(data.images))
			for _, v := range icons {
				c.Check(v, C.Equals, data.images[v])
			}
		} else {
			c.Check(err, C.Not(C.Equals), nil)
			c.Check(len(icons), C.Equals, 0)
		}
	}
}

func (*testWrapper) TestSniffImageFormat(c *C.C) {
	var datas = []struct {
		file       string
		formatName string
	}{
		{
			file:       "testdata/origin_1920x1080.jpg",
			formatName: "jpeg",
		},
		{
			file:       "testdata/origin_icon.bmp",
			formatName: "bmp",
		},
		{
			file:       "testdata/origin_icon.gif",
			formatName: "gif",
		},
		{
			file:       "testdata/origin_icon_1_48x48.png",
			formatName: "png",
		},
		{
			file:       "testdata/origin_not_image",
			formatName: "",
		},
		{
			file:       "testdata/sniff_format.tiff",
			formatName: "tiff",
		},
	}

	for _, data := range datas {
		formatName, err := SniffImageFormat(data.file)
		c.Check(err, C.Equals, nil)
		c.Check(formatName, C.Equals, data.formatName)
	}
}
