package common

import (
	"bytes"
	"context"
	"strconv"
	"strings"

	"github.com/gorilla/css/scanner"

	"github.com/MontFerret/ferret/pkg/runtime/core"
	"github.com/MontFerret/ferret/pkg/runtime/values"
)

func DeserializeStyles(input values.String) (*values.Object, error) {
	styles := values.NewObject()

	if input == values.EmptyString {
		return styles, nil
	}

	s := scanner.New(input.String())

	var name string
	var value bytes.Buffer
	var setValue = func() {
		styles.Set(values.NewString(strings.TrimSpace(name)), values.NewString(strings.TrimSpace(value.String())))
		name = ""
		value.Reset()
	}

	for {
		token := s.Next()

		if token == nil {
			break
		}

		if token.Type == scanner.TokenEOF {
			break
		}

		if name == "" && token.Type == scanner.TokenIdent {
			name = token.Value

			// skip : and white spaces
			for {
				token = s.Next()

				if token.Value != ":" && token.Type != scanner.TokenS {
					break
				}
			}
		}

		switch token.Type {
		case scanner.TokenChar:
			// end of style declaration
			if token.Value == ";" {
				if name != "" {
					setValue()
				}
			} else {
				value.WriteString(token.Value)
			}
		case scanner.TokenNumber:
			num, err := strconv.ParseFloat(token.Value, 64)

			if err == nil {
				styles.Set(values.NewString(name), values.NewFloat(num))
				// reset prop
				name = ""
				value.Reset()
			}
		default:
			value.WriteString(token.Value)
		}
	}

	if name != "" && value.Len() > 0 {
		setValue()
	}

	return styles, nil
}

func SerializeStyles(_ context.Context, styles *values.Object) values.String {
	if styles == nil {
		return values.EmptyString
	}

	var b bytes.Buffer

	styles.ForEach(func(value core.Value, key string) bool {
		b.WriteString(key)
		b.WriteString(": ")
		b.WriteString(value.String())
		b.WriteString("; ")

		return true
	})

	return values.NewString(b.String())
}