package soap

import (
	
	
	
	
	
	
	
	
	
	
)

var (
	// localLoc acts like time.Local for this package, but is faked out by the
	// unit tests to ensure that things stay constant (especially when running
	// this test in a place where local time is UTC which might mask bugs).
	localLoc = time.Local
)

func ( uint8) (string, error) {
	return strconv.FormatUint(uint64(), 10), nil
}

func ( string) (uint8, error) {
	,  := strconv.ParseUint(, 10, 8)
	return uint8(), 
}

func ( uint16) (string, error) {
	return strconv.FormatUint(uint64(), 10), nil
}

func ( string) (uint16, error) {
	,  := strconv.ParseUint(, 10, 16)
	return uint16(), 
}

func ( uint32) (string, error) {
	return strconv.FormatUint(uint64(), 10), nil
}

func ( string) (uint32, error) {
	,  := strconv.ParseUint(, 10, 32)
	return uint32(), 
}

func ( uint64) (string, error) {
	return strconv.FormatUint(, 10), nil
}

func ( string) (uint64, error) {
	,  := strconv.ParseUint(, 10, 64)
	return uint64(), 
}

func ( int8) (string, error) {
	return strconv.FormatInt(int64(), 10), nil
}

func ( string) (int8, error) {
	,  := strconv.ParseInt(, 10, 8)
	return int8(), 
}

func ( int16) (string, error) {
	return strconv.FormatInt(int64(), 10), nil
}

func ( string) (int16, error) {
	,  := strconv.ParseInt(, 10, 16)
	return int16(), 
}

func ( int32) (string, error) {
	return strconv.FormatInt(int64(), 10), nil
}

func ( string) (int32, error) {
	,  := strconv.ParseInt(, 10, 32)
	return int32(), 
}

func ( int64) (string, error) {
	return strconv.FormatInt(, 10), nil
}

func ( string) (int64, error) {
	return strconv.ParseInt(, 10, 64)
}

func ( float32) (string, error) {
	return strconv.FormatFloat(float64(), 'G', -1, 32), nil
}

func ( string) (float32, error) {
	,  := strconv.ParseFloat(, 32)
	return float32(), 
}

func ( float64) (string, error) {
	return strconv.FormatFloat(, 'G', -1, 64), nil
}

func ( string) (float64, error) {
	,  := strconv.ParseFloat(, 64)
	return float64(), 
}

// MarshalFixed14_4 marshals float64 to SOAP "fixed.14.4" type.
func ( float64) (string, error) {
	if  >= 1e14 ||  <= -1e14 {
		return "", fmt.Errorf("soap fixed14.4: value %v out of bounds", )
	}
	return strconv.FormatFloat(, 'f', 4, 64), nil
}

// UnmarshalFixed14_4 unmarshals float64 from SOAP "fixed.14.4" type.
func ( string) (float64, error) {
	,  := strconv.ParseFloat(, 64)
	if  != nil {
		return 0, 
	}
	if  >= 1e14 ||  <= -1e14 {
		return 0, fmt.Errorf("soap fixed14.4: value %q out of bounds", )
	}
	return , nil
}

// MarshalChar marshals rune to SOAP "char" type.
func ( rune) (string, error) {
	if  == 0 {
		return "", errors.New("soap char: rune 0 is not allowed")
	}
	return string(), nil
}

// UnmarshalChar unmarshals rune from SOAP "char" type.
func ( string) (rune, error) {
	if len() == 0 {
		return 0, errors.New("soap char: got empty string")
	}
	,  := utf8.DecodeRune([]byte())
	if  != len() {
		return 0, fmt.Errorf("soap char: value %q is not a single rune", )
	}
	return , nil
}

func ( string) (string, error) {
	return , nil
}

func ( string) (string, error) {
	return , nil
}

func parseInt( string,  *error) int {
	,  := strconv.ParseInt(, 10, 64)
	if  != nil {
		* = 
	}
	return int()
}

