// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/json/values_util.h"

#include <optional>

#include "base/files/file_path.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"

// Warning: The Values involved could be stored on persistent storage like files
// on disks. Therefore, changes in implementation could lead to data corruption
// and must be done with caution.

namespace base {

namespace {

// Helper to serialize/deserialize an UnguessableToken.
//
// It assumes a little-endian CPU, which is arguably a bug.
union UnguessableTokenRepresentation {
  struct Field {
    uint64_t high;
    uint64_t low;
  } field;

  uint8_t buffer[sizeof(Field)];
};

}  // namespace

Value Int64ToValue(int64_t integer) {
  return Value(NumberToString(integer));
}

std::optional<int64_t> ValueToInt64(const Value* value) {
  return value ? ValueToInt64(*value) : std::nullopt;
}

std::optional<int64_t> ValueToInt64(const Value& value) {
  if (!value.is_string())
    return std::nullopt;

  int64_t integer;
  if (!StringToInt64(value.GetString(), &integer))
    return std::nullopt;

  return integer;
}

Value TimeDeltaToValue(TimeDelta time_delta) {
  return Int64ToValue(time_delta.InMicroseconds());
}

std::optional<TimeDelta> ValueToTimeDelta(const Value* value) {
  return value ? ValueToTimeDelta(*value) : std::nullopt;
}

std::optional<TimeDelta> ValueToTimeDelta(const Value& value) {
  std::optional<int64_t> integer = ValueToInt64(value);
  if (!integer)
    return std::nullopt;
  return Microseconds(*integer);
}

Value TimeToValue(Time time) {
  return TimeDeltaToValue(time.ToDeltaSinceWindowsEpoch());
}

std::optional<Time> ValueToTime(const Value* value) {
  return value ? ValueToTime(*value) : std::nullopt;
}

std::optional<Time> ValueToTime(const Value& value) {
  std::optional<TimeDelta> time_delta = ValueToTimeDelta(value);
  if (!time_delta)
    return std::nullopt;
  return Time::FromDeltaSinceWindowsEpoch(*time_delta);
}

Value FilePathToValue(const FilePath& file_path) {
  return Value(file_path.AsUTF8Unsafe());
}

std::optional<FilePath> ValueToFilePath(const Value* value) {
  return value ? ValueToFilePath(*value) : std::nullopt;
}

std::optional<FilePath> ValueToFilePath(const Value& value) {
  if (!value.is_string())
    return std::nullopt;
  return FilePath::FromUTF8Unsafe(value.GetString());
}

Value UnguessableTokenToValue(UnguessableToken token) {
  UnguessableTokenRepresentation repr;
  repr.field.high = token.GetHighForSerialization();
  repr.field.low = token.GetLowForSerialization();
  return Value(HexEncode(repr.buffer, sizeof(repr.buffer)));
}

std::optional<UnguessableToken> ValueToUnguessableToken(const Value* value) {
  return value ? ValueToUnguessableToken(*value) : std::nullopt;
}

std::optional<UnguessableToken> ValueToUnguessableToken(const Value& value) {
  if (!value.is_string())
    return std::nullopt;
  UnguessableTokenRepresentation repr;
  if (!HexStringToSpan(value.GetString(), repr.buffer))
    return std::nullopt;
  std::optional<base::UnguessableToken> token =
      UnguessableToken::Deserialize(repr.field.high, repr.field.low);
  if (!token.has_value()) {
    return std::nullopt;
  }
  return token;
}

}  // namespace base
