package tutorial.persistence.solutions;

import java.util.*;
import javax.persistence.*;

@Entity(name = "Rabbit")
public class Rabbit
    extends Animal {

    // the resale price of a rabbit
    public static final float RESALE_PRICE = 3.0f;

    @Basic
    @Column(name = "GENDER")
    private boolean isFemale = false;

    @Basic
    @Column(name = "EXPIRED")
    private boolean isDead = false;

    @ManyToMany
    @JoinTable(name = "RABBIT_CHILDREN",
        joinColumns = @JoinColumn(name = "PARENT_ID"),
        inverseJoinColumns = @JoinColumn(name = "CHILD_ID"))
    private Set<Rabbit> children = new HashSet<Rabbit>();

    @ManyToMany(mappedBy = "children")
    private Set<Rabbit> parents = new HashSet<Rabbit>();

    @ManyToOne
    @JoinColumn(name = "EATER_ID")
    private Snake eater;

    // some rabbit names; used by breedHelper () to name new rabbits
    private static final String[] maleRabbitNames = {
        "Bugs",
        "Peter",
        "Roger",
        "Buttons",
        "John",
        "Ed",
        "Elvis",
    };
    private static final String[] femaleRabbitNames = {
        "Babs",
        "Fluffy",
        "Cottontail",
        "Daisie",
        "Poofy",
        "Flower",
        "Britney",
    };

    public Rabbit(String name, boolean isFemale) {
        super(name, RESALE_PRICE);
        this.isFemale = isFemale;
    }

    public Rabbit(String name, Rabbit mother, Rabbit father, boolean isFemale) {
        super(name, RESALE_PRICE);
        parents.add(mother);
        parents.add(father);
        this.isFemale = isFemale;
    }

    /**
     * Return <code>true</code> if this rabbit is female; else
     * <code>false</code>.
     */
    public boolean isFemale() {
        return isFemale;
    }

    /**
     * Designate that this rabbit is dead.
     */
    public void kill() {
        isDead = true;
    }

    /**
     * Adds <code>child</code> to this rabbit's children collection.
     * Performs no validation.
     */
    public void addChild(Rabbit child) {
        children.add(child);
    }

    public Snake getEater() {
        return eater;
    }

    public void setEater(Snake snake) {
        eater = snake;
    }

    public String toString(boolean detailed) {
        StringBuffer buf = new StringBuffer(512);
        buf.append("Rabbit ").append(getName());
        buf.append(" (").append((isFemale) ? "female" : "male").append(")");

        if (detailed) {
            buf.append(" sells for ").append(getPrice());
            buf.append(" dollars.\n\tparents: ").append(parents);
            buf.append("\n\tchildren: ").append(children);
        } else
            buf.append("; has " + children.size() + " children.");

        return buf.toString();
    }

    public static void main(String[] args) {
        // create some new rabbits
        try {
            if (args.length == 2 && args[0].equals("breed")) {
                int iters = Integer.parseInt(args[1]);
                EntityManagerFactory emf = Persistence.
                    createEntityManagerFactory(null);
                EntityManager em = emf.createEntityManager();
                breed(em, iters);
                em.close();
                emf.close();
                return;
            }
        }
        catch (NumberFormatException e) {
        }

        // if we get here, something went wrong
        System.out.println("Usage:");
        System.out.println("\tjava tutorial.persistence.Rabbit breed "
            + "'iterations'");
    }

    /**
     * Find two random compatible rabbits in the database, toss them
     * in a dark room for a little while, and repeat <code>iterations</code>
     * times.
     */
    public static void breed(EntityManager em, int iterations) {
        em.getTransaction().begin();
        while (iterations > 0) {
            breedHelper(em);
            iterations--;
        }
        em.getTransaction().commit();
    }

    private static void breedHelper(EntityManager em) {
        Random random = new Random();

        // find a female rabbit
        Query femaleQuery = em.createQuery("select r from Rabbit r"
            + " where r.isFemale = true and r.isDead = false");

        List females = femaleQuery.getResultList();
        if (females.isEmpty())
            throw new RuntimeException("No females in db. Please create one.");
        Rabbit female = (Rabbit) females.get(random.nextInt(females.size()));

        // find a male rabbit
        Query maleQuery = em.createQuery("select r from Rabbit r"
            + " where r.isFemale = false and r.isDead = false");
        List males = maleQuery.getResultList();
        if (males.isEmpty())
            throw new RuntimeException("No males in db. Please create one.");
        Rabbit male = (Rabbit) males.get(random.nextInt(males.size()));

        // breed the two
        for (int count = random.nextInt(3) + 1; count > 0; count--) {
            boolean isFemale = random.nextBoolean();
            String[] names;
            if (isFemale)
                names = femaleRabbitNames;
            else
                names = maleRabbitNames;
            String name = names[random.nextInt(names.length)];
            Rabbit kid = new Rabbit(name, female, male, isFemale);
            female.addChild(kid);
            male.addChild(kid);
            em.persist(kid);
            System.out.println("New rabbit born: " + kid);
		}
	}
}