var dateRegexps = []*regexp.Regexp{
	// yyyy[-mm[-dd]]
	regexp.MustCompile(`^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?$`),
	// yyyy[mm[dd]]
	regexp.MustCompile(`^(\d{4})(?:(\d{2})(?:(\d{2}))?)?$`),
}

func parseDateParts( string) (, ,  int,  error) {
	var  []string
	for ,  := range dateRegexps {
		 = .FindStringSubmatch()
		if  != nil {
			break
		}
	}
	if  == nil {
		 = fmt.Errorf("soap date: value %q is not in a recognized ISO8601 date format", )
		return
	}

	 = parseInt([1], &)
	 = 1
	 = 1
	if len([2]) != 0 {
		 = parseInt([2], &)
		if len([3]) != 0 {
			 = parseInt([3], &)
		}
	}

	if  != nil {
		 = fmt.Errorf("soap date: %q: %v", , )
	}

	return
}

var timeRegexps = []*regexp.Regexp{
	// hh[:mm[:ss]]
	regexp.MustCompile(`^(\d{2})(?::(\d{2})(?::(\d{2}))?)?$`),
	// hh[mm[ss]]
	regexp.MustCompile(`^(\d{2})(?:(\d{2})(?:(\d{2}))?)?$`),
}

func parseTimeParts( string) (, ,  int,  error) {
	var  []string
	for ,  := range timeRegexps {
		 = .FindStringSubmatch()
		if  != nil {
			break
		}
	}
	if  == nil {
		 = fmt.Errorf("soap time: value %q is not in ISO8601 time format", )
		return
	}

	 = parseInt([1], &)
	if len([2]) != 0 {
		 = parseInt([2], &)
		if len([3]) != 0 {
			 = parseInt([3], &)
		}
	}

	if  != nil {
		 = fmt.Errorf("soap time: %q: %v", , )
	}

	return
}

// (+|-)hh[[:]mm]
var timezoneRegexp = regexp.MustCompile(`^([+-])(\d{2})(?::?(\d{2}))?$`)

func parseTimezone( string) ( int,  error) {
	if  == "Z" {
		return 0, nil
	}
	 := timezoneRegexp.FindStringSubmatch()
	if  == nil {
		 = fmt.Errorf("soap timezone: value %q is not in ISO8601 timezone format", )
		return
	}

	 = parseInt([2], &) * 3600
	if len([3]) != 0 {
		 += parseInt([3], &) * 60
	}
	if [1] == "-" {
		 = -
	}

	if  != nil {
		 = fmt.Errorf("soap timezone: %q: %v", , )
	}

	return
}

var completeDateTimeZoneRegexp = regexp.MustCompile(`^([^T]+)(?:T([^-+Z]+)(.+)?)?$`)

// splitCompleteDateTimeZone splits date, time and timezone apart from an
// ISO8601 string. It does not ensure that the contents of each part are
// correct, it merely splits on certain delimiters.
// e.g "2010-09-08T12:15:10+0700" => "2010-09-08", "12:15:10", "+0700".
// Timezone can only be present if time is also present.
func splitCompleteDateTimeZone( string) (, ,  string,  error) {
	 := completeDateTimeZoneRegexp.FindStringSubmatch()
	if  == nil {
		 = fmt.Errorf("soap date/time/zone: value %q is not in ISO8601 datetime format", )
		return
	}
	 = [1]
	 = [2]
	 = [3]
	return
}

// MarshalDate marshals time.Time to SOAP "date" type. Note that this converts
// to local time, and discards the time-of-day components.
func ( time.Time) (string, error) {
	return .In(localLoc).Format("2006-01-02"), nil
}

var dateFmts = []string{"2006-01-02", "20060102"}

// UnmarshalDate unmarshals time.Time from SOAP "date" type. This outputs the
// date as midnight in the local time zone.
func ( string) (time.Time, error) {
	, , ,  := parseDateParts()
	if  != nil {
		return time.Time{}, 
	}
	return time.Date(, time.Month(), , 0, 0, 0, 0, localLoc), nil
}

