package arn

import (
	"bytes"
	"fmt"
	"image"
	"net/http"
	"path"
	"time"

	"github.com/aerogo/http/client"
	"github.com/akyoto/imageserver"
)

const (
	// CharacterImageLargeWidth is the minimum width in pixels of a large character image.
	// We subtract 6 pixels due to border removal which can remove up to 6 pixels.
	CharacterImageLargeWidth = 225 - 6

	// CharacterImageLargeHeight is the minimum height in pixels of a large character image.
	// We subtract 6 pixels due to border removal which can remove up to 6 pixels.
	CharacterImageLargeHeight = 350 - 6

	// CharacterImageMediumWidth is the minimum width in pixels of a medium character image.
	CharacterImageMediumWidth = 112

	// CharacterImageMediumHeight is the minimum height in pixels of a medium character image.
	CharacterImageMediumHeight = 112

	// CharacterImageSmallWidth is the minimum width in pixels of a small character image.
	CharacterImageSmallWidth = 56

	// CharacterImageSmallHeight is the minimum height in pixels of a small character image.
	CharacterImageSmallHeight = 56

	// CharacterImageWebPQuality is the WebP quality of character images.
	CharacterImageWebPQuality = 70

	// CharacterImageJPEGQuality is the JPEG quality of character images.
	CharacterImageJPEGQuality = 70

	// CharacterImageQualityBonusLowDPI ...
	CharacterImageQualityBonusLowDPI = 12

	// CharacterImageQualityBonusLarge ...
	CharacterImageQualityBonusLarge = 10

	// CharacterImageQualityBonusMedium ...
	CharacterImageQualityBonusMedium = 15

	// CharacterImageQualityBonusSmall ...
	CharacterImageQualityBonusSmall = 15
)

// Define the character image outputs
var characterImageOutputs = []imageserver.Output{
	// Original at full size
	&imageserver.OriginalFile{
		Directory: path.Join(Root, "images/characters/original/"),
		Width:     0,
		Height:    0,
		Quality:   0,
	},

	// JPEG - Large
	&imageserver.JPEGFile{
		Directory: path.Join(Root, "images/characters/large/"),
		Width:     CharacterImageLargeWidth,
		Height:    CharacterImageLargeHeight,
		Quality:   CharacterImageJPEGQuality + CharacterImageQualityBonusLowDPI + CharacterImageQualityBonusLarge,
	},

	// JPEG - Medium
	&imageserver.JPEGFile{
		Directory: path.Join(Root, "images/characters/medium/"),
		Width:     CharacterImageMediumWidth,
		Height:    CharacterImageMediumHeight,
		Quality:   CharacterImageJPEGQuality + CharacterImageQualityBonusLowDPI + CharacterImageQualityBonusMedium,
	},

	// JPEG - Small
	&imageserver.JPEGFile{
		Directory: path.Join(Root, "images/characters/small/"),
		Width:     CharacterImageSmallWidth,
		Height:    CharacterImageSmallHeight,
		Quality:   CharacterImageJPEGQuality + CharacterImageQualityBonusLowDPI + CharacterImageQualityBonusSmall,
	},

	// WebP - Large
	&imageserver.WebPFile{
		Directory: path.Join(Root, "images/characters/large/"),
		Width:     CharacterImageLargeWidth,
		Height:    CharacterImageLargeHeight,
		Quality:   CharacterImageWebPQuality + CharacterImageQualityBonusLowDPI + CharacterImageQualityBonusLarge,
	},

	// WebP - Medium
	&imageserver.WebPFile{
		Directory: path.Join(Root, "images/characters/medium/"),
		Width:     CharacterImageMediumWidth,
		Height:    CharacterImageMediumHeight,
		Quality:   CharacterImageWebPQuality + CharacterImageQualityBonusLowDPI + CharacterImageQualityBonusMedium,
	},

	// WebP - Small
	&imageserver.WebPFile{
		Directory: path.Join(Root, "images/characters/small/"),
		Width:     CharacterImageSmallWidth,
		Height:    CharacterImageSmallHeight,
		Quality:   CharacterImageWebPQuality + CharacterImageQualityBonusLowDPI + CharacterImageQualityBonusSmall,
	},
}

// Define the high DPI character image outputs
var characterImageOutputsHighDPI = []imageserver.Output{
	// NOTE: We don't save "large" images in double size because that's usually the maximum size anyway.

	// JPEG - Medium
	&imageserver.JPEGFile{
		Directory: path.Join(Root, "images/characters/medium/"),
		Width:     CharacterImageMediumWidth * 2,
		Height:    CharacterImageMediumHeight * 2,
		Quality:   CharacterImageJPEGQuality + CharacterImageQualityBonusMedium,
	},

	// JPEG - Small
	&imageserver.JPEGFile{
		Directory: path.Join(Root, "images/characters/small/"),
		Width:     CharacterImageSmallWidth * 2,
		Height:    CharacterImageSmallHeight * 2,
		Quality:   CharacterImageJPEGQuality + CharacterImageQualityBonusSmall,
	},

	// WebP - Medium
	&imageserver.WebPFile{
		Directory: path.Join(Root, "images/characters/medium/"),
		Width:     CharacterImageMediumWidth * 2,
		Height:    CharacterImageMediumHeight * 2,
		Quality:   CharacterImageWebPQuality + CharacterImageQualityBonusMedium,
	},

	// WebP - Small
	&imageserver.WebPFile{
		Directory: path.Join(Root, "images/characters/small/"),
		Width:     CharacterImageSmallWidth * 2,
		Height:    CharacterImageSmallHeight * 2,
		Quality:   CharacterImageWebPQuality + CharacterImageQualityBonusSmall,
	},
}

// SetImageBytes accepts a byte buffer that represents an image file and updates the character image.
func (character *Character) SetImageBytes(data []byte) error {
	// Decode
	img, format, err := image.Decode(bytes.NewReader(data))

	if err != nil {
		return err
	}

	return character.SetImage(&imageserver.MetaImage{
		Image:  img,
		Format: format,
		Data:   data,
	})
}

// SetImage sets the character image to the given MetaImage.
func (character *Character) SetImage(metaImage *imageserver.MetaImage) error {
	var lastError error

	// Save the different image formats and sizes in low DPI
	for _, output := range characterImageOutputs {
		err := output.Save(metaImage, character.ID)

		if err != nil {
			lastError = err
		}
	}

	// Save the different image formats and sizes in high DPI
	for _, output := range characterImageOutputsHighDPI {
		err := output.Save(metaImage, character.ID+"@2")

		if err != nil {
			lastError = err
		}
	}

	character.Image.Extension = metaImage.Extension()
	character.Image.Width = metaImage.Image.Bounds().Dx()
	character.Image.Height = metaImage.Image.Bounds().Dy()
	character.Image.AverageColor = GetAverageColor(metaImage.Image)
	character.Image.LastModified = time.Now().Unix()
	return lastError
}

// DownloadImage ...
func (character *Character) DownloadImage(url string) error {
	response, err := client.Get(url).End()

	// Cancel the import if image could not be fetched
	if err != nil {
		return err
	}

	if response.StatusCode() != http.StatusOK {
		return fmt.Errorf("Image response status code: %d", response.StatusCode())
	}

	return character.SetImageBytes(response.Bytes())
}

// HasImage returns true if the character has an image.
func (character *Character) HasImage() bool {
	return character.Image.Extension != "" && character.Image.Width > 0
}
