// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package precis

import (
	
	
	

	
	
	
	
	
	
)

var (
	errDisallowedRune = errors.New("precis: disallowed rune encountered")
)

var dpTrie = newDerivedPropertiesTrie(0)

// A Profile represents a set of rules for normalizing and validating strings in
// the PRECIS framework.
type Profile struct {
	options
	class *class
}

// NewIdentifier creates a new PRECIS profile based on the Identifier string
// class. Profiles created from this class are suitable for use where safety is
// prioritized over expressiveness like network identifiers, user accounts, chat
// rooms, and file names.
func ( ...Option) *Profile {
	return &Profile{
		options: getOpts(...),
		class:   identifier,
	}
}

// NewFreeform creates a new PRECIS profile based on the Freeform string class.
// Profiles created from this class are suitable for use where expressiveness is
// prioritized over safety like passwords, and display-elements such as
// nicknames in a chat room.
func ( ...Option) *Profile {
	return &Profile{
		options: getOpts(...),
		class:   freeform,
	}
}

// NewRestrictedProfile creates a new PRECIS profile based on an existing
// profile.
// If the parent profile already had the Disallow option set, the new rule
// overrides the parents rule.
func ( *Profile,  runes.Set) *Profile {
	 := *
	Disallow()(&.options)
	return &
}

// NewTransformer creates a new transform.Transformer that performs the PRECIS
// preparation and enforcement steps on the given UTF-8 encoded bytes.
func ( *Profile) () *Transformer {
	var  []transform.Transformer

	// These transforms are applied in the order defined in
	// https://tools.ietf.org/html/rfc7564#section-7

	// RFC 8266 ยง2.1:
	//
	//     Implementation experience has shown that applying the rules for the
	//     Nickname profile is not an idempotent procedure for all code points.
	//     Therefore, an implementation SHOULD apply the rules repeatedly until
	//     the output string is stable; if the output string does not stabilize
	//     after reapplying the rules three (3) additional times after the first
	//     application, the implementation SHOULD terminate application of the
	//     rules and reject the input string as invalid.
	//
	// There is no known string that will change indefinitely, so repeat 4 times
	// and rely on the Span method to keep things relatively performant.
	 := 1
	if .options.repeat {
		 = 4
	}
	for ;  > 0; -- {
		if .options.foldWidth {
			 = append(, width.Fold)
		}

		for ,  := range .options.additional {
			 = append(, ())
		}

		if .options.cases != nil {
			 = append(, .options.cases)
		}

		 = append(, .options.norm)

		if .options.bidiRule {
			 = append(, bidirule.New())
		}

		 = append(, &checker{p: , allowed: .Allowed()})
	}

	// TODO: Add the disallow empty rule with a dummy transformer?

	return &Transformer{transform.Chain(...)}
}

var errEmptyString = errors.New("precis: transformation resulted in empty string")

type buffers struct {
	src  []byte
	buf  [2][]byte
	next int
}

func ( *buffers) ( transform.SpanningTransformer) ( error) {
	,  := .Span(.src, true)
	if  != transform.ErrEndOfSpan {
		return 
	}
	 := .next & 1
	if .buf[] == nil {
		.buf[] = make([]byte, 0, 8+len(.src)+len(.src)>>2)
	}
	 := append(.buf[][:0], .src[:]...)
	.src, _,  = transform.Append(, , .src[:])
	.buf[] = .src
	.next++
	return 
}

// Pre-allocate transformers when possible. In some cases this avoids allocation.
var (
	foldWidthT transform.SpanningTransformer = width.Fold
	lowerCaseT transform.SpanningTransformer = cases.Lower(language.Und, cases.HandleFinalSigma(false))
)

// TODO: make this a method on profile.

func ( *buffers) ( *Profile,  []byte,  bool) ( []byte,  error) {
	.src = 

	 := true
	for ,  := range  {
		if  >= utf8.RuneSelf {
			 = false
			break
		}
	}
	// ASCII fast path.
	if  {
		for ,  := range .options.additional {
			if  = .apply(());  != nil {
				return nil, 
			}
		}
		switch {
		case .options.asciiLower || ( && .options.ignorecase):
			for ,  := range .src {
				if 'A' <=  &&  <= 'Z' {
					.src[] =  ^ 1<<5
				}
			}
		case .options.cases != nil:
			.apply(.options.cases)
		}
		 := checker{p: }
		if ,  := .span(.src, true);  != nil {
			return nil, 
		}
		if .disallow != nil {
			for ,  := range .src {
				if .disallow.Contains(rune()) {
					return nil, errDisallowedRune
				}
			}
		}
		if .options.disallowEmpty && len(.src) == 0 {
			return nil, errEmptyString
		}
		return .src, nil
	}

	// These transforms are applied in the order defined in
	// https://tools.ietf.org/html/rfc8264#section-7

	 := 1
	if .options.repeat {
		 = 4
	}
	for ;  > 0; -- {
		// TODO: allow different width transforms options.
		if .options.foldWidth || (.options.ignorecase && ) {
			.apply(foldWidthT)
		}
		for ,  := range .options.additional {
			if  = .apply(());  != nil {
				return nil, 
			}
		}
		if .options.cases != nil {
			.apply(.options.cases)
		}
		if  && .options.ignorecase {
			.apply(lowerCaseT)
		}
		.apply(.norm)
		if .options.bidiRule && !bidirule.Valid(.src) {
			return nil, bidirule.ErrInvalid
		}
		 := checker{p: }
		if ,  := .span(.src, true);  != nil {
			return nil, 
		}
		if .disallow != nil {
			for  := 0;  < len(.src); {
				,  := utf8.DecodeRune(.src[:])
				if .disallow.Contains() {
					return nil, errDisallowedRune
				}
				 += 
			}
		}
		if .options.disallowEmpty && len(.src) == 0 {
			return nil, errEmptyString
		}
	}
	return .src, nil
}

