// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build go1.18

package kernels

import (
	
	
	
	

	
	
	
	
)

const millisecondsInDay = 86400000

func [,  int32 | int64]( *exec.KernelCtx,  arrow.TimestampConvertOp,  int64, ,  *exec.ArraySpan) error {
	 := .State.(CastState)
	 := exec.GetSpanValues[](, 1)
	 := exec.GetSpanValues[](, 1)

	switch {
	case  == 1:
		for ,  := range  {
			[] = ()
		}
		return nil

	case  == arrow.ConvMULTIPLY:
		if .AllowTimeOverflow {
			multiplyConstant(, , )
			return nil
		}

		,  := math.MaxInt64/, math.MinInt64/
		if .Nulls != 0 && len(.Buffers[0].Buf) > 0 {
			 := bitutil.NewBitmapReader(.Buffers[0].Buf, int(.Offset), int(.Len))
			for ,  := range  {
				if .Set() && (int64() <  || int64() > ) {
					return fmt.Errorf("%w: casting from %s to %s would result in out of bounds timestamp: %v",
						arrow.ErrInvalid, .Type, .Type, )
				}
				[] = () * ()
				.Next()
			}
			return nil
		}

		for ,  := range  {
			if int64() <  || int64() >  {
				return fmt.Errorf("%w: casting from %s to %s would result in out of bounds timestamp: %v",
					arrow.ErrInvalid, .Type, .Type, )
			}
			[] = () * ()
		}
		return nil
	default:
		if .AllowTimeTruncate {
			divideConstant(, , )
			return nil
		}

		if .Nulls != 0 && len(.Buffers[0].Buf) > 0 {
			 := bitutil.NewBitmapReader(.Buffers[0].Buf, int(.Offset), int(.Len))
			for ,  := range  {
				[] = ( / ())
				if .Set() && (([])*() != ) {
					return fmt.Errorf("%w: casting from %s to %s would lose data: %v",
						arrow.ErrInvalid, .Type, .Type, )
				}
				.Next()
			}
			return nil
		}

		for ,  := range  {
			[] = ( / ())
			if ([])*() !=  {
				return fmt.Errorf("%w: casting from %s to %s would lose data: %v",
					arrow.ErrInvalid, .Type, .Type, )
			}
		}

		return nil
	}
}

func ( *exec.KernelCtx,  *exec.ExecSpan,  *exec.ExecResult) error {
	 := .Values[0].Array.Type.(*arrow.TimestampType)
	,  := .GetToTimeFunc()
	if  != nil {
		return fmt.Errorf("%w: %s", arrow.ErrInvalid, )
	}

	return ScalarUnaryNotNull(func( *exec.KernelCtx,  arrow.Timestamp,  *error) arrow.Date32 {
		 := ()
		if ,  := .Zone();  != 0 {
			// normalize the tm
			 = .Add(time.Duration() * time.Second).UTC()
		}
		return arrow.Date32FromTime()
	})(, , )
}

func ( *exec.KernelCtx,  *exec.ExecSpan,  *exec.ExecResult) error {
	 := .Values[0].Array.Type.(*arrow.TimestampType)
	,  := .GetToTimeFunc()
	if  != nil {
		return fmt.Errorf("%w: %s", arrow.ErrInvalid, )
	}

	return ScalarUnaryNotNull(func( *exec.KernelCtx,  arrow.Timestamp,  *error) arrow.Date64 {
		 := ()
		if ,  := .Zone();  != 0 {
			// normalize the tm
			 = .Add(time.Duration() * time.Second).UTC()
		}
		return arrow.Date64FromTime()
	})(, , )
}

