// Copyright 2025 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/objects/js-temporal-zoneinfo64.h"

#include <iostream>

#include "src/base/logging.h"
#include "temporal_rs/Provider.hpp"
#include "temporal_rs/TimeZone.hpp"

#ifdef V8_INTL_SUPPORT
#include "udatamem.h"
#else
// Defined in builtins-temporal-zoneinfo64-data.cc, generated by
// include-file-as-bytes.py
extern "C" uint32_t zoneinfo64_static_data[];
extern "C" size_t zoneinfo64_static_data_len;
#endif

namespace v8::internal {

ZoneInfo64Provider::ZoneInfo64Provider() {
#ifdef V8_INTL_SUPPORT
  UErrorCode status = U_ZERO_ERROR;
  memory = udata_open(0, "res", "zoneinfo64", &status);
  if (U_FAILURE(status)) {
    DCHECK_WITH_MSG(false, "Failed to open zoneinfo64.res");
    provider = temporal_rs::Provider::empty();
    return;
  }
  // NOT udata_getLength: this ignores the header,
  // and we're parsing resb files with the header
  auto length = memory->length;
  const void* data = udata_getRawMemory(memory);
  DCHECK_WITH_MSG(length % 4 == 0, "ICU4C should align udata to uint32_t");
  if (length % 4 != 0) {
    // This really shouldn't happen: ICU4C aligns these files
    // to 4 when baking them in
    provider = temporal_rs::Provider::empty();
    return;
  }

  const uint32_t* data_32 = static_cast<const uint32_t*>(data);
  std::span<const uint32_t> data_span(data_32, length / 4);

#else
  std::span<const uint32_t> data_span(zoneinfo64_static_data,
                                      zoneinfo64_static_data_len);
#endif

  auto result = temporal_rs::Provider::new_zoneinfo64(data_span);
  DCHECK_WITH_MSG(result.is_ok(), "Baked-in zoneinfo64 file must parse");
  if (result.is_ok()) {
    provider = std::move(result).ok().value();
  } else {
    provider = temporal_rs::Provider::empty();
  }
}

ZoneInfo64Provider& ZoneInfo64Provider::Singleton() {
  // Allocate it so that it gets leaked (-Wno-exit-destructors)
  static ZoneInfo64Provider* SINGLETON = new ZoneInfo64Provider();
  return *SINGLETON;
}

// This destructor exists for completeness in case someone decides to use this
// as a stack value, though the default way of accessing this is via singleton.
ZoneInfo64Provider::~ZoneInfo64Provider() {
  // Release provider first since it
  // may reference memory
  provider = nullptr;

  // Then clean up memory
  // This ideally is a no-op when using static data
#ifdef V8_INTL_SUPPORT
  if (memory) {
    udata_close(memory);
  }
#endif
}

}  // namespace v8::internal
