// Copyright (c) 2010 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 "chrome/browser/autofill/contact_info.h"

#include "base/basictypes.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/autofill/autofill_type.h"
#include "chrome/browser/autofill/field_types.h"

static const string16 kNameSplitChars = ASCIIToUTF16("-'. ");

static const AutoFillFieldType kAutoFillContactInfoTypes[] = {
  NAME_FIRST,
  NAME_MIDDLE,
  NAME_LAST,
  EMAIL_ADDRESS,
  COMPANY_NAME,
};

static const size_t kAutoFillContactInfoLength =
    arraysize(kAutoFillContactInfoTypes);

ContactInfo::ContactInfo() {}

ContactInfo::~ContactInfo() {}

FormGroup* ContactInfo::Clone() const {
  return new ContactInfo(*this);
}

void ContactInfo::GetPossibleFieldTypes(const string16& text,
                                        FieldTypeSet* possible_types) const {
  DCHECK(possible_types);

  if (IsFirstName(text))
    possible_types->insert(NAME_FIRST);

  if (IsMiddleName(text))
    possible_types->insert(NAME_MIDDLE);

  if (IsLastName(text))
    possible_types->insert(NAME_LAST);

  if (IsMiddleInitial(text))
    possible_types->insert(NAME_MIDDLE_INITIAL);

  if (IsSuffix(text))
    possible_types->insert(NAME_SUFFIX);

  if (IsFullName(text))
    possible_types->insert(NAME_FULL);

  if (email_ == text)
    possible_types->insert(EMAIL_ADDRESS);

  if (company_name_ == text)
    possible_types->insert(COMPANY_NAME);
}

void ContactInfo::GetAvailableFieldTypes(FieldTypeSet* available_types) const {
  DCHECK(available_types);

  if (!first().empty())
    available_types->insert(NAME_FIRST);

  if (!middle().empty())
    available_types->insert(NAME_MIDDLE);

  if (!last().empty())
    available_types->insert(NAME_LAST);

  if (!MiddleInitial().empty())
    available_types->insert(NAME_MIDDLE_INITIAL);

  if (!FullName().empty())
    available_types->insert(NAME_FULL);

  if (!suffix().empty())
    available_types->insert(NAME_SUFFIX);

  if (!email().empty())
    available_types->insert(EMAIL_ADDRESS);

  if (!company_name().empty())
    available_types->insert(COMPANY_NAME);
}

void ContactInfo::FindInfoMatches(const AutoFillType& type,
                                  const string16& info,
                                  std::vector<string16>* matched_text) const {
  DCHECK(matched_text);

  string16 match;
  if (type.field_type() == UNKNOWN_TYPE) {
    for (size_t i = 0; i < kAutoFillContactInfoLength; i++) {
      if (FindInfoMatchesHelper(kAutoFillContactInfoTypes[i], info, &match))
        matched_text->push_back(match);
    }
  } else if (FindInfoMatchesHelper(type.field_type(), info, &match)) {
      matched_text->push_back(match);
  }
}

string16 ContactInfo::GetFieldText(const AutoFillType& type) const {
  AutoFillFieldType field_type = type.field_type();
  if (field_type == NAME_FIRST)
    return first();

  if (field_type == NAME_MIDDLE)
    return middle();

  if (field_type == NAME_LAST)
    return last();

  if (field_type == NAME_MIDDLE_INITIAL)
    return MiddleInitial();

  if (field_type == NAME_FULL)
    return FullName();

  if (field_type == NAME_SUFFIX)
    return suffix();

  if (field_type == EMAIL_ADDRESS)
    return email();

  if (field_type == COMPANY_NAME)
    return company_name();

  return string16();
}

void ContactInfo::SetInfo(const AutoFillType& type, const string16& value) {
  AutoFillFieldType field_type = type.field_type();
  DCHECK(type.group() == AutoFillType::CONTACT_INFO);
  if (field_type == NAME_FIRST)
    SetFirst(value);
  else if (field_type == NAME_MIDDLE || field_type == NAME_MIDDLE_INITIAL)
    SetMiddle(value);
  else if (field_type == NAME_LAST)
    SetLast(value);
  else if (field_type == NAME_SUFFIX)
    set_suffix(value);
  else if (field_type == EMAIL_ADDRESS)
    email_ = value;
  else if (field_type == COMPANY_NAME)
    company_name_ = value;
  else if (field_type == NAME_FULL)
    SetFullName(value);
  else
    NOTREACHED();
}