func [,  arrow.Duration | arrow.Time32 | arrow.Time64 | arrow.Timestamp]( *exec.KernelCtx,  *exec.ExecSpan,  *exec.ExecResult) error {
	var (
		   = &.Values[0].Array
		  = .Type.(arrow.TemporalWithUnit)
		 = .Type.(arrow.TemporalWithUnit)
	)

	if .TimeUnit() == .TimeUnit() && .BitWidth() == .BitWidth() {
		 := .Type
		for  := range .Buffers {
			if .Buffers[].SelfAlloc && .Buffers[].Owner != nil {
				.Buffers[].Owner.Release()
			}
		}

		* = *
		.Type = 
		return nil
	}

	,  := arrow.GetTimestampConvert(.TimeUnit(), .TimeUnit())
	 := unsafe.Sizeof((0))
	 := unsafe.Sizeof((0))
	switch  {
	case 4:
		switch  {
		case 4:
			return ShiftTime[int32, int32](, , , , )
		default:
			return ShiftTime[int32, int64](, , , , )
		}
	default:
		switch  {
		case 4:
			return ShiftTime[int64, int32](, , , , )
		default:
			return ShiftTime[int64, int64](, , , , )
		}
	}
}

func [ int32 | int64]( *exec.KernelCtx,  *exec.ExecSpan,  *exec.ExecResult) error {
	 := .Type.(*arrow.TimestampType)
	,  := .GetZone()
	if  != nil {
		return 
	}

	 := .TimeZone != ""

	return ScalarUnaryNotNullBinaryArg[arrow.Timestamp, ](func( *exec.KernelCtx,  []byte,  *error) arrow.Timestamp {
		 := *(*string)(unsafe.Pointer(&))
		, ,  := arrow.TimestampFromStringInLocation(, .Unit, )
		if  != nil {
			* = 
		}

		if  !=  {
			if  {
				* = fmt.Errorf("%w: failed to parse string '%s' as a value of type %s,"+
					"expected a zone offset. If these timestamps are in local time, cast to timestamp without timezone",
					arrow.ErrInvalid, , )
			} else {
				* = fmt.Errorf("%w: failed to parse string '%s' as a value of type %s, expected no zone offset",
					arrow.ErrInvalid, , )
			}
		}

		return 
	})(, , )
}

func ( *exec.KernelCtx,  *exec.ExecSpan,  *exec.ExecResult) error {
	var (
		  = .Values[0].Type().(*arrow.TimestampType)
		 = .Type.(*arrow.Time32Type)
		    = .State.(CastState)
	)

	,  := .GetToTimeFunc()
	if  != nil {
		return fmt.Errorf("%w: %s", arrow.ErrInvalid, )
	}

	if .TimeZone != "" && .TimeZone != "UTC" {
		 := 
		 = func( arrow.Timestamp) time.Time {
			 := ()
			,  := .Zone()
			return .Add(time.Duration() * time.Second).UTC()
		}
	}

	var  func(time.Duration, *error) arrow.Time32
	switch .Unit {
	case arrow.Second:
		 = func( time.Duration,  *error) arrow.Time32 {
			return arrow.Time32(.Seconds())
		}
	case arrow.Millisecond:
		 = func( time.Duration,  *error) arrow.Time32 {
			return arrow.Time32(.Milliseconds())
		}
	default:
		return fmt.Errorf("%w: bad unit type for cast to time32: %s",
			arrow.ErrInvalid, .Unit)
	}

	,  := arrow.GetTimestampConvert(.Unit, .Unit)
	if  == arrow.ConvDIVIDE && !.AllowTimeTruncate {
		 := 
		switch .Unit {
		case arrow.Millisecond:
			 = func( time.Duration,  *error) arrow.Time32 {
				 := (, )
				if int64()* != .Milliseconds() {
					* = fmt.Errorf("%w: cast would lose data: %d", arrow.ErrInvalid, .Milliseconds())
				}
				return 
			}
		case arrow.Microsecond:
			 = func( time.Duration,  *error) arrow.Time32 {
				 := (, )
				if int64()* != .Microseconds() {
					* = fmt.Errorf("%w: cast would lose data: %d", arrow.ErrInvalid, .Microseconds())
				}
				return 
			}
		case arrow.Nanosecond:
			 = func( time.Duration,  *error) arrow.Time32 {
				 := (, )
				if int64()* != .Nanoseconds() {
					* = fmt.Errorf("%w: cast would lose data: %d", arrow.ErrInvalid, .Nanoseconds())
				}
				return 
			}
		}
	}

	return ScalarUnaryNotNull(func( *exec.KernelCtx,  arrow.Timestamp,  *error) arrow.Time32 {
		 := ()
		 := .Sub(.Truncate(24 * time.Hour))
		return (, )
	})(, , )
}

