package objectstructures;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

public class Person {

  private String name;
  private char gender;

  private List<Person> children = new ArrayList<>();
  private Optional<Person> mother = Optional.empty();
  private Optional<Person> father = Optional.empty();

  public Person(String name, char gender) {
    this.name = name;
    this.gender = gender;
  }

  public String getName() {
    return name;
  }

  public char getGender() {
    return gender;
  }

  public Collection<Person> getChildren() {
    return children;
  }

  public int getChildCount() {
    return children.size();
  }

  public Person getChild(int n) {
    if (0 > n || n >= this.children.size())
      throw new IllegalArgumentException("Child " + n + " doesn't exist");
    return this.children.get(n);
  }

  public Person getMother() {
    return mother.orElse(null);
  }

  public Person getFather() {
    return father.orElse(null);
  }

  public void addChild(Person child) {
    this.children.add(child);

    if (this.gender == 'M' && child.getFather() != this)
      child.setFather(this);
    else if (this.gender == 'F' && child.getMother() != this)
      child.setMother(this);
  }

  public void removeChild(Person child) {
    this.children.remove(child);

    if (this.gender == 'M' && child.getFather() == this)
      child.setFather(null);
    else if (this.gender == 'F' && child.getMother() == this)
      child.setMother(null);
  }

  private void setParent(Person newParent,
                         Optional<Person> currentParent,
                         char gender)
  {
    if (newParent != null && newParent.getGender() != gender)
      throw new IllegalArgumentException("Gender mismatch");
    if (newParent == this)
      throw new IllegalArgumentException("A person can not be its own parent");

    var previousParent = currentParent;
    currentParent = Optional.ofNullable(newParent);

    if (newParent.getGender() == 'M')
      this.father = Optional.ofNullable(newParent);
    else 
      this.mother = Optional.ofNullable(newParent);    

    currentParent.ifPresent(p -> {
      if (!p.getChildren().contains(this)) p.addChild(this);
    });
    previousParent.ifPresent(p -> p.removeChild(this));

  }

  public void setMother(Person mother) {
    setParent(mother, this.mother, 'F');
  }

  public void setFather(Person father) {
    setParent(father, this.father, 'M');
  }

}