// TimeOfDay is used in cases where SOAP "time" or "time.tz" is used.
type TimeOfDay struct {
	// Duration of time since midnight.
	FromMidnight time.Duration

	// Set to true if Offset is specified. If false, then the timezone is
	// unspecified (and by ISO8601 - implies some "local" time).
	HasOffset bool

	// Offset is non-zero only if time.tz is used. It is otherwise ignored. If
	// non-zero, then it is regarded as a UTC offset in seconds. Note that the
	// sub-minutes is ignored by the marshal function.
	Offset int
}

// MarshalTimeOfDay marshals TimeOfDay to the "time" type.
func ( TimeOfDay) (string, error) {
	 := int64(.FromMidnight / time.Second)
	 :=  / 3600
	 =  % 3600
	 :=  / 60
	 :=  % 60

	return fmt.Sprintf("%02d:%02d:%02d", , , ), nil
}

// UnmarshalTimeOfDay unmarshals TimeOfDay from the "time" type.
func ( string) (TimeOfDay, error) {
	,  := UnmarshalTimeOfDayTz()
	if  != nil {
		return TimeOfDay{}, 
	} else if .HasOffset {
		return TimeOfDay{}, fmt.Errorf("soap time: value %q contains unexpected timezone", )
	}
	return , nil
}

// MarshalTimeOfDayTz marshals TimeOfDay to the "time.tz" type.
func ( TimeOfDay) (string, error) {
	 := int64(.FromMidnight / time.Second)
	 :=  / 3600
	 =  % 3600
	 :=  / 60
	 :=  % 60

	 := ""
	if .HasOffset {
		if .Offset == 0 {
			 = "Z"
		} else {
			 := .Offset / 60
			 := '+'
			if  < 1 {
				 = -
				 = '-'
			}
			 = fmt.Sprintf("%c%02d:%02d", , /60, %60)
		}
	}

	return fmt.Sprintf("%02d:%02d:%02d%s", , , , ), nil
}

// UnmarshalTimeOfDayTz unmarshals TimeOfDay from the "time.tz" type.
func ( string) ( TimeOfDay,  error) {
	 := strings.IndexAny(, "Z+-")
	var  string
	var  bool
	var  int
	if  == -1 {
		 = false
		 = 
	} else {
		 = true
		 = [:]
		if ,  = parseTimezone([:]);  != nil {
			return
		}
	}

	, , ,  := parseTimeParts()
	if  != nil {
		return
	}

	 := time.Duration(*3600+*60+) * time.Second

	// ISO8601 special case - values up to 24:00:00 are allowed, so using
	// strictly greater-than for the maximum value.
	if  > 24*time.Hour ||  >= 60 ||  >= 60 {
		return TimeOfDay{}, fmt.Errorf("soap time.tz: value %q has value(s) out of range", )
	}

	return TimeOfDay{
		FromMidnight: time.Duration(*3600+*60+) * time.Second,
		HasOffset:    ,
		Offset:       ,
	}, nil
}

// MarshalDateTime marshals time.Time to SOAP "dateTime" type. Note that this
// converts to local time.
func ( time.Time) (string, error) {
	return .In(localLoc).Format("2006-01-02T15:04:05"), nil
}

// UnmarshalDateTime unmarshals time.Time from the SOAP "dateTime" type. This
// returns a value in the local timezone.
func ( string) ( time.Time,  error) {
	, , ,  := splitCompleteDateTimeZone()
	if  != nil {
		return
	}

	if len() != 0 {
		 = fmt.Errorf("soap datetime: unexpected timezone in %q", )
		return
	}

	, , ,  := parseDateParts()
	if  != nil {
		return
	}

	var , ,  int
	if len() != 0 {
		, , ,  = parseTimeParts()
		if  != nil {
			return
		}
	}

	 = time.Date(, time.Month(), , , , , 0, localLoc)
	return
}

// MarshalDateTimeTz marshals time.Time to SOAP "dateTime.tz" type.
func ( time.Time) (string, error) {
	return .Format("2006-01-02T15:04:05-07:00"), nil
}

