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

// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include "core/fxcrt/css/cfx_cssvaluelistparser.h"

#include "core/fxcrt/check.h"
#include "core/fxcrt/check_op.h"
#include "core/fxcrt/compiler_specific.h"
#include "core/fxcrt/fx_extension.h"
#include "core/fxcrt/fx_system.h"

CFX_CSSValueListParser::CFX_CSSValueListParser(WideStringView list,
                                               wchar_t separator)
    : cur_(list), separator_(separator) {
  DCHECK(CharsRemain());
}

CFX_CSSValueListParser::~CFX_CSSValueListParser() = default;

std::optional<CFX_CSSValueListParser::Result>
CFX_CSSValueListParser::NextValue() {
  while (CharsRemain() &&
         (CurrentChar() <= ' ' || CurrentChar() == separator_)) {
    Advance();
  }
  if (!CharsRemain()) {
    return std::nullopt;
  }
  auto eType = CFX_CSSValue::PrimitiveType::kUnknown;
  WideStringView start = cur_;
  size_t nLength = 0;
  wchar_t wch = CurrentChar();
  if (wch == '#') {
    nLength = SkipToChar(' ');
    if (nLength == 4 || nLength == 7) {
      eType = CFX_CSSValue::PrimitiveType::kRGB;
    }
  } else if (FXSYS_IsDecimalDigit(wch) || wch == '.' || wch == '-' ||
             wch == '+') {
    while (CharsRemain() &&
           (CurrentChar() > ' ' && CurrentChar() != separator_)) {
      ++nLength;
      Advance();
    }
    eType = CFX_CSSValue::PrimitiveType::kNumber;
  } else if (wch == '\"' || wch == '\'') {
    start = start.Substr(1);
    Advance();
    nLength = SkipToChar(wch);
    Advance();
    eType = CFX_CSSValue::PrimitiveType::kString;
  } else if (cur_.First(4).EqualsASCIINoCase("rgb(")) {  // First() always safe.
    nLength = SkipToChar(')') + 1;
    Advance();
    eType = CFX_CSSValue::PrimitiveType::kRGB;
  } else {
    nLength = SkipToCharMatchingParens(separator_);
    eType = CFX_CSSValue::PrimitiveType::kString;
  }
  if (nLength == 0) {
    return std::nullopt;
  }
  return Result{eType, start.First(nLength)};
}

size_t CFX_CSSValueListParser::SkipToChar(wchar_t wch) {
  size_t count = 0;
  while (CharsRemain() && CurrentChar() != wch) {
    Advance();
    ++count;
  }
  return count;
}

size_t CFX_CSSValueListParser::SkipToCharMatchingParens(wchar_t wch) {
  size_t nLength = 0;
  int64_t bracketCount = 0;
  while (CharsRemain() && CurrentChar() != wch) {
    if (CurrentChar() <= ' ') {
      break;
    }
    if (CurrentChar() == '(') {
      bracketCount++;
    } else if (CurrentChar() == ')') {
      bracketCount--;
    }
    ++nLength;
    Advance();
  }
  while (bracketCount > 0 && CharsRemain()) {
    if (CurrentChar() == ')') {
      bracketCount--;
    }
    ++nLength;
    Advance();
  }
  return nLength;
}
