// Copyright 2013 The Chromium 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 "components/autofill/core/common/form_data.h"

#include <stddef.h>
#include <tuple>

#include "base/base64.h"
#include "base/pickle.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/common/form_field_data.h"

namespace autofill {

namespace {

const int kPickleVersion = 6;

bool ReadGURL(base::PickleIterator* iter, GURL* url) {
  std::string spec;
  if (!iter->ReadString(&spec))
    return false;

  *url = GURL(spec);
  return true;
}

bool ReadOrigin(base::PickleIterator* iter, url::Origin* origin) {
  std::string spec;
  if (!iter->ReadString(&spec))
    return false;

  *origin = url::Origin::Create(GURL(spec));
  return true;
}

void SerializeFormFieldDataVector(const std::vector<FormFieldData>& fields,
                                  base::Pickle* pickle) {
  pickle->WriteInt(static_cast<int>(fields.size()));
  for (size_t i = 0; i < fields.size(); ++i) {
    SerializeFormFieldData(fields[i], pickle);
  }
}

bool DeserializeFormFieldDataVector(base::PickleIterator* iter,
                                    std::vector<FormFieldData>* fields) {
  int size;
  if (!iter->ReadInt(&size))
    return false;

  FormFieldData temp;
  for (int i = 0; i < size; ++i) {
    if (!DeserializeFormFieldData(iter, &temp))
      return false;

    fields->push_back(temp);
  }
  return true;
}

void LogDeserializationError(int version) {
  DVLOG(1) << "Could not deserialize version " << version
           << " FormData from pickle.";
}

}  // namespace

FormData::FormData() : is_form_tag(true), is_formless_checkout(false) {}

FormData::FormData(const FormData& data)
    : name(data.name),
      origin(data.origin),
      action(data.action),
      main_frame_origin(data.main_frame_origin),
      is_form_tag(data.is_form_tag),
      is_formless_checkout(data.is_formless_checkout),
      unique_renderer_id(data.unique_renderer_id),
      fields(data.fields),
      username_predictions(data.username_predictions) {}

FormData::~FormData() {
}

bool FormData::SameFormAs(const FormData& form) const {
  if (name != form.name || origin != form.origin || action != form.action ||
      is_form_tag != form.is_form_tag ||
      is_formless_checkout != form.is_formless_checkout ||
      fields.size() != form.fields.size())
    return false;
  for (size_t i = 0; i < fields.size(); ++i) {
    if (!fields[i].SameFieldAs(form.fields[i]))
      return false;
  }
  return true;
}

bool FormData::SimilarFormAs(const FormData& form) const {
  if (name != form.name || origin != form.origin || action != form.action ||
      is_form_tag != form.is_form_tag ||
      is_formless_checkout != form.is_formless_checkout ||
      fields.size() != form.fields.size())
    return false;
  for (size_t i = 0; i < fields.size(); ++i) {
    if (!fields[i].SimilarFieldAs(form.fields[i]))
      return false;
  }
  return true;
}

bool FormData::DynamicallySameFormAs(const FormData& form) const {
  if (name != form.name || fields.size() != form.fields.size())
    return false;
  for (size_t i = 0; i < fields.size(); ++i) {
    if (!fields[i].DynamicallySameFieldAs(form.fields[i]))
      return false;
  }
  return true;
}

bool FormData::operator==(const FormData& form) const {
  return name == form.name && origin == form.origin && action == form.action &&
         unique_renderer_id == form.unique_renderer_id &&
         is_form_tag == form.is_form_tag &&
         is_formless_checkout == form.is_formless_checkout &&
         fields == form.fields &&
         username_predictions == form.username_predictions;
}

bool FormData::operator!=(const FormData& form) const {
  return !(*this == form);
}

bool FormData::operator<(const FormData& form) const {
  return std::tie(name, origin, action, is_form_tag, is_formless_checkout,
                  fields) < std::tie(form.name, form.origin, form.action,
                                     form.is_form_tag,
                                     form.is_formless_checkout, form.fields);
}

std::ostream& operator<<(std::ostream& os, const FormData& form) {
  os << base::UTF16ToUTF8(form.name) << " " << form.origin << " " << form.action
     << " " << form.main_frame_origin << " " << form.is_form_tag << " "
     << form.is_formless_checkout << " "
     << "Fields:";
  for (size_t i = 0; i < form.fields.size(); ++i) {
    os << form.fields[i] << ",";
  }
  return os;
}

void SerializeFormData(const FormData& form_data, base::Pickle* pickle) {
  pickle->WriteInt(kPickleVersion);
  pickle->WriteString16(form_data.name);
  pickle->WriteString(form_data.origin.spec());
  pickle->WriteString(form_data.action.spec());
  SerializeFormFieldDataVector(form_data.fields, pickle);
  pickle->WriteBool(form_data.is_form_tag);
  pickle->WriteBool(form_data.is_formless_checkout);
  pickle->WriteString(form_data.main_frame_origin.Serialize());
}

void SerializeFormDataToBase64String(const FormData& form_data,
                                     std::string* output) {
  base::Pickle pickle;
  SerializeFormData(form_data, &pickle);
  Base64Encode(
      base::StringPiece(static_cast<const char*>(pickle.data()), pickle.size()),
      output);
}

bool DeserializeFormData(base::PickleIterator* iter, FormData* form_data) {
  int version;
  FormData temp_form_data;
  if (!iter->ReadInt(&version)) {
    DVLOG(1) << "Bad pickle of FormData, no version present";
    return false;
  }

  if (version < 1 || version > kPickleVersion) {
    DVLOG(1) << "Unknown FormData pickle version " << version;
    return false;
  }

  if (!iter->ReadString16(&temp_form_data.name)) {
    LogDeserializationError(version);
    return false;
  }

  if (version == 1) {
    base::string16 method;
    if (!iter->ReadString16(&method)) {
      LogDeserializationError(version);
      return false;
    }
  }

  bool unused_user_submitted;
  if (!ReadGURL(iter, &temp_form_data.origin) ||
      !ReadGURL(iter, &temp_form_data.action) ||
      // user_submitted was removed/no longer serialized in version 4.
      (version < 4 && !iter->ReadBool(&unused_user_submitted)) ||
      !DeserializeFormFieldDataVector(iter, &temp_form_data.fields)) {
    LogDeserializationError(version);
    return false;
  }

  if (version >= 3) {
    if (!iter->ReadBool(&temp_form_data.is_form_tag)) {
      LogDeserializationError(version);
      return false;
    }
  } else {
    form_data->is_form_tag = true;
  }

  if (version >= 5) {
    if (!iter->ReadBool(&temp_form_data.is_formless_checkout)) {
      LogDeserializationError(version);
      return false;
    }
  }

  if (version >= 6) {
    if (!ReadOrigin(iter, &temp_form_data.main_frame_origin)) {
      LogDeserializationError(version);
      return false;
    }
  }

  *form_data = temp_form_data;
  return true;
}

bool DeserializeFormDataFromBase64String(const base::StringPiece& input,
                                         FormData* form_data) {
  if (input.empty())
    return false;
  std::string pickle_data;
  Base64Decode(input, &pickle_data);
  base::Pickle pickle(pickle_data.data(), static_cast<int>(pickle_data.size()));
  base::PickleIterator iter(pickle);
  return DeserializeFormData(&iter, form_data);
}

}  // namespace autofill
