// Copyright 2025 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.

///|
pub struct Decompressor {
  mut input_buffer : Array[Byte]
  dictionary : Bytes
}

///|
pub fn new_decompressor() -> Decompressor {
  { input_buffer: Array::new(), dictionary: b"" }
}

///|
pub fn new_decompressor_with_dictionary(dictionary : Bytes) -> Decompressor {
  { input_buffer: Array::new(), dictionary }
}

///|
pub fn Decompressor::push(self : Decompressor, chunk : Bytes) -> Unit {
  append_bytes(self.input_buffer, chunk, 0, chunk.length())
}

///|
pub fn Decompressor::pending_input(self : Decompressor) -> Int {
  self.input_buffer.length()
}

///|
pub fn Decompressor::pull(self : Decompressor) -> Bytes raise ZstdError {
  self.drain_complete_frames(false)
}

///|
pub fn Decompressor::finish(self : Decompressor) -> Bytes raise ZstdError {
  self.drain_complete_frames(true)
}

///|
fn Decompressor::drain_complete_frames(
  self : Decompressor,
  require_complete : Bool,
) -> Bytes raise ZstdError {
  if self.input_buffer.length() == 0 {
    return b""
  }

  let src = Bytes::from_array(self.input_buffer)
  let src_len = src.length()
  let out : Array[Byte] = Array::new()
  let mut pos = 0

  while pos < src_len {
    let result = try
      decode_frame_into_with_dictionary(src, pos, out, self.dictionary)
    catch {
      e => Err(e)
    } noraise {
      value => Ok(value)
    }
    match result {
      Ok(next) => {
        if next <= pos {
          raise CorruptionDetected
        }
        pos = next
      }
      Err(SrcSizeWrong) => {
        if require_complete {
          raise SrcSizeWrong
        }
        break
      }
      Err(CorruptionDetected) => raise CorruptionDetected
      Err(BoundOverflow) => raise BoundOverflow
      Err(SrcSizeTooLarge(size)) => raise SrcSizeTooLarge(size)
      Err(DictionaryRequired(dict_id)) => raise DictionaryRequired(dict_id)
      Err(UnsupportedFeature(msg)) => raise UnsupportedFeature(msg)
    }
  }

  if pos > 0 {
    let next_buffer : Array[Byte] = Array::new()
    append_bytes(next_buffer, src, pos, src_len - pos)
    self.input_buffer = next_buffer
  } else if require_complete && src_len > 0 {
    raise SrcSizeWrong
  }

  Bytes::from_array(out)
}