package thumbnail

import (
	"bytes"
	"github.com/disintegration/imaging"
	"github.com/owncloud/ocis/ocis-pkg/log"
	"github.com/owncloud/ocis/thumbnails/pkg/thumbnail/storage"
	"image"
	"mime"
	"strings"
)

var (
	SupportedMimeTypes = [...]string{
		"image/png",
		"image/jpg",
		"image/jpeg",
		"image/gif",
		"text/plain",
	}
)

// Request bundles information needed to generate a thumbnail for afile
type Request struct {
	Resolution image.Rectangle
	Encoder    Encoder
	Checksum   string
}

// Manager is responsible for generating thumbnails
type Manager interface {
	// Generate will return a thumbnail for a file
	Generate(Request, image.Image) ([]byte, error)
	// Get loads the thumbnail from the storage.
	// It will return nil if no image is stored for the given context.
	Get(Request) ([]byte, bool)
}

// NewSimpleManager creates a new instance of SimpleManager
func NewSimpleManager(resolutions Resolutions, storage storage.Storage, logger log.Logger) SimpleManager {
	return SimpleManager{
		storage:     storage,
		logger:      logger,
		resolutions: resolutions,
	}
}

// SimpleManager is a simple implementation of Manager
type SimpleManager struct {
	storage     storage.Storage
	logger      log.Logger
	resolutions Resolutions
}

// Generate creates a thumbnail and stores it.
// The created thumbnail is also being returned.
func (s SimpleManager) Generate(r Request, img image.Image) ([]byte, error) {
	match := s.resolutions.ClosestMatch(r.Resolution, img.Bounds())
	thumbnail := s.generate(match, img)

	dst := new(bytes.Buffer)
	err := r.Encoder.Encode(dst, thumbnail)
	if err != nil {
		return nil, err
	}

	k := s.storage.BuildKey(mapToStorageRequest(r))
	err = s.storage.Put(k, dst.Bytes())
	if err != nil {
		s.logger.Warn().Err(err).Msg("could not store thumbnail")
	}
	return dst.Bytes(), nil
}

// Get tries to get the stored thumbnail and return it.
// If there is no cached thumbnail it will return nil
func (s SimpleManager) Get(r Request) ([]byte, bool) {
	k := s.storage.BuildKey(mapToStorageRequest(r))
	return s.storage.Get(k)
}

func (s SimpleManager) generate(r image.Rectangle, img image.Image) image.Image {
	return imaging.Thumbnail(img, r.Dx(), r.Dy(), imaging.Lanczos)
}

func mapToStorageRequest(r Request) storage.Request {
	return storage.Request{
		Checksum:   r.Checksum,
		Resolution: r.Resolution,
		Types:      r.Encoder.Types(),
	}
}

func IsMimeTypeSupported(m string) bool {
	mimeType, _, err := mime.ParseMediaType(m)
	if err != nil {
		return false
	}
	for _, mt := range SupportedMimeTypes {
		if strings.EqualFold(mt, mimeType) {
			return true
		}
	}
	return false
}