ContactInfo::ContactInfo(const ContactInfo& contact_info)
    : FormGroup(),
      first_tokens_(contact_info.first_tokens_),
      middle_tokens_(contact_info.middle_tokens_),
      last_tokens_(contact_info.last_tokens_),
      first_(contact_info.first_),
      middle_(contact_info.middle_),
      last_(contact_info.last_),
      suffix_(contact_info.suffix_),
      email_(contact_info.email_),
      company_name_(contact_info.company_name_) {
}

string16 ContactInfo::FullName() const {
  if (first_.empty())
    return string16();

  std::vector<string16> full_name;
  full_name.push_back(first_);

  if (!middle_.empty())
    full_name.push_back(middle_);

  if (!last_.empty())
    full_name.push_back(last_);

  if (!suffix_.empty())
    full_name.push_back(suffix_);

  return JoinString(full_name, ' ');
}

string16 ContactInfo::MiddleInitial() const {
  if (middle_.empty())
    return string16();

  string16 middle_name(middle());
  string16 initial;
  initial.push_back(middle_name[0]);
  return initial;
}

bool ContactInfo::FindInfoMatchesHelper(const AutoFillFieldType& field_type,
                                        const string16& info,
                                        string16* match) const {
  if (match == NULL) {
    DLOG(ERROR) << "NULL match string passed in";
    return false;
  }

  match->clear();
  if (field_type == NAME_FIRST &&
      StartsWith(first(), info, false)) {
    *match = first();
  } else if (field_type == NAME_MIDDLE &&
             StartsWith(middle(), info, false)) {
    *match = middle();
  } else if (field_type == NAME_LAST &&
             StartsWith(last(), info, false)) {
    *match = last();
  } else if (field_type == NAME_SUFFIX &&
             StartsWith(suffix(), info, false)) {
    *match = suffix();
  } else if (field_type == NAME_MIDDLE_INITIAL && IsMiddleInitial(info)) {
    *match = MiddleInitial();
  } else if (field_type == NAME_FULL &&
             StartsWith(FullName(), info, false)) {
    *match = FullName();
  } else if (field_type == EMAIL_ADDRESS &&
             StartsWith(email(), info, false)) {
    *match = email();
  } else if (field_type == COMPANY_NAME &&
             StartsWith(company_name(), info, false)) {
    *match = company_name();
  }

  return !match->empty();
}

// If each of the 'words' contained in the text are also present in the first
// name then we will consider the text to be of type kFirstName. This means
// that people with multiple first names will be able to enter any one of
// their first names and have it correctly recognized.
bool ContactInfo::IsFirstName(const string16& text) const {
  return IsNameMatch(text, first_tokens_);
}

// If each of the 'words' contained in the text are also present in the middle
// name then we will consider the text to be of type kMiddleName.
bool ContactInfo::IsMiddleName(const string16& text) const {
  return IsNameMatch(text, middle_tokens_);
}

// If each of the 'words' contained in the text are also present in the last
// name then we will consider the text to be of type kLastName.
bool ContactInfo::IsLastName(const string16& text) const {
  return IsNameMatch(text, last_tokens_);
}

bool ContactInfo::IsSuffix(const string16& text) const {
  string16 lower_suffix = StringToLowerASCII(suffix_);
  return (lower_suffix == text);
}

bool ContactInfo::IsMiddleInitial(const string16& text) const {
  if (text.length() != 1)
     return false;

  string16 lower_case = StringToLowerASCII(text);
  // If the text entered was a single character and it matches the first letter
  // of any of the given middle names then we consider it to be a middle
  // initial field.
  size_t middle_tokens_size = middle_tokens_.size();
  for (size_t i = 0; i < middle_tokens_size; ++i) {
    if (middle_tokens_[i][0] == lower_case[0])
      return true;
  }

  return false;
}