// UnmarshalDateTimeTz unmarshals time.Time from the SOAP "dateTime.tz" type.
// This returns a value in the local timezone when the timezone is unspecified.
func ( string) ( time.Time,  error) {
	, , ,  := splitCompleteDateTimeZone()
	if  != nil {
		return
	}

	, , ,  := parseDateParts()
	if  != nil {
		return
	}

	var , ,  int
	var  *time.Location = localLoc
	if len() != 0 {
		, , ,  = parseTimeParts()
		if  != nil {
			return
		}
		if len() != 0 {
			var  int
			,  = parseTimezone()
			if  == 0 {
				 = time.UTC
			} else {
				 = time.FixedZone("", )
			}
		}
	}

	 = time.Date(, time.Month(), , , , , 0, )
	return
}

// MarshalBoolean marshals bool to SOAP "boolean" type.
func ( bool) (string, error) {
	if  {
		return "1", nil
	}
	return "0", nil
}

// UnmarshalBoolean unmarshals bool from the SOAP "boolean" type.
func ( string) (bool, error) {
	switch  {
	case "0", "false", "no":
		return false, nil
	case "1", "true", "yes":
		return true, nil
	}
	return false, fmt.Errorf("soap boolean: %q is not a valid boolean value", )
}

// MarshalBinBase64 marshals []byte to SOAP "bin.base64" type.
func ( []byte) (string, error) {
	return base64.StdEncoding.EncodeToString(), nil
}

// UnmarshalBinBase64 unmarshals []byte from the SOAP "bin.base64" type.
func ( string) ([]byte, error) {
	return base64.StdEncoding.DecodeString()
}

// MarshalBinHex marshals []byte to SOAP "bin.hex" type.
func ( []byte) (string, error) {
	return hex.EncodeToString(), nil
}

// UnmarshalBinHex unmarshals []byte from the SOAP "bin.hex" type.
func ( string) ([]byte, error) {
	return hex.DecodeString()
}

// MarshalURI marshals *url.URL to SOAP "uri" type.
func ( *url.URL) (string, error) {
	return .String(), nil
}

// UnmarshalURI unmarshals *url.URL from the SOAP "uri" type.
func ( string) (*url.URL, error) {
	return url.Parse()
}

// TypeData provides metadata about for marshalling and unmarshalling a SOAP
// type.
type TypeData struct {
	funcSuffix string
	goType     string
}

// GoTypeName returns the name of the Go type.
func ( TypeData) () string {
	return .goType
}

// MarshalFunc returns the name of the function that marshals the type.
func ( TypeData) () string {
	return fmt.Sprintf("Marshal%s", .funcSuffix)
}

// UnmarshalFunc returns the name of the function that unmarshals the type.
func ( TypeData) () string {
	return fmt.Sprintf("Unmarshal%s", .funcSuffix)
}

// TypeDataMap maps from a SOAP type (e.g "fixed.14.4") to its type data.
var TypeDataMap = map[string]TypeData{
	"ui1":         {"Ui1", "uint8"},
	"ui2":         {"Ui2", "uint16"},
	"ui4":         {"Ui4", "uint32"},
	"ui8":         {"Ui8", "uint64"},
	"i1":          {"I1", "int8"},
	"i2":          {"I2", "int16"},
	"i4":          {"I4", "int32"},
	"int":         {"Int", "int64"},
	"r4":          {"R4", "float32"},
	"r8":          {"R8", "float64"},
	"number":      {"R8", "float64"}, // Alias for r8.
	"fixed.14.4":  {"Fixed14_4", "float64"},
	"float":       {"R8", "float64"},
	"char":        {"Char", "rune"},
	"string":      {"String", "string"},
	"date":        {"Date", "time.Time"},
	"dateTime":    {"DateTime", "time.Time"},
	"dateTime.tz": {"DateTimeTz", "time.Time"},
	"time":        {"TimeOfDay", "soap.TimeOfDay"},
	"time.tz":     {"TimeOfDayTz", "soap.TimeOfDay"},
	"boolean":     {"Boolean", "bool"},
	"bin.base64":  {"BinBase64", "[]byte"},
	"bin.hex":     {"BinHex", "[]byte"},
	"uri":         {"URI", "*url.URL"},
}