// Copyright 2026 International Digital Economy Academy
//
// Licensed 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.

///|
fn u8_lane(value : V128, lane : Int) -> Byte {
  let word = if lane < 8 { lo(value) } else { hi(value) }
  ((word >> (lane % 8 * 8)) & 0xffUL).to_byte()
}

///|
fn u16_lane(value : V128, lane : Int) -> UInt16 {
  let word = if lane < 4 { lo(value) } else { hi(value) }
  ((word >> (lane % 4 * 16)) & 0xffffUL).to_uint16()
}

///|
fn u32_lane(value : V128, lane : Int) -> UInt {
  let word = if lane < 2 { lo(value) } else { hi(value) }
  ((word >> (lane % 2 * 32)) & 0xffffffffUL).to_uint()
}

///|
fn u64_lane(value : V128, lane : Int) -> UInt64 {
  if lane == 0 {
    lo(value)
  } else {
    hi(value)
  }
}

///|
fn pack_u8x16_by(f : (Int) -> Byte) -> V128 {
  let mut lo_word = 0UL
  for i in 0..<8 {
    lo_word = lo_word | (f(i).to_uint64() << (i * 8))
  }
  let mut hi_word = 0UL
  for i in 0..<8 {
    hi_word = hi_word | (f(i + 8).to_uint64() << (i * 8))
  }
  make(lo_word, hi_word)
}

///|
fn pack_u16x8_by(f : (Int) -> UInt16) -> V128 {
  let mut lo_word = 0UL
  for i in 0..<4 {
    lo_word = lo_word | (f(i).to_uint64() << (i * 16))
  }
  let mut hi_word = 0UL
  for i in 0..<4 {
    hi_word = hi_word | (f(i + 4).to_uint64() << (i * 16))
  }
  make(lo_word, hi_word)
}

///|
fn pack_u32x4_by(f : (Int) -> UInt) -> V128 {
  make(
    f(0).to_uint64() | (f(1).to_uint64() << 32),
    f(2).to_uint64() | (f(3).to_uint64() << 32),
  )
}

///|
fn pack_u64x2_by(f : (Int) -> UInt64) -> V128 {
  make(f(0), f(1))
}

///|
fn widen_u8x4_to_u16x4(value : UInt64) -> UInt64 {
  (value & 0xffUL) |
  ((value & 0xff00UL) << 8) |
  ((value & 0xff0000UL) << 16) |
  ((value & 0xff000000UL) << 24)
}

///|
fn sign_extend_u8x4_to_u16x4(value : UInt64) -> UInt64 {
  let value = widen_u8x4_to_u16x4(value)
  value | (((value & 0x0080008000800080UL) << 1) * 0xffUL)
}

///|
fn widen_u16x2_to_u32x2(value : UInt64) -> UInt64 {
  (value & 0xffffUL) | ((value & 0xffff0000UL) << 16)
}

///|
fn sign_extend_u16x2_to_u32x2(value : UInt64) -> UInt64 {
  let value = widen_u16x2_to_u32x2(value)
  value | (((value & 0x0000800000008000UL) << 1) * 0xffffUL)
}

///|
fn sign_extend_u32_to_u64(value : UInt64) -> UInt64 {
  let value = value & 0xffffffffUL
  if (value & 0x80000000UL) != 0UL {
    value | 0xffffffff00000000UL
  } else {
    value
  }
}

///|
fn i8_signed(value : Byte) -> Int {
  let value = value.to_int()
  if value >= 128 {
    value - 256
  } else {
    value
  }
}

///|
fn i16_signed(value : UInt16) -> Int {
  Int16::reinterpret_from_uint16(value).to_int()
}

///|
fn bool_to_u8_mask(value : Bool) -> Byte {
  if value {
    0xff
  } else {
    0
  }
}

///|
fn bool_to_u16_mask(value : Bool) -> UInt16 {
  if value {
    0xffff
  } else {
    0
  }
}

///|
fn bool_to_u32_mask(value : Bool) -> UInt {
  if value {
    0xffffffff
  } else {
    0
  }
}

///|
fn bool_to_u64_mask(value : Bool) -> UInt64 {
  if value {
    0xffffffffffffffffUL
  } else {
    0UL
  }
}

///|
fn i8_abs(value : Byte) -> Byte {
  let value = i8_signed(value)
  if value < 0 {
    (-value).to_byte()
  } else {
    value.to_byte()
  }
}

///|
fn i16_abs(value : UInt16) -> UInt16 {
  Int16::abs(Int16::reinterpret_from_uint16(value)).reinterpret_as_uint16()
}

///|
fn i8_add_sat_s(a : Byte, b : Byte) -> Byte {
  let sum = i8_signed(a) + i8_signed(b)
  if sum > 127 {
    0x7f
  } else if sum < -128 {
    0x80
  } else {
    sum.to_byte()
  }
}

///|
fn i8_add_sat_u(a : Byte, b : Byte) -> Byte {
  let sum = a.to_uint() + b.to_uint()
  if sum > 255 {
    0xff
  } else {
    sum.to_byte()
  }
}

///|
fn i8_sub_sat_s(a : Byte, b : Byte) -> Byte {
  let diff = i8_signed(a) - i8_signed(b)
  if diff > 127 {
    0x7f
  } else if diff < -128 {
    0x80
  } else {
    diff.to_byte()
  }
}

///|
fn i8_sub_sat_u(a : Byte, b : Byte) -> Byte {
  if a < b {
    0
  } else {
    a - b
  }
}

///|
fn i8_min_s(a : Byte, b : Byte) -> Byte {
  if i8_signed(a) < i8_signed(b) {
    a
  } else {
    b
  }
}

///|
fn i8_max_s(a : Byte, b : Byte) -> Byte {
  if i8_signed(a) > i8_signed(b) {
    a
  } else {
    b
  }
}

///|
fn i8_avgr_u(a : Byte, b : Byte) -> Byte {
  ((a.to_uint() + b.to_uint() + 1) / 2).to_byte()
}

///|
fn i16_add_sat_s(a : UInt16, b : UInt16) -> UInt16 {
  let sum = i16_signed(a) + i16_signed(b)
  if sum > 32767 {
    0x7fff
  } else if sum < -32768 {
    0x8000
  } else {
    sum.to_uint16()
  }
}

///|
fn i16_add_sat_u(a : UInt16, b : UInt16) -> UInt16 {
  let sum = a.to_uint() + b.to_uint()
  if sum > 65535 {
    0xffff
  } else {
    sum.to_uint16()
  }
}

///|
fn i16_sub_sat_s(a : UInt16, b : UInt16) -> UInt16 {
  let diff = i16_signed(a) - i16_signed(b)
  if diff > 32767 {
    0x7fff
  } else if diff < -32768 {
    0x8000
  } else {
    diff.to_uint16()
  }
}

///|
fn i16_sub_sat_u(a : UInt16, b : UInt16) -> UInt16 {
  if a < b {
    0
  } else {
    a - b
  }
}

///|
fn i16_min_s(a : UInt16, b : UInt16) -> UInt16 {
  if i16_signed(a) < i16_signed(b) {
    a
  } else {
    b
  }
}

///|
fn i16_max_s(a : UInt16, b : UInt16) -> UInt16 {
  if i16_signed(a) > i16_signed(b) {
    a
  } else {
    b
  }
}

///|
fn i16_avgr_u(a : UInt16, b : UInt16) -> UInt16 {
  ((a.to_uint() + b.to_uint() + 1) / 2).to_uint16()
}