// A field will be considered to be of type NAME_FULL if:
//    1) it contains at least one word from the first name.
//    2) it contains at least one word from the last name.
//    3) all of the words in the field match a word in either the first,
//       middle, or last name.
bool ContactInfo::IsFullName(const string16& text) const {
  size_t first_tokens_size = first_tokens_.size();
  if (first_tokens_size == 0)
    return false;

  size_t middle_tokens_size = middle_tokens_.size();

  size_t last_tokens_size = last_tokens_.size();
  if (last_tokens_size == 0)
    return false;

  NameTokens text_tokens;
  Tokenize(text, kNameSplitChars, &text_tokens);
  size_t text_tokens_size = text_tokens.size();
  if (text_tokens_size == 0 || text_tokens_size < 2)
    return false;

  size_t name_tokens_size =
      first_tokens_size + middle_tokens_size + last_tokens_size;
  if (text_tokens_size > name_tokens_size)
    return false;

  bool first_name_match = false;
  bool last_name_match = false;
  NameTokens::iterator iter;
  for (iter = text_tokens.begin(); iter != text_tokens.end(); ++iter) {
    bool match = false;
    if (IsWordInName(*iter, first_tokens_)) {
      match = true;
      first_name_match = true;
    }

    if (IsWordInName(*iter, last_tokens_)) {
      match = true;
      last_name_match = true;
    }

    if (IsWordInName(*iter, middle_tokens_))
      match = true;

    if (!match)
      return false;
  }

  return (first_name_match && last_name_match);
}

bool ContactInfo::IsNameMatch(const string16& text,
                              const NameTokens& name_tokens) const {
  size_t name_tokens_size = name_tokens.size();
  if (name_tokens_size == 0)
    return false;

  NameTokens text_tokens;
  Tokenize(text, kNameSplitChars, &text_tokens);
  size_t text_tokens_size = text_tokens.size();
  if (text_tokens_size == 0)
    return false;

  if (text_tokens_size > name_tokens_size)
    return false;

  // If each of the 'words' contained in the text are also present in the name,
  // then we will consider the text to match the name.
  NameTokens::iterator iter;
  for (iter = text_tokens.begin(); iter != text_tokens.end(); ++iter) {
    if (!IsWordInName(*iter, name_tokens))
      return false;
  }

  return true;
}

bool ContactInfo::IsWordInName(const string16& word,
                               const NameTokens& name_tokens) const {
  NameTokens::const_iterator iter;
  for (iter = name_tokens.begin(); iter != name_tokens.end(); ++iter) {
    // |*iter| is already lower-cased.
    if (StringToLowerASCII(word) == *iter)
      return true;
  }

  return false;
}

void ContactInfo::SetFirst(const string16& first) {
  first_ = first;
  first_tokens_.clear();
  Tokenize(first, kNameSplitChars, &first_tokens_);
  NameTokens::iterator iter;
  for (iter = first_tokens_.begin(); iter != first_tokens_.end(); ++iter)
    *iter = StringToLowerASCII(*iter);
}

void ContactInfo::SetMiddle(const string16& middle) {
  middle_ = middle;
  middle_tokens_.clear();
  Tokenize(middle, kNameSplitChars, &middle_tokens_);
  NameTokens::iterator iter;
  for (iter = middle_tokens_.begin(); iter != middle_tokens_.end(); ++iter)
    *iter = StringToLowerASCII(*iter);
}

void ContactInfo::SetLast(const string16& last) {
  last_ = last;
  last_tokens_.clear();
  Tokenize(last, kNameSplitChars, &last_tokens_);
  NameTokens::iterator iter;
  for (iter = last_tokens_.begin(); iter != last_tokens_.end(); ++iter)
    *iter = StringToLowerASCII(*iter);
}

void ContactInfo::SetFullName(const string16& full) {
  NameTokens full_name_tokens;
  Tokenize(full, ASCIIToUTF16(" "), &full_name_tokens);
  // Clear the names.
  SetFirst(string16());
  SetMiddle(string16());
  SetLast(string16());

  // There are four possibilities: empty; first name; first and last names;
  // first, middle (possibly multiple strings) and then the last name.
  if (full_name_tokens.size() > 0) {
    SetFirst(full_name_tokens[0]);
    if (full_name_tokens.size() > 1) {
      SetLast(full_name_tokens.back());
      if (full_name_tokens.size() > 2) {
        full_name_tokens.erase(full_name_tokens.begin());
        full_name_tokens.pop_back();
        SetMiddle(JoinString(full_name_tokens, ' '));
      }
    }
  }
}