func ( *exec.KernelCtx,  *exec.ExecSpan,  *exec.ExecResult) error {
	var (
		  = .Values[0].Type().(*arrow.TimestampType)
		 = .Type.(*arrow.Time64Type)
		    = .State.(CastState)
	)

	,  := .GetToTimeFunc()
	if  != nil {
		return fmt.Errorf("%w: %s", arrow.ErrInvalid, )
	}

	if .TimeZone != "" && .TimeZone != "UTC" {
		 := 
		 = func( arrow.Timestamp) time.Time {
			 := ()
			,  := .Zone()
			return .Add(time.Duration() * time.Second).UTC()
		}
	}

	var  func(time.Duration, *error) arrow.Time64
	,  := arrow.GetTimestampConvert(.Unit, .Unit)
	if  == arrow.ConvDIVIDE && !.AllowTimeTruncate {
		// only one case can happen here, microseconds. nanoseconds
		// wouldn't be a downscale
		 = func( time.Duration,  *error) arrow.Time64 {
			if .Nanoseconds() != .Microseconds()*int64(time.Microsecond) {
				* = fmt.Errorf("%w: cast would lose data: %d", arrow.ErrInvalid, .Nanoseconds())
			}
			return arrow.Time64(.Microseconds())
		}
	} else {
		switch .Unit {
		case arrow.Microsecond:
			 = func( time.Duration,  *error) arrow.Time64 {
				return arrow.Time64(.Microseconds())
			}
		case arrow.Nanosecond:
			 = func( time.Duration,  *error) arrow.Time64 {
				return arrow.Time64(.Nanoseconds())
			}
		default:
			return fmt.Errorf("%w: bad unit type for cast to time64: %s",
				arrow.ErrInvalid, .Unit)
		}
	}

	return ScalarUnaryNotNull(func( *exec.KernelCtx,  arrow.Timestamp,  *error) arrow.Time64 {
		 := ()
		 := .Sub(.Truncate(24 * time.Hour))
		return (, )
	})(, , )
}

func () []exec.ScalarKernel {
	 := exec.NewOutputType(arrow.FixedWidthTypes.Date32)
	 := GetCommonCastKernels(arrow.DATE32, )
	 = append(, GetZeroCastKernel(arrow.INT32, exec.NewExactInput(arrow.PrimitiveTypes.Int32), ))

	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewExactInput(arrow.FixedWidthTypes.Date64)}, ,
		func( *exec.KernelCtx,  *exec.ExecSpan,  *exec.ExecResult) error {
			return ShiftTime[int64, int32](, arrow.ConvDIVIDE, millisecondsInDay, &.Values[0].Array, )
		}, nil))

	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewIDInput(arrow.TIMESTAMP)}, ,
		TimestampToDate32, nil))

	return 
}

func () []exec.ScalarKernel {
	 := exec.NewOutputType(arrow.FixedWidthTypes.Date64)
	 := GetCommonCastKernels(arrow.DATE64, )
	 = append(, GetZeroCastKernel(arrow.INT64, exec.NewExactInput(arrow.PrimitiveTypes.Int64), ))

	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewExactInput(arrow.FixedWidthTypes.Date32)}, ,
		func( *exec.KernelCtx,  *exec.ExecSpan,  *exec.ExecResult) error {
			return ShiftTime[int32, int64](, arrow.ConvMULTIPLY, millisecondsInDay, &.Values[0].Array, )
		}, nil))

	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewIDInput(arrow.TIMESTAMP)}, ,
		TimestampToDate64, nil))
	return 
}

