/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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 "actions/utils.h"

#include "annotator/collections.h"
#include "utils/base/logging.h"
#include "utils/normalization.h"
#include "utils/strings/stringpiece.h"

namespace libtextclassifier3 {

// Name for a datetime annotation that only includes time but no date.
const std::string& kTimeAnnotation =
    *[]() { return new std::string("time"); }();

void FillSuggestionFromSpec(const ActionSuggestionSpec* action,
                            MutableFlatbuffer* entity_data,
                            ActionSuggestion* suggestion) {
  if (action != nullptr) {
    suggestion->score = action->score();
    suggestion->priority_score = action->priority_score();
    if (action->type() != nullptr) {
      suggestion->type = action->type()->str();
    }
    if (action->response_text() != nullptr) {
      suggestion->response_text = action->response_text()->str();
    }
    if (action->serialized_entity_data() != nullptr) {
      TC3_CHECK_NE(entity_data, nullptr);
      entity_data->MergeFromSerializedFlatbuffer(
          StringPiece(action->serialized_entity_data()->data(),
                      action->serialized_entity_data()->size()));
    }
    if (action->entity_data() != nullptr) {
      TC3_CHECK_NE(entity_data, nullptr);
      entity_data->MergeFrom(
          reinterpret_cast<const flatbuffers::Table*>(action->entity_data()));
    }
  }
  if (entity_data != nullptr && entity_data->HasExplicitlySetFields()) {
    suggestion->serialized_entity_data = entity_data->Serialize();
  }
}

void SuggestTextRepliesFromCapturingMatch(
    const MutableFlatbufferBuilder* entity_data_builder,
    const RulesModel_::RuleActionSpec_::RuleCapturingGroup* group,
    const UnicodeText& match_text, const std::string& smart_reply_action_type,
    std::vector<ActionSuggestion>* actions) {
  if (group->text_reply() != nullptr) {
    ActionSuggestion suggestion;
    suggestion.response_text = match_text.ToUTF8String();
    suggestion.type = smart_reply_action_type;
    std::unique_ptr<MutableFlatbuffer> entity_data =
        entity_data_builder != nullptr ? entity_data_builder->NewRoot()
                                       : nullptr;
    FillSuggestionFromSpec(group->text_reply(), entity_data.get(), &suggestion);
    actions->push_back(suggestion);
  }
}

UnicodeText NormalizeMatchText(
    const UniLib& unilib,
    const RulesModel_::RuleActionSpec_::RuleCapturingGroup* group,
    StringPiece match_text) {
  return NormalizeMatchText(unilib, group,
                            UTF8ToUnicodeText(match_text, /*do_copy=*/false));
}

UnicodeText NormalizeMatchText(
    const UniLib& unilib,
    const RulesModel_::RuleActionSpec_::RuleCapturingGroup* group,
    const UnicodeText match_text) {
  if (group->normalization_options() == nullptr) {
    return match_text;
  }
  return NormalizeText(unilib, group->normalization_options(), match_text);
}

bool FillAnnotationFromCapturingMatch(
    const CodepointSpan& span,
    const RulesModel_::RuleActionSpec_::RuleCapturingGroup* group,
    const int message_index, StringPiece match_text,
    ActionSuggestionAnnotation* annotation) {
  if (group->annotation_name() == nullptr &&
      group->annotation_type() == nullptr) {
    return false;
  }
  annotation->span.span = span;
  annotation->span.message_index = message_index;
  annotation->span.text = match_text.ToString();
  if (group->annotation_name() != nullptr) {
    annotation->name = group->annotation_name()->str();
  }
  if (group->annotation_type() != nullptr) {
    annotation->entity.collection = group->annotation_type()->str();
  }
  return true;
}

bool MergeEntityDataFromCapturingMatch(
    const RulesModel_::RuleActionSpec_::RuleCapturingGroup* group,
    StringPiece match_text, MutableFlatbuffer* buffer) {
  if (group->entity_field() != nullptr) {
    if (!buffer->ParseAndSet(group->entity_field(), match_text.ToString())) {
      TC3_LOG(ERROR) << "Could not set entity data from rule capturing group.";
      return false;
    }
  }
  if (group->entity_data() != nullptr) {
    if (!buffer->MergeFrom(reinterpret_cast<const flatbuffers::Table*>(
            group->entity_data()))) {
      TC3_LOG(ERROR) << "Could not set entity data for capturing match.";
      return false;
    }
  }
  return true;
}

void ConvertDatetimeToTime(std::vector<AnnotatedSpan>* annotations) {
  for (int i = 0; i < annotations->size(); i++) {
    ClassificationResult* classification =
        &(*annotations)[i].classification.front();
    // Specialize datetime annotation to time annotation if no date
    // component is present.
    if (classification->collection == Collections::DateTime() &&
        classification->datetime_parse_result.IsSet()) {
      bool has_only_time = true;
      for (const DatetimeComponent& component :
           classification->datetime_parse_result.datetime_components) {
        if (component.component_type !=
                DatetimeComponent::ComponentType::UNSPECIFIED &&
            component.component_type < DatetimeComponent::ComponentType::HOUR) {
          has_only_time = false;
          break;
        }
      }
      if (has_only_time) {
        classification->collection = kTimeAnnotation;
      }
    }
  }
}

}  // namespace libtextclassifier3