/**
 * This file is part of Eclipse Steady.
 *
 * 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.
 *
 * SPDX-License-Identifier: Apache-2.0
 * SPDX-FileCopyrightText: Copyright (c) 2018-2020 SAP SE or an SAP affiliate company and Eclipse Steady contributors
 */
package org.eclipse.steady.shared.json.model.diff;

import java.util.HashSet;
import java.util.Set;

import javax.validation.constraints.NotNull;

import org.eclipse.steady.shared.enums.ConstructType;
import org.eclipse.steady.shared.json.model.ConstructId;
import org.eclipse.steady.shared.json.model.LibraryId;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;

/**
 * <p>JarDiffResult class.</p>
 */
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class JarDiffResult {

  private LibraryId oldLibId;
  private LibraryId newLibId;

  private Set<ConstructId> newPackages;
  private Set<ConstructId> deletedPackages;

  private Set<ConstructId> newClasses;
  private Set<ClassDiffResult> modifiedClasses;
  private Set<ConstructId> deletedClasses;
  private Set<ConstructId> deprecatedClasses;
  private Set<ConstructId> undeprecatedClasses;

  private Set<ConstructId> newInterfaces;
  private Set<ClassDiffResult> modifiedInterfaces;
  private Set<ConstructId> deletedInterfaces;
  private Set<ConstructId> deprecatedInterfaces;
  private Set<ConstructId> undeprecatedInterfaces;

  /**
   * <p>Getter for the field <code>oldLibId</code>.</p>
   *
   * @return a {@link org.eclipse.steady.shared.json.model.LibraryId} object.
   */
  public LibraryId getOldLibId() {
    return oldLibId;
  }
  /**
   * <p>Setter for the field <code>oldLibId</code>.</p>
   *
   * @param oldLibId a {@link org.eclipse.steady.shared.json.model.LibraryId} object.
   */
  public void setOldLibId(LibraryId oldLibId) {
    this.oldLibId = oldLibId;
  }

  /**
   * <p>Getter for the field <code>newLibId</code>.</p>
   *
   * @return a {@link org.eclipse.steady.shared.json.model.LibraryId} object.
   */
  public LibraryId getNewLibId() {
    return newLibId;
  }
  /**
   * <p>Setter for the field <code>newLibId</code>.</p>
   *
   * @param newLibId a {@link org.eclipse.steady.shared.json.model.LibraryId} object.
   */
  public void setNewLibId(LibraryId newLibId) {
    this.newLibId = newLibId;
  }

  /**
   * <p>Getter for the field <code>newPackages</code>.</p>
   *
   * @return a {@link java.util.Set} object.
   */
  public Set<ConstructId> getNewPackages() {
    return newPackages;
  }
  /**
   * <p>addNewPackage.</p>
   *
   * @param _p a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   */
  public void addNewPackage(ConstructId _p) {
    if (this.newPackages == null) this.newPackages = new HashSet<ConstructId>();
    this.newPackages.add(_p);
  }
  /**
   * <p>Setter for the field <code>newPackages</code>.</p>
   *
   * @param newPackages a {@link java.util.Set} object.
   */
  public void setNewPackages(Set<ConstructId> newPackages) {
    this.newPackages = newPackages;
  }

  /**
   * <p>Getter for the field <code>deletedPackages</code>.</p>
   *
   * @return a {@link java.util.Set} object.
   */
  public Set<ConstructId> getDeletedPackages() {
    return deletedPackages;
  }
  /**
   * <p>addDeletedPackage.</p>
   *
   * @param _p a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   */
  public void addDeletedPackage(ConstructId _p) {
    if (this.deletedPackages == null) this.deletedPackages = new HashSet<ConstructId>();
    this.deletedPackages.add(_p);
  }
  /**
   * <p>Setter for the field <code>deletedPackages</code>.</p>
   *
   * @param deletedPackages a {@link java.util.Set} object.
   */
  public void setDeletedPackages(Set<ConstructId> deletedPackages) {
    this.deletedPackages = deletedPackages;
  }

  /**
   * <p>Getter for the field <code>newClasses</code>.</p>
   *
   * @return a {@link java.util.Set} object.
   */
  public Set<ConstructId> getNewClasses() {
    return newClasses;
  }
  /**
   * <p>addNewClass.</p>
   *
   * @param _class a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   */
  public void addNewClass(ConstructId _class) {
    if (this.newClasses == null) this.newClasses = new HashSet<ConstructId>();
    this.newClasses.add(_class);
  }
  /**
   * <p>Setter for the field <code>newClasses</code>.</p>
   *
   * @param newClasses a {@link java.util.Set} object.
   */
  public void setNewClasses(Set<ConstructId> newClasses) {
    this.newClasses = newClasses;
  }

  /**
   * <p>Getter for the field <code>modifiedClasses</code>.</p>
   *
   * @return a {@link java.util.Set} object.
   */
  public Set<ClassDiffResult> getModifiedClasses() {
    return modifiedClasses;
  }
  /**
   * <p>addModifiedClass.</p>
   *
   * @param _class a {@link org.eclipse.steady.shared.json.model.diff.ClassDiffResult} object.
   */
  public void addModifiedClass(ClassDiffResult _class) {
    if (this.modifiedClasses == null) this.modifiedClasses = new HashSet<ClassDiffResult>();
    this.modifiedClasses.add(_class);
  }
  /**
   * <p>Setter for the field <code>modifiedClasses</code>.</p>
   *
   * @param modifiedClasses a {@link java.util.Set} object.
   */
  public void setModifiedClasses(Set<ClassDiffResult> modifiedClasses) {
    this.modifiedClasses = modifiedClasses;
  }

  /**
   * <p>Getter for the field <code>deletedClasses</code>.</p>
   *
   * @return a {@link java.util.Set} object.
   */
  public Set<ConstructId> getDeletedClasses() {
    return deletedClasses;
  }
  /**
   * <p>addDeletedClass.</p>
   *
   * @param _class a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   */
  public void addDeletedClass(ConstructId _class) {
    if (this.deletedClasses == null) this.deletedClasses = new HashSet<ConstructId>();
    this.deletedClasses.add(_class);
  }
  /**
   * <p>Setter for the field <code>deletedClasses</code>.</p>
   *
   * @param deletedClasses a {@link java.util.Set} object.
   */
  public void setDeletedClasses(Set<ConstructId> deletedClasses) {
    this.deletedClasses = deletedClasses;
  }

  /**
   * <p>Getter for the field <code>deprecatedClasses</code>.</p>
   *
   * @return a {@link java.util.Set} object.
   */
  public Set<ConstructId> getDeprecatedClasses() {
    return deprecatedClasses;
  }
  /**
   * <p>addDeprecatedClass.</p>
   *
   * @param _class a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   */
  public void addDeprecatedClass(ConstructId _class) {
    if (this.deprecatedClasses == null) this.deprecatedClasses = new HashSet<ConstructId>();
    this.deprecatedClasses.add(_class);
  }
  /**
   * <p>Setter for the field <code>deprecatedClasses</code>.</p>
   *
   * @param deprecatedClasses a {@link java.util.Set} object.
   */
  public void setDeprecatedClasses(Set<ConstructId> deprecatedClasses) {
    this.deprecatedClasses = deprecatedClasses;
  }

  /**
   * <p>Getter for the field <code>undeprecatedClasses</code>.</p>
   *
   * @return a {@link java.util.Set} object.
   */
  public Set<ConstructId> getUndeprecatedClasses() {
    return undeprecatedClasses;
  }
  /**
   * <p>addUndeprecatedClass.</p>
   *
   * @param _class a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   */
  public void addUndeprecatedClass(ConstructId _class) {
    if (this.undeprecatedClasses == null) this.undeprecatedClasses = new HashSet<ConstructId>();
    this.undeprecatedClasses.add(_class);
  }
  /**
   * <p>Setter for the field <code>undeprecatedClasses</code>.</p>
   *
   * @param undeprecatedClasses a {@link java.util.Set} object.
   */
  public void setUndeprecatedClasses(Set<ConstructId> undeprecatedClasses) {
    this.undeprecatedClasses = undeprecatedClasses;
  }

  /**
   * <p>Getter for the field <code>newInterfaces</code>.</p>
   *
   * @return a {@link java.util.Set} object.
   */
  public Set<ConstructId> getNewInterfaces() {
    return newInterfaces;
  }
  /**
   * <p>addNewInterface.</p>
   *
   * @param _class a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   */
  public void addNewInterface(ConstructId _class) {
    if (this.newInterfaces == null) this.newInterfaces = new HashSet<ConstructId>();
    this.newInterfaces.add(_class);
  }
  /**
   * <p>Setter for the field <code>newInterfaces</code>.</p>
   *
   * @param newInterfaces a {@link java.util.Set} object.
   */
  public void setNewInterfaces(Set<ConstructId> newInterfaces) {
    this.newInterfaces = newInterfaces;
  }

  /**
   * <p>Getter for the field <code>modifiedInterfaces</code>.</p>
   *
   * @return a {@link java.util.Set} object.
   */
  public Set<ClassDiffResult> getModifiedInterfaces() {
    return modifiedInterfaces;
  }
  /**
   * <p>addModifiedInterface.</p>
   *
   * @param _class a {@link org.eclipse.steady.shared.json.model.diff.ClassDiffResult} object.
   */
  public void addModifiedInterface(ClassDiffResult _class) {
    if (this.modifiedInterfaces == null) this.modifiedInterfaces = new HashSet<ClassDiffResult>();
    this.modifiedInterfaces.add(_class);
  }
  /**
   * <p>Setter for the field <code>modifiedInterfaces</code>.</p>
   *
   * @param modifiedInterfaces a {@link java.util.Set} object.
   */
  public void setModifiedInterfaces(Set<ClassDiffResult> modifiedInterfaces) {
    this.modifiedInterfaces = modifiedInterfaces;
  }

  /**
   * <p>Getter for the field <code>deletedInterfaces</code>.</p>
   *
   * @return a {@link java.util.Set} object.
   */
  public Set<ConstructId> getDeletedInterfaces() {
    return deletedInterfaces;
  }
  /**
   * <p>addDeletedInterface.</p>
   *
   * @param _class a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   */
  public void addDeletedInterface(ConstructId _class) {
    if (this.deletedInterfaces == null) this.deletedInterfaces = new HashSet<ConstructId>();
    this.deletedInterfaces.add(_class);
  }
  /**
   * <p>Setter for the field <code>deletedInterfaces</code>.</p>
   *
   * @param deletedInterfaces a {@link java.util.Set} object.
   */
  public void setDeletedInterfaces(Set<ConstructId> deletedInterfaces) {
    this.deletedInterfaces = deletedInterfaces;
  }

  /**
   * <p>Getter for the field <code>deprecatedInterfaces</code>.</p>
   *
   * @return a {@link java.util.Set} object.
   */
  public Set<ConstructId> getDeprecatedInterfaces() {
    return deprecatedInterfaces;
  }
  /**
   * <p>addDeprecatedInterface.</p>
   *
   * @param _class a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   */
  public void addDeprecatedInterface(ConstructId _class) {
    if (this.deprecatedInterfaces == null) this.deprecatedInterfaces = new HashSet<ConstructId>();
    this.deprecatedInterfaces.add(_class);
  }
  /**
   * <p>Setter for the field <code>deprecatedInterfaces</code>.</p>
   *
   * @param deprecatedInterfaces a {@link java.util.Set} object.
   */
  public void setDeprecatedInterfaces(Set<ConstructId> deprecatedInterfaces) {
    this.deprecatedInterfaces = deprecatedInterfaces;
  }

  /**
   * <p>Getter for the field <code>undeprecatedInterfaces</code>.</p>
   *
   * @return a {@link java.util.Set} object.
   */
  public Set<ConstructId> getUndeprecatedInterfaces() {
    return undeprecatedInterfaces;
  }
  /**
   * <p>addUndeprecatedInterface.</p>
   *
   * @param _class a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   */
  public void addUndeprecatedInterface(ConstructId _class) {
    if (this.undeprecatedInterfaces == null)
      this.undeprecatedInterfaces = new HashSet<ConstructId>();
    this.undeprecatedInterfaces.add(_class);
  }
  /**
   * <p>Setter for the field <code>undeprecatedInterfaces</code>.</p>
   *
   * @param undeprecatedInterfaces a {@link java.util.Set} object.
   */
  public void setUndeprecatedInterfaces(Set<ConstructId> undeprecatedInterfaces) {
    this.undeprecatedInterfaces = undeprecatedInterfaces;
  }

  /**
   * Returns true if the given construct is deleted from the class, the entire class is deleted or the package is deleted.
   * from the container class.
   *
   * @param _cid a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   * @return a boolean.
   */
  @JsonIgnore
  public boolean isDeleted(@NotNull ConstructId _cid) {
    // Package deleted
    final ConstructId pack = JarDiffResult.getPackageOf(_cid);
    boolean del =
        this.deletedPackages != null && pack != null && this.deletedPackages.contains(pack);

    if (_cid.getType() != ConstructType.PACK) {
      // Class deleted
      if (!del) {
        final ConstructId clas = JarDiffResult.getClassOf(_cid);
        del = this.deletedClasses != null && this.deletedClasses.contains(clas);
      }

      // Construct deleted
      if (!del && this.modifiedClasses != null) {
        for (ClassDiffResult cdr : this.modifiedClasses) {
          if (cdr.isDeleted(_cid)) {
            del = true;
            break;
          }
        }
      }
    }
    return del;
  }

  /**
   * Returns true if ...
   * from the container class.
   *
   * @param _cid a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   * @return a boolean.
   */
  @JsonIgnore
  public boolean isBodyChanged(@NotNull ConstructId _cid) {
    boolean mod = false;
    if (this.modifiedClasses != null) {
      for (ClassDiffResult cdr : this.modifiedClasses) {
        if (cdr.isBodyChanged(_cid)) {
          mod = true;
          break;
        }
      }
    }
    return mod;
  }

  /**
   * Returns a {@link ConstructId} of type {@link ConstructType#PACK} in which the given construct ID has been declared.
   *
   * @param _cid a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   * @return a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   */
  public static ConstructId getPackageOf(ConstructId _cid) {
    ConstructId ccid = null;

    if (_cid.getType() == ConstructType.PACK) ccid = _cid;
    else if (_cid.getType() == ConstructType.CLAS) {
      int idx = _cid.getQname().lastIndexOf(".");
      // final String ctx = _cid.getQname().substring(0, idx);
      if (idx == -1)
        ccid = null; // throw new IllegalArgumentException("Cannot determine . in qname [" +
      // _cid.getQname() + "]");
      else
        ccid =
            new ConstructId(_cid.getLang(), ConstructType.PACK, _cid.getQname().substring(0, idx));
    } else if (_cid.getType() == ConstructType.INIT || _cid.getType() == ConstructType.METH)
      ccid = JarDiffResult.getPackageOf(JarDiffResult.getClassOf(_cid));
    else if (_cid.getType() == ConstructType.CONS)
      ccid = JarDiffResult.getPackageOf(JarDiffResult.getClassOf(_cid));
    return ccid;
  }

  /**
   * Returns a {@link ConstructId} of type {@link ConstructType#CLAS} in which the given construct ID has been declared.
   *
   * @param _cid a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   * @return a {@link org.eclipse.steady.shared.json.model.ConstructId} object.
   */
  public static ConstructId getClassOf(ConstructId _cid) {
    ConstructId ccid = null;

    if (_cid.getType() == ConstructType.PACK)
      throw new IllegalArgumentException("Cannot determine class of a package");
    else if (_cid.getType() == ConstructType.CLAS) ccid = _cid;
    else if (_cid.getType() == ConstructType.INIT || _cid.getType() == ConstructType.METH) {
      int idx = _cid.getQname().lastIndexOf(".");
      // final String ctx = _cid.getQname().substring(0, idx);
      if (idx == -1)
        throw new IllegalArgumentException("Cannot determine . in qname [" + _cid.getQname() + "]");
      else
        ccid =
            new ConstructId(_cid.getLang(), ConstructType.CLAS, _cid.getQname().substring(0, idx));
    } else if (_cid.getType() == ConstructType.CONS) {
      final int idx = _cid.getQname().lastIndexOf("(");
      if (idx == -1)
        throw new IllegalArgumentException("Cannot determine . in qname [" + _cid.getQname() + "]");
      else
        ccid =
            new ConstructId(_cid.getLang(), ConstructType.CLAS, _cid.getQname().substring(0, idx));
    }
    return ccid;
  }
}
