// MessagePack encoder

// Buffer for building MessagePack data

///|
priv struct Buffer {
  bytes : Array[Int]
}

///|
fn Buffer::new() -> Buffer {
  { bytes: [] }
}

///|
fn Buffer::write_byte(self : Buffer, byte : Int) -> Unit {
  self.bytes.push(byte)
}

///|
fn Buffer::write_u16(self : Buffer, value : Int) -> Unit {
  self.write_byte((value >> 8) & 0xFF)
  self.write_byte(value & 0xFF)
}

///|
fn Buffer::write_u32(self : Buffer, value : Int) -> Unit {
  self.write_byte((value >> 24) & 0xFF)
  self.write_byte((value >> 16) & 0xFF)
  self.write_byte((value >> 8) & 0xFF)
  self.write_byte(value & 0xFF)
}

///|
fn Buffer::to_bytes(self : Buffer) -> Bytes {
  let arr : Array[Byte] = []
  for i in self.bytes {
    arr.push(i.to_byte())
  }
  Bytes::from_array(arr)
}

// MessagePack encoder

///|
pub fn encode(value : Value) -> Bytes {
  let buffer = Buffer::new()
  encode_to_buffer(buffer, value)
  buffer.to_bytes()
}

///|
fn encode_to_buffer(buffer : Buffer, value : Value) -> Unit {
  match value {
    Nil => buffer.write_byte(NIL_CODE)
    Bool(true) => buffer.write_byte(TRUE_CODE)
    Bool(false) => buffer.write_byte(FALSE_CODE)
    Int(i) =>
      if i >= 0 && i <= 127 {
        // positive fixint: 0x00 - 0x7f
        buffer.write_byte(i)
      } else if i >= -32 && i < 0 {
        // negative fixint: 0xe0 - 0xff
        buffer.write_byte(NEGATIVE_FIXINT_CODE | (i & 0x1f))
      } else if i >= 128 && i <= 255 {
        // uint8
        buffer.write_byte(0xcc)
        buffer.write_byte(i)
      } else if i >= 256 && i <= 65535 {
        // uint16
        buffer.write_byte(0xcd)
        buffer.write_u16(i)
      } else if i >= 65536 {
        // uint32
        buffer.write_byte(0xce)
        buffer.write_u32(i)
      } else if i >= -128 && i < -32 {
        // int8
        buffer.write_byte(INT8_CODE)
        buffer.write_byte(i & 0xFF)
      } else if i >= -32768 && i < -128 {
        // int16
        buffer.write_byte(INT16_CODE)
        buffer.write_u16(i)
      } else {
        // int32
        buffer.write_byte(INT32_CODE)
        buffer.write_u32(i)
      }
    Int64(_i64) => {
      // For now, just encode as int32 - this is a simplification
      // In a full implementation, we'd need proper Int64 handling
      buffer.write_byte(INT32_CODE)
      buffer.write_u32(0) // placeholder
    }
    UInt64(_u64) => {
      // For now, just encode as uint32 - this is a simplification
      buffer.write_byte(UINT32_CODE)
      buffer.write_u32(0) // placeholder
    }
    Float(_f) => {
      // For now, just write the float64 code with zero bytes
      // In a full implementation, we'd need proper IEEE 754 encoding
      buffer.write_byte(FLOAT64_CODE)
      buffer.write_u32(0) // high 32 bits
      buffer.write_u32(0) // low 32 bits
    }
    String(s) => {
      let len = s.length()
      if len <= 31 {
        // fixstr
        buffer.write_byte(FIXSTR_CODE | len)
      } else if len <= 255 {
        // str8
        buffer.write_byte(STR8_CODE)
        buffer.write_byte(len)
      } else if len <= 65535 {
        // str16
        buffer.write_byte(STR16_CODE)
        buffer.write_u16(len)
      } else {
        // str32
        buffer.write_byte(STR32_CODE)
        buffer.write_u32(len)
      }
      // Write string bytes
      for i = 0; i < len; i = i + 1 {
        buffer.write_byte(s[i].to_byte().to_int())
      }
    }
    Binary(_b) => {
      // For now, just write empty binary
      buffer.write_byte(BIN8_CODE)
      buffer.write_byte(0)
    }
    Array(arr) => {
      let len = arr.length()
      if len <= 15 {
        // fixarray
        buffer.write_byte(FIXARRAY_CODE | len)
      } else if len <= 65535 {
        // array16
        buffer.write_byte(ARRAY16_CODE)
        buffer.write_u16(len)
      } else {
        // array32
        buffer.write_byte(ARRAY32_CODE)
        buffer.write_u32(len)
      }
      // Encode each element
      for i = 0; i < len; i = i + 1 {
        encode_to_buffer(buffer, arr[i])
      }
    }
    Map(map) => {
      let len = map.size()
      if len <= 15 {
        // fixmap
        buffer.write_byte(FIXMAP_CODE | len)
      } else if len <= 65535 {
        // map16
        buffer.write_byte(MAP16_CODE)
        buffer.write_u16(len)
      } else {
        // map32
        buffer.write_byte(MAP32_CODE)
        buffer.write_u32(len)
      }
      // Encode each key-value pair
      map.each(fn(key, value) {
        encode_to_buffer(buffer, String(key))
        encode_to_buffer(buffer, value)
      })
    }
  }
}