// Copyright 2023 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 raw

import (
	
	
	
	
	
	

	
	
)

// TextReader parses a text format trace with only very basic validation
// into an event stream.
type TextReader struct {
	v     version.Version
	specs []event.Spec
	names map[string]event.Type
	s     *bufio.Scanner
}

// NewTextReader creates a new reader for the trace text format.
func ( io.Reader) (*TextReader, error) {
	 := &TextReader{s: bufio.NewScanner()}
	,  := .nextLine()
	if  != nil {
		return nil, 
	}
	,  := readToken()
	if  != "Trace" {
		return nil, fmt.Errorf("failed to parse header")
	}
	,  := readToken()
	if !strings.HasPrefix(, "Go1.") {
		return nil, fmt.Errorf("failed to parse header Go version")
	}
	,  := strconv.ParseUint([len("Go1."):], 10, 64)
	if  != nil {
		return nil, fmt.Errorf("failed to parse header Go version: %v", )
	}
	 := version.Version()
	if !.Valid() {
		return nil, fmt.Errorf("unknown or unsupported Go version 1.%d", )
	}
	.v = 
	.specs = .Specs()
	.names = event.Names(.specs)
	for ,  := range  {
		if !unicode.IsSpace() {
			return nil, fmt.Errorf("encountered unexpected non-space at the end of the header: %q", )
		}
	}
	return , nil
}

// Version returns the version of the trace that we're reading.
func ( *TextReader) () version.Version {
	return .v
}

// ReadEvent reads and returns the next trace event in the text stream.
func ( *TextReader) () (Event, error) {
	,  := .nextLine()
	if  != nil {
		return Event{}, 
	}
	,  := readToken()
	,  := .names[]
	if ! {
		return Event{}, fmt.Errorf("unidentified event: %s", )
	}
	 := .specs[]
	,  := readArgs(, .Args)
	if  != nil {
		return Event{}, fmt.Errorf("reading args for %s: %v", , )
	}
	if .IsStack {
		 := int([1])
		for  := 0;  < ; ++ {
			,  := .nextLine()
			if  == io.EOF {
				return Event{}, fmt.Errorf("unexpected EOF while reading stack: args=%v", )
			}
			if  != nil {
				return Event{}, 
			}
			,  := readArgs(, frameFields)
			if  != nil {
				return Event{}, 
			}
			 = append(, ...)
		}
	}
	var  []byte
	if .HasData {
		,  := .nextLine()
		if  == io.EOF {
			return Event{}, fmt.Errorf("unexpected EOF while reading data for %s: args=%v", , )
		}
		if  != nil {
			return Event{}, 
		}
		,  = readData()
		if  != nil {
			return Event{}, 
		}
	}
	return Event{
		Version: .v,
		Ev:      ,
		Args:    ,
		Data:    ,
	}, nil
}

func ( *TextReader) () (string, error) {
	for {
		if !.s.Scan() {
			if  := .s.Err();  != nil {
				return "", 
			}
			return "", io.EOF
		}
		 := .s.Text()
		,  := readToken()
		if  == "" {
			continue // Empty line or comment.
		}
		return , nil
	}
}

var frameFields = []string{"pc", "func", "file", "line"}

func readArgs( string,  []string) ([]uint64, error) {
	var  []uint64
	for ,  := range  {
		, , ,  := readArg()
		if  != nil {
			return nil, 
		}
		if  !=  {
			return nil, fmt.Errorf("expected argument %q, but got %q", , )
		}
		 = append(, )
		 = 
	}
	for ,  := range  {
		if !unicode.IsSpace() {
			return nil, fmt.Errorf("encountered unexpected non-space at the end of an event: %q", )
		}
	}
	return , nil
}

func readArg( string) ( string,  uint64,  string,  error) {
	var  string
	,  = readToken()
	if len() == 0 {
		return "", 0, , fmt.Errorf("no argument")
	}
	 := strings.SplitN(, "=", 2)
	if len() < 2 {
		return "", 0, , fmt.Errorf("malformed argument: %q", )
	}
	 = [0]
	,  = strconv.ParseUint([1], 10, 64)
	if  != nil {
		return , , , fmt.Errorf("failed to parse argument value %q for arg %q", [1], [0])
	}
	return
}

func readToken( string) (,  string) {
	 := -1
	for ,  := range  {
		if  == '#' {
			return "", ""
		}
		if !unicode.IsSpace() {
			 = 
			break
		}
	}
	if  < 0 {
		return "", ""
	}
	 := -1
	for ,  := range [:] {
		if unicode.IsSpace() ||  == '#' {
			 =  + 
			break
		}
	}
	if  < 0 {
		return [:], ""
	}
	return [:], [:]
}

func readData( string) ([]byte, error) {
	 := strings.SplitN(, "=", 2)
	if len() < 2 || strings.TrimSpace([0]) != "data" {
		return nil, fmt.Errorf("malformed data: %q", )
	}
	,  := strconv.Unquote(strings.TrimSpace([1]))
	if  != nil {
		return nil, fmt.Errorf("failed to parse data: %q: %v", , )
	}
	return []byte(), nil
}