// 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/phone_number.h"

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

namespace {

const char16 kPhoneNumberSeparators[] = { ' ', '.', '(', ')', '-', 0 };

// The number of digits in a phone number.
const size_t kPhoneNumberLength = 7;

// The number of digits in an area code.
const size_t kPhoneCityCodeLength = 3;

const AutoFillType::FieldTypeSubGroup kAutoFillPhoneTypes[] = {
  AutoFillType::PHONE_NUMBER,
  AutoFillType::PHONE_CITY_CODE,
  AutoFillType::PHONE_COUNTRY_CODE,
  AutoFillType::PHONE_CITY_AND_NUMBER,
  AutoFillType::PHONE_WHOLE_NUMBER,
};

const int kAutoFillPhoneLength = arraysize(kAutoFillPhoneTypes);

}  // namespace

PhoneNumber::PhoneNumber() {}

PhoneNumber::~PhoneNumber() {}

void PhoneNumber::GetPossibleFieldTypes(const string16& text,
                                        FieldTypeSet* possible_types) const {
  string16 stripped_text(text);
  StripPunctuation(&stripped_text);
  if (!Validate(stripped_text))
    return;

  if (IsNumber(stripped_text))
    possible_types->insert(GetNumberType());

  if (IsCityCode(stripped_text))
    possible_types->insert(GetCityCodeType());

  if (IsCountryCode(stripped_text))
    possible_types->insert(GetCountryCodeType());

  if (IsCityAndNumber(stripped_text))
    possible_types->insert(GetCityAndNumberType());

  if (IsWholeNumber(stripped_text))
    possible_types->insert(GetWholeNumberType());
}

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

  if (!number().empty())
    available_types->insert(GetNumberType());

  if (!city_code().empty())
    available_types->insert(GetCityCodeType());

  if (!country_code().empty())
    available_types->insert(GetCountryCodeType());

  if (!CityAndNumber().empty())
    available_types->insert(GetCityAndNumberType());

  if (!WholeNumber().empty())
    available_types->insert(GetWholeNumberType());
}

string16 PhoneNumber::GetFieldText(const AutoFillType& type) const {
  AutoFillFieldType field_type = type.field_type();
  if (field_type == GetNumberType())
    return number();

  if (field_type == GetCityCodeType())
    return city_code();

  if (field_type == GetCountryCodeType())
    return country_code();

  if (field_type == GetCityAndNumberType())
    return CityAndNumber();

  if (field_type == GetWholeNumberType())
    return WholeNumber();

  return string16();
}

