package domain

import (
	"bytes"
	"image"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"io"
	"mime"
	"path"
	"time"

	_ "golang.org/x/image/bmp"
	_ "golang.org/x/image/webp"
)

type Resource struct {
	File File

	modTime      time.Time
	reader       io.ReadSeeker
	params       map[string]any // TODO(toby3d): set from Page configuration
	mediaType    MediaType
	key          string
	name         string // TODO(toby3d): set from Page configuration
	title        string // TODO(toby3d): set from Page configuration
	resourceType ResourceType
	image        image.Config
}

func NewResource(modTime time.Time, content []byte, key string) *Resource {
	mediaType, _, _ := mime.ParseMediaType(mime.TypeByExtension(path.Ext(key)))
	out := &Resource{
		File:      NewFile(key),
		modTime:   modTime,
		key:       key,
		name:      key,                  // TODO(toby3d): set from Page configuration
		title:     "",                   // TODO(toby3d): set from Page configuration
		params:    make(map[string]any), // TODO(toby3d): set from Page configuration
		mediaType: NewMediaType(mediaType),
		reader:    bytes.NewReader(content),
	}

	switch path.Ext(key) {
	default:
		out.resourceType, _ = ParseResourceType(out.mediaType.mainType)
	case ".md":
		out.resourceType = ResourceTypePage
	case ".webmanifest":
		out.resourceType = ResourceTypeText
	}

	switch out.resourceType {
	case ResourceTypeImage:
		out.image, _, _ = image.DecodeConfig(out.reader)
	}

	return out
}

func (r Resource) Key() string {
	return r.key
}

func (r Resource) Name() string {
	return r.name
}

func (r Resource) MediaType() MediaType {
	return r.mediaType
}

// Width returns width if current r is an image.
func (r Resource) Width() int {
	return r.image.Width
}

// Height returns height if current r is an image.
func (r Resource) Height() int {
	return r.image.Height
}

func (r Resource) ResourceType() ResourceType {
	return r.resourceType
}

func (r Resource) Content() []byte {
	content, _ := io.ReadAll(r.reader)

	return content
}

func (r Resource) Read(p []byte) (int, error) {
	return r.reader.Read(p)
}

func (r Resource) Seek(offset int64, whence int) (int64, error) {
	return r.reader.Seek(offset, whence)
}

func (f Resource) GoString() string {
	return "domain.Resource(" + f.key + ")"
}

// ResourceModTime used in http.ServeContent to get modtime of this resource.
func ResourceModTime(r *Resource) time.Time {
	if r == nil {
		return time.Time{}
	}

	return r.modTime
}
