/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.gecko.sync.repositories.domain;

import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.repositories.android.RepoUtils;

public class PasswordRecord extends Record {
  private static final String LOG_TAG = "PasswordRecord";

  public static final String COLLECTION_NAME = "passwords";
  public static long PASSWORDS_TTL = -1; // Never expire passwords.

  // Payload strings.
  public static final String PAYLOAD_HOSTNAME = "hostname";
  public static final String PAYLOAD_FORM_SUBMIT_URL = "formSubmitURL";
  public static final String PAYLOAD_HTTP_REALM = "httpRealm";
  public static final String PAYLOAD_USERNAME = "username";
  public static final String PAYLOAD_PASSWORD = "password";
  public static final String PAYLOAD_USERNAME_FIELD = "usernameField";
  public static final String PAYLOAD_PASSWORD_FIELD = "passwordField";

  public PasswordRecord(String guid, String collection, long lastModified, boolean deleted) {
    super(guid, collection, lastModified, deleted);
    this.ttl = PASSWORDS_TTL;
  }
  public PasswordRecord(String guid, String collection, long lastModified) {
    this(guid, collection, lastModified, false);
  }
  public PasswordRecord(String guid, String collection) {
    this(guid, collection, 0, false);
  }
  public PasswordRecord(String guid) {
    this(guid, COLLECTION_NAME, 0, false);
  }
  public PasswordRecord() {
    this(Utils.generateGuid(), COLLECTION_NAME, 0, false);
  }

  public String id;
  public String hostname;
  public String formSubmitURL;
  public String httpRealm;
  // TODO these are encrypted in the passwords content provider,
  // need to figure out what we need to do here.
  public String usernameField;
  public String passwordField;
  public String encryptedUsername;
  public String encryptedPassword;
  public String encType;

  public long   timeCreated;
  public long   timeLastUsed;
  public long   timePasswordChanged;
  public long   timesUsed;


  @Override
  public Record copyWithIDs(String guid, long androidID) {
    PasswordRecord out = new PasswordRecord(guid, this.collection, this.lastModified, this.deleted);
    out.androidID = androidID;
    out.sortIndex = this.sortIndex;
    out.ttl       = this.ttl;

    // Copy PasswordRecord fields.
    out.id            = this.id;
    out.hostname      = this.hostname;
    out.formSubmitURL = this.formSubmitURL;
    out.httpRealm     = this.httpRealm;

    out.usernameField       = this.usernameField;
    out.passwordField       = this.passwordField;
    out.encryptedUsername   = this.encryptedUsername;
    out.encryptedPassword   = this.encryptedPassword;
    out.encType             = this.encType;

    out.timeCreated         = this.timeCreated;
    out.timeLastUsed        = this.timeLastUsed;
    out.timePasswordChanged = this.timePasswordChanged;
    out.timesUsed           = this.timesUsed;

    return out;
  }

  @Override
  public void initFromPayload(ExtendedJSONObject payload) {
    this.hostname = payload.getString(PAYLOAD_HOSTNAME);
    this.formSubmitURL = payload.getString(PAYLOAD_FORM_SUBMIT_URL);
    this.httpRealm = payload.getString(PAYLOAD_HTTP_REALM);
    this.encryptedUsername = payload.getString(PAYLOAD_USERNAME);
    this.encryptedPassword = payload.getString(PAYLOAD_PASSWORD);
    this.usernameField = payload.getString(PAYLOAD_USERNAME_FIELD);
    this.passwordField = payload.getString(PAYLOAD_PASSWORD_FIELD);
  }

  @Override
  public void populatePayload(ExtendedJSONObject payload) {
    putPayload(payload, PAYLOAD_HOSTNAME, this.hostname);
    putPayload(payload, PAYLOAD_FORM_SUBMIT_URL, this.formSubmitURL);
    putPayload(payload, PAYLOAD_HTTP_REALM, this.httpRealm);
    putPayload(payload, PAYLOAD_USERNAME, this.encryptedUsername);
    putPayload(payload, PAYLOAD_PASSWORD, this.encryptedPassword);
    putPayload(payload, PAYLOAD_USERNAME_FIELD, this.usernameField);
    putPayload(payload, PAYLOAD_PASSWORD_FIELD, this.passwordField);
  }