void PhoneNumber::FindInfoMatches(const AutoFillType& type,
                                  const string16& info,
                                  std::vector<string16>* matched_text) const {
  if (matched_text == NULL) {
    DLOG(ERROR) << "NULL matched vector passed in";
    return;
  }

  string16 number(info);
  StripPunctuation(&number);
  if (!Validate(number))
    return;

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

void PhoneNumber::SetInfo(const AutoFillType& type, const string16& value) {
  string16 number(value);
  StripPunctuation(&number);
  if (!Validate(number))
    return;

  FieldTypeSubGroup subgroup = type.subgroup();
  if (subgroup == AutoFillType::PHONE_NUMBER)
    set_number(number);
  else if (subgroup == AutoFillType::PHONE_CITY_CODE)
    set_city_code(number);
  else if (subgroup == AutoFillType::PHONE_COUNTRY_CODE)
    set_country_code(number);
  else if (subgroup == AutoFillType::PHONE_CITY_AND_NUMBER ||
           subgroup == AutoFillType::PHONE_WHOLE_NUMBER)
    set_whole_number(number);
  else
    NOTREACHED();
}

// Static.
bool PhoneNumber::ParsePhoneNumber(const string16& value,
                                   string16* number,
                                   string16* city_code,
                                   string16* country_code) {
  DCHECK(number);
  DCHECK(city_code);
  DCHECK(country_code);

  // Make a working copy of value.
  string16 working = value;

  *number = string16();
  *city_code = string16();
  *country_code = string16();

  // First remove any punctuation.
  StripPunctuation(&working);

  if (working.size() < kPhoneNumberLength)
    return false;

  // Treat the last 7 digits as the number.
  *number = working.substr(working.size() - kPhoneNumberLength,
                           kPhoneNumberLength);
  working.resize(working.size() - kPhoneNumberLength);
  if (working.size() < kPhoneCityCodeLength)
    return true;

  // Treat the next three digits as the city code.
  *city_code = working.substr(working.size() - kPhoneCityCodeLength,
                              kPhoneCityCodeLength);
  working.resize(working.size() - kPhoneCityCodeLength);
  if (working.empty())
    return true;

  // Treat any remaining digits as the country code.
  *country_code = working;
  return true;
}

string16 PhoneNumber::WholeNumber() const {
  string16 whole_number;
  if (!country_code_.empty())
    whole_number.append(country_code_);

  if (!city_code_.empty())
    whole_number.append(city_code_);

  if (!number_.empty())
    whole_number.append(number_);

  return whole_number;
}

void PhoneNumber::set_number(const string16& number) {
  string16 digits(number);
  StripPunctuation(&digits);
  number_ = digits;
}

void PhoneNumber::set_whole_number(const string16& whole_number) {
  string16 number, city_code, country_code;
  ParsePhoneNumber(whole_number, &number, &city_code, &country_code);
  set_number(number);
  set_city_code(city_code);
  set_country_code(country_code);
}

PhoneNumber::PhoneNumber(const PhoneNumber& phone_number)
    : FormGroup(),
      country_code_(phone_number.country_code_),
      city_code_(phone_number.city_code_),
      number_(phone_number.number_),
      extension_(phone_number.extension_) {
}

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

  match->clear();
  if (subgroup == AutoFillType::PHONE_NUMBER &&
      StartsWith(number(), info, true)) {
    *match = number();
  } else if (subgroup == AutoFillType::PHONE_CITY_CODE &&
             StartsWith(city_code(), info, true)) {
    *match = city_code();
  } else if (subgroup == AutoFillType::PHONE_COUNTRY_CODE &&
             StartsWith(country_code(), info, true)) {
    *match = country_code();
  } else if (subgroup == AutoFillType::PHONE_CITY_AND_NUMBER &&
             StartsWith(CityAndNumber(), info, true)) {
    *match = CityAndNumber();
  } else if (subgroup == AutoFillType::PHONE_WHOLE_NUMBER &&
             StartsWith(WholeNumber(), info, true)) {
    *match = WholeNumber();
  }

  return !match->empty();
}

bool PhoneNumber::IsNumber(const string16& text) const {
  if (text.length() > number_.length())
    return false;

  return StartsWith(number_, text, false);
}

bool PhoneNumber::IsCityCode(const string16& text) const {
  if (text.length() > city_code_.length())
    return false;

  return StartsWith(city_code_, text, false);
}

bool PhoneNumber::IsCountryCode(const string16& text) const {
  if (text.length() > country_code_.length())
    return false;

  return StartsWith(country_code_, text, false);
}

bool PhoneNumber::IsCityAndNumber(const string16& text) const {
  string16 city_and_number(CityAndNumber());
  if (text.length() > city_and_number.length())
    return false;

  return StartsWith(city_and_number, text, false);
}

bool PhoneNumber::IsWholeNumber(const string16& text) const {
  string16 whole_number(WholeNumber());
  if (text.length() > whole_number.length())
    return false;

  return StartsWith(whole_number, text, false);
}

bool PhoneNumber::Validate(const string16& number) const {
  for (size_t i = 0; i < number.length(); ++i) {
    if (!IsAsciiDigit(number[i]))
      return false;
  }

  return true;
}

// Static.
void PhoneNumber::StripPunctuation(string16* number) {
  RemoveChars(*number, kPhoneNumberSeparators, number);
}