// Copyright (C) 2016 Kohei YOSHIDA. All rights reserved.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of The BSD 3-Clause License
// that can be found in the LICENSE file.

package uritemplate

import (
	
	
	
)

var (
	hex = []byte("0123456789ABCDEF")
	// reserved   = gen-delims / sub-delims
	// gen-delims =  ":" / "/" / "?" / "#" / "[" / "]" / "@"
	// sub-delims =  "!" / "$" / "&" / "’" / "(" / ")"
	//            /  "*" / "+" / "," / ";" / "="
	rangeReserved = &unicode.RangeTable{
		R16: []unicode.Range16{
			{Lo: 0x21, Hi: 0x21, Stride: 1}, // '!'
			{Lo: 0x23, Hi: 0x24, Stride: 1}, // '#' - '$'
			{Lo: 0x26, Hi: 0x2C, Stride: 1}, // '&' - ','
			{Lo: 0x2F, Hi: 0x2F, Stride: 1}, // '/'
			{Lo: 0x3A, Hi: 0x3B, Stride: 1}, // ':' - ';'
			{Lo: 0x3D, Hi: 0x3D, Stride: 1}, // '='
			{Lo: 0x3F, Hi: 0x40, Stride: 1}, // '?' - '@'
			{Lo: 0x5B, Hi: 0x5B, Stride: 1}, // '['
			{Lo: 0x5D, Hi: 0x5D, Stride: 1}, // ']'
		},
		LatinOffset: 9,
	}
	reReserved = `\x21\x23\x24\x26-\x2c\x2f\x3a\x3b\x3d\x3f\x40\x5b\x5d`
	// ALPHA      = %x41-5A / %x61-7A
	// DIGIT      = %x30-39
	// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
	rangeUnreserved = &unicode.RangeTable{
		R16: []unicode.Range16{
			{Lo: 0x2D, Hi: 0x2E, Stride: 1}, // '-' - '.'
			{Lo: 0x30, Hi: 0x39, Stride: 1}, // '0' - '9'
			{Lo: 0x41, Hi: 0x5A, Stride: 1}, // 'A' - 'Z'
			{Lo: 0x5F, Hi: 0x5F, Stride: 1}, // '_'
			{Lo: 0x61, Hi: 0x7A, Stride: 1}, // 'a' - 'z'
			{Lo: 0x7E, Hi: 0x7E, Stride: 1}, // '~'
		},
	}
	reUnreserved = `\x2d\x2e\x30-\x39\x41-\x5a\x5f\x61-\x7a\x7e`
)

type runeClass uint8

const (
	runeClassU runeClass = 1 << iota
	runeClassR
	runeClassPctE
	runeClassLast

	runeClassUR = runeClassU | runeClassR
)

var runeClassNames = []string{
	"U",
	"R",
	"pct-encoded",
}

func ( runeClass) () string {
	 := make([]string, 0, len(runeClassNames))
	for ,  := 0, runeClass(1);  < runeClassLast;  <<= 1 {
		if & ==  {
			 = append(, runeClassNames[])
		}
		++
	}
	return strings.Join(, "+")
}

func pctEncode( *strings.Builder,  rune) {
	if  :=  >> 24 & 0xff;  > 0 {
		.Write([]byte{'%', hex[/16], hex[%16]})
	}
	if  :=  >> 16 & 0xff;  > 0 {
		.Write([]byte{'%', hex[/16], hex[%16]})
	}
	if  :=  >> 8 & 0xff;  > 0 {
		.Write([]byte{'%', hex[/16], hex[%16]})
	}
	if  :=  & 0xff;  > 0 {
		.Write([]byte{'%', hex[/16], hex[%16]})
	}
}

func unhex( byte) byte {
	switch {
	case '0' <=  &&  <= '9':
		return  - '0'
	case 'a' <=  &&  <= 'f':
		return  - 'a' + 10
	case 'A' <=  &&  <= 'F':
		return  - 'A' + 10
	}
	return 0
}

func ishex( byte) bool {
	switch {
	case '0' <=  &&  <= '9':
		return true
	case 'a' <=  &&  <= 'f':
		return true
	case 'A' <=  &&  <= 'F':
		return true
	default:
		return false
	}
}

func pctDecode( string) string {
	 := len()
	for  := 0;  < len(); {
		switch [] {
		case '%':
			 -= 2
			 += 3
		default:
			++
		}
	}
	if  == len() {
		return 
	}

	 := make([]byte, )
	 := 0
	for  := 0;  < len(); {
		switch  := [];  {
		case '%':
			[] = unhex([+1])<<4 | unhex([+2])
			 += 3
			++
		default:
			[] = 
			++
			++
		}
	}
	return string()
}

type escapeFunc func(*strings.Builder, string) error

func escapeLiteral( *strings.Builder,  string) error {
	.WriteString()
	return nil
}

func escapeExceptU( *strings.Builder,  string) error {
	for  := 0;  < len(); {
		,  := utf8.DecodeRuneInString([:])
		if  == utf8.RuneError {
			return errorf(, "invalid encoding")
		}
		if unicode.Is(rangeUnreserved, ) {
			.WriteRune()
		} else {
			pctEncode(, )
		}
		 += 
	}
	return nil
}

func escapeExceptUR( *strings.Builder,  string) error {
	for  := 0;  < len(); {
		,  := utf8.DecodeRuneInString([:])
		if  == utf8.RuneError {
			return errorf(, "invalid encoding")
		}
		// TODO(yosida95): is pct-encoded triplets allowed here?
		if unicode.In(, rangeUnreserved, rangeReserved) {
			.WriteRune()
		} else {
			pctEncode(, )
		}
		 += 
	}
	return nil
}