func () []exec.ScalarKernel {
	 := GetCommonCastKernels(arrow.TIME32, OutputTargetType)
	 = append(, GetZeroCastKernel(arrow.INT32, exec.NewExactInput(arrow.PrimitiveTypes.Int32), OutputTargetType))

	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewIDInput(arrow.TIME64)}, OutputTargetType,
		SimpleTemporalCast[arrow.Time64, arrow.Time32], nil))
	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewIDInput(arrow.TIME32)}, OutputTargetType,
		SimpleTemporalCast[arrow.Time32, arrow.Time32], nil))
	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewIDInput(arrow.TIMESTAMP)}, OutputTargetType,
		TimestampToTime32, nil))

	return 
}

func () []exec.ScalarKernel {
	 := GetCommonCastKernels(arrow.TIME64, OutputTargetType)
	 = append(, GetZeroCastKernel(arrow.INT64, exec.NewExactInput(arrow.PrimitiveTypes.Int64), OutputTargetType))

	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewIDInput(arrow.TIME64)}, OutputTargetType,
		SimpleTemporalCast[arrow.Time64, arrow.Time64], nil))
	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewIDInput(arrow.TIME32)}, OutputTargetType,
		SimpleTemporalCast[arrow.Time32, arrow.Time64], nil))
	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewIDInput(arrow.TIMESTAMP)}, OutputTargetType,
		TimestampToTime64, nil))

	return 
}

func () []exec.ScalarKernel {
	 := GetCommonCastKernels(arrow.DURATION, OutputTargetType)
	 = append(, GetZeroCastKernel(arrow.INT64,
		exec.NewExactInput(arrow.PrimitiveTypes.Int64), OutputTargetType))

	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewIDInput(arrow.DURATION)}, OutputTargetType,
		SimpleTemporalCast[arrow.Duration, arrow.Duration], nil))
	return 
}

func () []exec.ScalarKernel {
	return GetCommonCastKernels(arrow.INTERVAL_MONTH_DAY_NANO, OutputTargetType)
}

func () []exec.ScalarKernel {
	 := GetCommonCastKernels(arrow.TIMESTAMP, OutputTargetType)

	// same integer representation
	 = append(, GetZeroCastKernel(arrow.INT64, exec.NewExactInput(arrow.PrimitiveTypes.Int64), OutputTargetType))
	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewIDInput(arrow.DATE32)}, OutputTargetType,
		func( *exec.KernelCtx,  *exec.ExecSpan,  *exec.ExecResult) error {
			,  := arrow.GetTimestampConvert(arrow.Second, .Type.(arrow.TemporalWithUnit).TimeUnit())
			debug.Assert( == arrow.ConvMULTIPLY, "date32 -> timestamp should be multiply operation")

			// multiply to achieve days -> unit
			 *= millisecondsInDay / 1000
			return ShiftTime[int32, int64](, , , &.Values[0].Array, )
		}, nil))
	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewIDInput(arrow.DATE64)}, OutputTargetType,
		func( *exec.KernelCtx,  *exec.ExecSpan,  *exec.ExecResult) error {
			// date64 is ms since epoch
			,  := arrow.GetTimestampConvert(arrow.Millisecond, .Type.(arrow.TemporalWithUnit).TimeUnit())
			debug.Assert( == arrow.ConvMULTIPLY, "date64 -> timestamp should be multiply operation")

			return ShiftTime[int64, int64](, , , &.Values[0].Array, )
		}, nil))

	// string -> timestamp
	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewExactInput(arrow.BinaryTypes.String)}, OutputTargetType,
		StringToTimestamp[int32], nil))
	// large_string -> timestamp
	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewExactInput(arrow.BinaryTypes.LargeString)}, OutputTargetType,
		StringToTimestamp[int64], nil))
	// from one timestamp to another
	 = append(, exec.NewScalarKernel(
		[]exec.InputType{exec.NewIDInput(arrow.TIMESTAMP)}, OutputTargetType,
		SimpleTemporalCast[arrow.Timestamp, arrow.Timestamp], nil))
	return 
}