  @Override
  public boolean congruentWith(Object o) {
    if (!(o instanceof PasswordRecord)) {
      return false;
    }
    PasswordRecord other = (PasswordRecord) o;
    if (!super.congruentWith(other)) {
      return false;
    }
    return RepoUtils.stringsEqual(this.hostname, other.hostname)
        && RepoUtils.stringsEqual(this.formSubmitURL, other.formSubmitURL)
        // Bug 738347 - SQLiteBridge does not check for nulls in ContentValues.
        // && RepoUtils.stringsEqual(this.httpRealm, other.httpRealm)
        // && RepoUtils.stringsEqual(this.encType, other.encType)
        && RepoUtils.stringsEqual(this.usernameField, other.usernameField)
        && RepoUtils.stringsEqual(this.passwordField, other.passwordField)
        && RepoUtils.stringsEqual(this.encryptedUsername, other.encryptedUsername)
        && RepoUtils.stringsEqual(this.encryptedPassword, other.encryptedPassword);
  }

  @Override
  public boolean equalPayloads(Object o) {
    if (!(o instanceof PasswordRecord)) {
      return false;
    }

    PasswordRecord other = (PasswordRecord) o;
    Logger.debug("PasswordRecord", "thisRecord:" + this.toString());
    Logger.debug("PasswordRecord", "otherRecord:" + o.toString());

    if (this.deleted) {
      if (other.deleted) {
        // Deleted records are equal if their guids match.
        return RepoUtils.stringsEqual(this.guid, other.guid);
      }
      // One record is deleted, the other is not. Not equal.
      return false;
    }

    if (!super.equalPayloads(other)) {
      Logger.debug(LOG_TAG, "super.equalPayloads returned false.");
      return false;
    }

    return RepoUtils.stringsEqual(this.hostname, other.hostname)
        && RepoUtils.stringsEqual(this.formSubmitURL, other.formSubmitURL)
        // Bug 738347 - SQLiteBridge does not check for nulls in ContentValues.
        // && RepoUtils.stringsEqual(this.httpRealm, other.httpRealm)
        // && RepoUtils.stringsEqual(this.encType, other.encType)
        && RepoUtils.stringsEqual(this.usernameField, other.usernameField)
        && RepoUtils.stringsEqual(this.passwordField, other.passwordField)
        && RepoUtils.stringsEqual(this.encryptedUsername, other.encryptedUsername)
        && RepoUtils.stringsEqual(this.encryptedPassword, other.encryptedPassword);
        // Desktop sync never sets timeCreated so this isn't relevant for sync records.
  }

  @Override
  public String toString() {
    return "PasswordRecord {"
        + "lastModified: " + this.lastModified + ", "
        + "hostname null?: " + (this.hostname == null) + ", "
        + "formSubmitURL null?: " + (this.formSubmitURL == null) + ", "
        + "httpRealm null?: " + (this.httpRealm == null) + ", "
        + "usernameField null?: " + (this.usernameField == null) + ", "
        + "passwordField null?: " + (this.passwordField == null) + ", "
        + "encryptedUsername null?: " + (this.encryptedUsername == null) + ", "
        + "encryptedPassword null?: " + (this.encryptedPassword == null) + ", "
        + "encType: " + this.encType + ", "
        + "timeCreated: " + this.timeCreated + ", "
        + "timeLastUsed: " + this.timeLastUsed + ", "
        + "timePasswordChanged: " + this.timePasswordChanged + ", "
        + "timesUsed: " + this.timesUsed;
  }

  /**
   * A PasswordRecord is considered valid if it abides by the database
   * constraints of the PasswordsProvider (moz_logins).
   *
   * See toolkit/components/passwordmgr/storage-mozStorage.js for the
   * definitions:
   *
   * http://hg.mozilla.org/mozilla-central/file/00955d61cc94/toolkit/components/passwordmgr/storage-mozStorage.js#l98
   */
    public boolean isValid() {
        if (this.deleted) {
            return true;
        }

        return this.hostname != null &&
               this.encryptedUsername != null &&
               this.encryptedPassword != null &&
               this.usernameField != null &&
               this.passwordField != null;
  }
}