// Append appends the result of applying p to src writing the result to dst.
// It returns an error if the input string is invalid.
func ( *Profile) (,  []byte) ([]byte, error) {
	var  buffers
	,  := .enforce(, , false)
	if  != nil {
		return nil, 
	}
	return append(, ...), nil
}

func processBytes( *Profile,  []byte,  bool) ([]byte, error) {
	var  buffers
	,  := .enforce(, , )
	if  != nil {
		return nil, 
	}
	if .next == 0 {
		 := make([]byte, len())
		copy(, )
		return , nil
	}
	return , nil
}

// Bytes returns a new byte slice with the result of applying the profile to b.
func ( *Profile) ( []byte) ([]byte, error) {
	return processBytes(, , false)
}

// AppendCompareKey appends the result of applying p to src (including any
// optional rules to make strings comparable or useful in a map key such as
// applying lowercasing) writing the result to dst. It returns an error if the
// input string is invalid.
func ( *Profile) (,  []byte) ([]byte, error) {
	var  buffers
	,  := .enforce(, , true)
	if  != nil {
		return nil, 
	}
	return append(, ...), nil
}

func processString( *Profile,  string,  bool) (string, error) {
	var  buffers
	,  := .enforce(, []byte(), )
	if  != nil {
		return "", 
	}
	return string(), nil
}

// String returns a string with the result of applying the profile to s.
func ( *Profile) ( string) (string, error) {
	return processString(, , false)
}

// CompareKey returns a string that can be used for comparison, hashing, or
// collation.
func ( *Profile) ( string) (string, error) {
	return processString(, , true)
}

// Compare enforces both strings, and then compares them for bit-string identity
// (byte-for-byte equality). If either string cannot be enforced, the comparison
// is false.
func ( *Profile) (,  string) bool {
	var  buffers

	,  := .enforce(, []byte(), true)
	if  != nil {
		return false
	}

	 = buffers{}
	,  := .enforce(, []byte(), true)
	if  != nil {
		return false
	}

	return bytes.Equal(, )
}

// Allowed returns a runes.Set containing every rune that is a member of the
// underlying profile's string class and not disallowed by any profile specific
// rules.
func ( *Profile) () runes.Set {
	if .options.disallow != nil {
		return runes.Predicate(func( rune) bool {
			return .class.Contains() && !.options.disallow.Contains()
		})
	}
	return .class
}

type checker struct {
	p       *Profile
	allowed runes.Set

	beforeBits catBitmap
	termBits   catBitmap
	acceptBits catBitmap
}

func ( *checker) () {
	.beforeBits = 0
	.termBits = 0
	.acceptBits = 0
}

func ( *checker) ( []byte,  bool) ( int,  error) {
	for  < len() {
		,  := dpTrie.lookup([:])
		 := categoryTransitions[category(&catMask)]
		if  == 0 {
			if ! {
				return , transform.ErrShortSrc
			}
			return , errDisallowedRune
		}
		 := false
		if property() < .p.class.validFrom {
			if .rule == nil {
				return , errDisallowedRune
			}
			,  = .rule(.beforeBits)
			if  != nil {
				return , 
			}
		}
		.beforeBits &= .keep
		.beforeBits |= .set
		if .termBits != 0 {
			// We are currently in an unterminated lookahead.
			if .beforeBits&.termBits != 0 {
				.termBits = 0
				.acceptBits = 0
			} else if .beforeBits&.acceptBits == 0 {
				// Invalid continuation of the unterminated lookahead sequence.
				return , errContext
			}
		}
		if  {
			if .termBits != 0 {
				// A previous lookahead run has not been terminated yet.
				return , errContext
			}
			.termBits = .term
			.acceptBits = .accept
		}
		 += 
	}
	if  := .beforeBits >> finalShift; .beforeBits& !=  || .termBits != 0 {
		 = errContext
	}
	return , 
}

// TODO: we may get rid of this transform if transform.Chain understands
// something like a Spanner interface.
func ( checker) (,  []byte,  bool) (,  int,  error) {
	 := false
	if len() < len() {
		 = [:len()]
		 = false
		 = true
	}
	,  = .span(, )
	 = copy(, [:])
	if  && ( == transform.ErrShortSrc ||  == nil) {
		 = transform.ErrShortDst
	}
	return , , 
}