/*
 * Open Chinese Convert
 *
 * Copyright 2010-2026 Carbo Kuo and contributors
 *
 * 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.
 */

#include <list>
#include <string_view>

#include "ConversionChain.hpp"
#include "Segments.hpp"

using namespace opencc;

ConversionChain::ConversionChain(const std::list<ConversionPtr> _conversions)
    : conversions(_conversions) {}

SegmentsPtr ConversionChain::Convert(const SegmentsPtr& input) const {
  SegmentsPtr output = input;
  for (auto conversion : conversions) {
    output = conversion->Convert(output);
  }
  return output;
}

void ConversionChain::AppendConvertedSegment(const char* segment,
                                             std::string* output) const {
  if (conversions.empty()) {
    output->append(segment);
    return;
  }

  auto conversion = conversions.begin();
  auto lastConversion = conversions.end();
  --lastConversion;
  if (conversion == lastConversion) {
    (*conversion)->AppendConverted(segment, output);
    return;
  }

  std::string converted;
  (*conversion)->AppendConverted(segment, &converted);
  ++conversion;
  for (; conversion != lastConversion; ++conversion) {
    std::string next;
    (*conversion)->AppendConverted(converted, &next);
    converted.swap(next);
  }
  (*lastConversion)->AppendConverted(converted, output);
}

void ConversionChain::AppendConvertedSegment(std::string_view segment,
                                             std::string* output) const {
  if (segment.empty()) {
    return;
  }
  if (conversions.empty()) {
    output->append(segment.data(), segment.size());
    return;
  }

  auto conversion = conversions.begin();
  auto lastConversion = conversions.end();
  --lastConversion;
  if (conversion == lastConversion) {
    (*conversion)->AppendConverted(segment, output);
    return;
  }

  std::string converted;
  (*conversion)->AppendConverted(segment, &converted);
  ++conversion;
  for (; conversion != lastConversion; ++conversion) {
    std::string next;
    (*conversion)->AppendConverted(std::string_view(converted), &next);
    converted.swap(next);
  }
  (*lastConversion)->AppendConverted(std::string_view(converted), output);
}

std::vector<SegmentsPtr>
ConversionChain::ConvertWithTrace(const SegmentsPtr& input) const {
  std::vector<SegmentsPtr> trace;
  trace.reserve(conversions.size());
  SegmentsPtr output = input;
  for (auto conversion : conversions) {
    output = conversion->Convert(output);
    trace.push_back(output);
  }
  return trace;
}
