// Copyright 2024 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 oldtrace

import 

type orderEvent struct {
	ev   Event
	proc *proc
}

type gStatus int

type gState struct {
	seq    uint64
	status gStatus
}

const (
	gDead gStatus = iota
	gRunnable
	gRunning
	gWaiting

	unordered = ^uint64(0)
	garbage   = ^uint64(0) - 1
	noseq     = ^uint64(0)
	seqinc    = ^uint64(0) - 1
)

// stateTransition returns goroutine state (sequence and status) when the event
// becomes ready for merging (init) and the goroutine state after the event (next).
func stateTransition( *Event) ( uint64, ,  gState) {
	// Note that we have an explicit return in each case, as that produces slightly better code (tested on Go 1.19).

	switch .Type {
	case EvGoCreate:
		 = .Args[0]
		 = gState{0, gDead}
		 = gState{1, gRunnable}
		return
	case EvGoWaiting, EvGoInSyscall:
		 = .G
		 = gState{1, gRunnable}
		 = gState{2, gWaiting}
		return
	case EvGoStart, EvGoStartLabel:
		 = .G
		 = gState{.Args[1], gRunnable}
		 = gState{.Args[1] + 1, gRunning}
		return
	case EvGoStartLocal:
		// noseq means that this event is ready for merging as soon as
		// frontier reaches it (EvGoStartLocal is emitted on the same P
		// as the corresponding EvGoCreate/EvGoUnblock, and thus the latter
		// is already merged).
		// seqinc is a stub for cases when event increments g sequence,
		// but since we don't know current seq we also don't know next seq.
		 = .G
		 = gState{noseq, gRunnable}
		 = gState{seqinc, gRunning}
		return
	case EvGoBlock, EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect,
		EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, EvGoSleep,
		EvGoSysBlock, EvGoBlockGC:
		 = .G
		 = gState{noseq, gRunning}
		 = gState{noseq, gWaiting}
		return
	case EvGoSched, EvGoPreempt:
		 = .G
		 = gState{noseq, gRunning}
		 = gState{noseq, gRunnable}
		return
	case EvGoUnblock, EvGoSysExit:
		 = .Args[0]
		 = gState{.Args[1], gWaiting}
		 = gState{.Args[1] + 1, gRunnable}
		return
	case EvGoUnblockLocal, EvGoSysExitLocal:
		 = .Args[0]
		 = gState{noseq, gWaiting}
		 = gState{seqinc, gRunnable}
		return
	case EvGCStart:
		 = garbage
		 = gState{.Args[0], gDead}
		 = gState{.Args[0] + 1, gDead}
		return
	default:
		// no ordering requirements
		 = unordered
		return
	}
}

func transitionReady( uint64, ,  gState) bool {
	return  == unordered || (.seq == noseq || .seq == .seq) && .status == .status
}

func transition( map[uint64]gState,  uint64, ,  gState) error {
	if  == unordered {
		return nil
	}
	 := []
	if !transitionReady(, , ) {
		// See comment near the call to transition, where we're building the frontier, for details on how this could
		// possibly happen.
		return errors.New("encountered impossible goroutine state transition")
	}
	switch .seq {
	case noseq:
		.seq = .seq
	case seqinc:
		.seq = .seq + 1
	}
	[] = 
	return nil
}

type orderEventList []orderEvent

func ( *orderEventList) (,  int) bool {
	return (*)[].ev.Ts < (*)[].ev.Ts
}

func ( *orderEventList) ( orderEvent) {
	* = append(*, )
	heapUp(, len(*)-1)
}

func ( *orderEventList) () orderEvent {
	 := len(*) - 1
	(*)[0], (*)[] = (*)[], (*)[0]
	heapDown(, 0, )
	 := (*)[len(*)-1]
	* = (*)[:len(*)-1]
	return 
}

func heapUp( *orderEventList,  int) {
	for {
		 := ( - 1) / 2 // parent
		if  ==  || !.Less(, ) {
			break
		}
		(*)[], (*)[] = (*)[], (*)[]
		 = 
	}
}

func heapDown( *orderEventList, ,  int) bool {
	 := 
	for {
		 := 2* + 1
		if  >=  ||  < 0 { // j1 < 0 after int overflow
			break
		}
		 :=  // left child
		if  :=  + 1;  <  && .Less(, ) {
			 =  // = 2*i + 2  // right child
		}
		if !.Less(, ) {
			break
		}
		(*)[], (*)[] = (*)[], (*)[]
		 = 
	}
	return  > 
}