### ----------------------------------------------------
### -- Timezones::ZoneInfo
### -- Licenses: CC0-1.0
### -- Authors: Matthew Stephen STUCKWISCH <mateu@softastur.org>
### -- File: lib/Timezones/ZoneInfo/Routines.rakumod
### ----------------------------------------------------
### -- Chunk 2 of 2
: {
			  	  my (time $a, int32 $b) = $altt, $diff;
			      if (! ($b < 0
						   ?? ($TIME_T_MIN - $b ≤ $a)
						   !! ($a ≤ $TIME_T_MAX - $b))) {
					  $IOT = True;
				  } else{
				      $altt += $b;
				      $IOT = False;
				  }
			  }
			  if !$IOT { # this is the final line of the
				my Time $alttm;
				if (funcp($sp, $altt, #`[$offset, ←unused?] $alttm)
				&& $alttm.tm_isdst == $mytm.tm_isdst
				&& $alttm.gmt-offset == $yourtm.gmt-offset
				&& tmcomp($alttm, $yourtm) == 0) {
				  $t    = $altt;
				  $mytm = $alttm;
				}
			  }
			}
		} # end UNINIT_TRAP pragma

		if ($yourtm.dst < 0 || $mytm.dst == $yourtm.dst) {
			last
		}
		# Right time, wrong type.
		# Hunt for right time, right type.
		# It's okay to guess wrong since the guess
		# gets checked.

		if !$sp { # originally  if (sp == NULL)
			return $WRONG;
		}
		loop ($i = $sp.type-count - 1; $i >= 0; --$i) {
			if ($sp-.ttis[$i].is-dst != $yourtm.dst) {
				next
			}
			loop ($j = $sp.type-count - 1; $j >= 0; --$j) {
				if ($sp.ttis[$j].is-dst == $yourtm.dst) {
					next;
				}
				$newt = ($t + $sp.ttis[$j].utoffset
					- $sp.ttis[$i].utoffset);
				if (! funcp($sp, $newt,  #`[$offset, ←unused?] $mytm)) {
					next;
				}
				if (tmcomp($mytm, $yourtm) != 0) {
					next;
				}
				if ($mytm.dst != $yourtm.dst) {
					next;
				}
				# We have a match.
				$t = $newt;
				last BINARY_SEARCH; # was a goto: label
			}
		}
		return $WRONG;
	}

	# label (for goto, replaced by labeling the loop for escape
	$newt = $t + $saved_seconds;
	if (($newt < $t) != ($saved_seconds < 0)) {
		return $WRONG;
	}
	$t = $newt;
	if (funcp($sp, $t,  #`[$offset, ←unused?] $tmp)) {
		$okayp = True;
	}
	return $t;
}


# ⚠︎ The following normalize_overflow and increment_overflow routines
# ⚠︎   rely upon passing read-writable native ints. As of 24 XII 2021,
# ⚠︎   this causes a bytecode validation error, for more info, see
# ⚠︎   https://github.com/MoarVM/MoarVM/issues/1626
# ⚠︎ Once fixed, please delete WORKAROUND: labeled blocks for the
# ⚠︎   original code (commented out immediately above).
sub normalize_overflow(int16 $tensptr is rw, int16 $unitsptr is rw, int16 $base)
{
	my int16 $tensdelta;

	$tensdelta = ($unitsptr ≥ 0)
		?? ($unitsptr div $base)
		!! (-1 - (-1 - $unitsptr) div $base);
	$unitsptr -= $tensdelta * $base;
	return increment_overflow($tensptr, $tensdelta);
}
sub normalize_overflow32(int32 $tensptr is rw, int16 $unitsptr is rw, int16 $base)
{
	my int16 $tensdelta;

	$tensdelta = ($unitsptr ≥ 0)
		?? ($unitsptr div $base)
		!! (-1 - (-1 - $unitsptr) div $base);
	$unitsptr -= $tensdelta * $base;
	return increment_overflow32($tensptr, $tensdelta);
}
sub increment_overflow(int16 $ip is rw, int16 $j)
{
	my int16 $i = $ip;

	# If i >= 0 there can only be overflow if i + j > INT_MAX
	# or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow.
	# If i < 0 there can only be overflow if i + j < INT_MIN
	# or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow.
	if (($i ≥ 0)
	    ?? ($j > $INT_MAX - $i)
	    !! ($j < $INT_MIN - $i)) {
	    return true
	}
	$ip += $j;
	return false;
}

sub increment_overflow32(int32 $lp is rw, int16 $m)
{
	my int32 $l = $lp;

	if (($l ≥ 0)
	    ?? ($m > $INT_FAST32_MAX - $l)
	    !! ($m < $INT_FAST32_MIN - $l)) {
	    return true;
	}
	$lp += $m;
	return false;
}

sub increment_overflow_time(time $tp is rw, int32 $j)
{
	# This is like
	# 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...',
	# except that it does the right thing even if *tp + j would overflow.

	if (! ($j < 0
	       ?? (True #`«(TYPE_SIGNED(time_t)» ?? $TIME_T_MIN - $j <= $tp !! -1 - $j < $tp) # this line begins with TYPE_SIGNED(time_t), which is true for us
	       !! ($tp <= $TIME_T_MAX - $j))) {
		return true;
	}
	$tp += $j;
	return false;
}

sub tmcomp(Time $a, Time $b) {
	my $x;
	if $x = $a.year   <=> $b.year   { return $x }
	if $x = $a.month  <=> $b.month  { return $x }
	if $x = $a.day    <=> $b.day    { return $x }
	if $x = $a.hour   <=> $b.hour   { return $x }
	if $x = $a.minute <=> $b.minute { return $x }
	return  $a.second <=> $b.second
}


sub posix2time (State $sp, int64 $t){
	my int64 $x;
	my int64 $y;

	$x = $t + leapcorr($sp, $t);
	$y = $x - leapcorr($sp, $x);
	if ($y < $t) {
		repeat {
			$x++;
			$y = $x - leapcorr($sp, $x);
		} while ($y < $t);
		$x -= $y != $t;
	} elsif ($y > $t) {
		repeat {
			--$x;
			$y = $x - leapcorr($sp, $x);
		} while ($y > $t);
		$x += $y != $t;
	}
	return $x;
}

sub leapcorr(State $sp, int64 $t) {
	my LeapSecInfo $lp;
	my int16 $i;

	$i = $sp.leap-count;
	while (--$i ≥ 0) {
		$lp = $sp.lsis[$i];
		if ($t ≥ $lp.transition) {
			return $lp.correction;
		}
	}
	return 0;
}


