package com.bloatit.model.data;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.Entity;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.OrderBy;

import com.bloatit.common.FatalErrorException;
import com.bloatit.common.PageIterable;
import com.bloatit.model.data.util.SessionManager;

@Entity
public class DaoDemand extends DaoKudosable {

    @OneToOne(optional = false)
    @Cascade(value = { CascadeType.ALL, CascadeType.DELETE_ORPHAN })
    private DaoDescription description;

    @OneToOne(mappedBy = "demand", optional = true)
    @Cascade(value = { CascadeType.ALL, CascadeType.DELETE_ORPHAN })
    private DaoSpecification specification;

    @OneToMany(mappedBy = "demand")
    @Cascade(value = { CascadeType.ALL, CascadeType.DELETE_ORPHAN })
    @OrderBy(clause = "DaoOffer.popularity")
    // TODO test me !
    private Set<DaoOffer> offers = new HashSet<DaoOffer>(0);

    // TODO make sure it is read only !
    @OneToMany(mappedBy = "demand")
    @Cascade(value = { CascadeType.ALL })
    private Set<DaoContribution> contributions = new HashSet<DaoContribution>(0);

    @OneToMany
    @Cascade(value = { CascadeType.ALL, CascadeType.DELETE_ORPHAN })
    private Set<DaoComment> comments = new HashSet<DaoComment>(0);

    /**
     * It is automatically in validated state (temporary)
     * 
     * @param member the author of the demand
     * @param description
     */
    public static DaoDemand createAndPersist(DaoMember member, DaoDescription Description) {
        final Session session = SessionManager.getSessionFactory().getCurrentSession();
        final DaoDemand Demand = new DaoDemand(member, Description);
        try {
            session.save(Demand);
        } catch (final HibernateException e) {
            session.getTransaction().rollback();
            session.beginTransaction();
            throw e;
        }
        return Demand;
    }

    protected DaoDemand(DaoMember member, DaoDescription Description) {
        super(member);
        setState(State.VALIDATED);
        this.description = Description;
        this.specification = null;
    }

    protected DaoDemand() {
        super();
    }

    public void delete() {
        final Session session = SessionManager.getSessionFactory().getCurrentSession();
        session.delete(this);
    }

    public void createSpecification(DaoMember member, String content) {
        specification = new DaoSpecification(member, content, this);
    }

    public DaoOffer addOffer(DaoMember member, DaoDescription Description, Date dateExpir) {
        final DaoOffer Offer = new DaoOffer(member, this, Description, dateExpir);
        offers.add(Offer);
        return Offer;
    }

    /**
     * delete offer from this demand AND FROM DB !
     * 
     * @param Offer the offer we want to delete.
     */
    public void removeOffer(DaoOffer Offer) {
        offers.remove(Offer);
    }

    public void addContribution(DaoMember member, BigDecimal amount) {
        if (amount.compareTo(new BigDecimal("0")) <= 0) {
            throw new FatalErrorException("The amount of a contribution cannot be <= 0.", null);
        }
        contributions.add(new DaoContribution(member, amount));
    }

    public DaoSpecification getSpecification() {
        return specification;
    }

    public DaoDescription getDescription() {
        return description;
    }

    public PageIterable<DaoOffer> getOffersFromQuery() {
        final Query q = SessionManager.getSessionFactory().getCurrentSession().createQuery("from com.bloatit.model.data.DaoOffer as f where f.demand = :this");
        q.setEntity("this", this);
        return new QueryCollection<DaoOffer>(q);
    }

    public Set<DaoOffer> getOffers() {
        return offers;
    }

    public PageIterable<DaoContribution> getContributionsFromQuery() {
        final Query q = SessionManager.getSessionFactory()
                                      .getCurrentSession()
                                      .createQuery("from com.bloatit.model.data.DaoContribution as f where f.demand = :this");
        q.setEntity("this", this);
        return new QueryCollection<DaoContribution>(q);
    }

    public Set<DaoContribution> getContributions() {
        return contributions;
    }

    public PageIterable<DaoComment> getCommentsFromQuery() {
        final Query q = SessionManager.getSessionFactory()
                                      .getCurrentSession()
                                      .createQuery("from com.bloatit.model.data.DaoComment as f where f.demand = :this");
        q.setEntity("this", this);
        return new QueryCollection<DaoComment>(q);
    }

    public Set<DaoComment> getComments() {
        return comments;
    }

    public void addComment(DaoComment comment) {
        comments.add(comment);
    }

    // ======================================================================
    // For hibernate mapping
    // ======================================================================

    protected void setSpecification(DaoSpecification Specification) {
        this.specification = Specification;
    }

    protected void setDescription(DaoDescription Description) {
        this.description = Description;
    }

    protected void setOffers(Set<DaoOffer> Offers) {
        this.offers = Offers;
    }

    protected void setContributions(Set<DaoContribution> Contributions) {
        this.contributions = Contributions;
    }

    public void setComments(Set<DaoComment> comments) {
        this.comments = comments;
    }

}

@@ -127,16 +127,48 @@
 l.Set;%0A%0A
+import javax.persistence.Basic;%0A
 import j
@@ -192,16 +192,16 @@
 Entity;%0A
-
 import j
@@ -688,16 +688,83 @@
 able %7B%0A%0A
+    @Basic(optional = false)%0A    private BigDecimal contribution;%0A%0A
     @One
@@ -1744,16 +1744,34 @@
 m member
+%0A     *           
  the aut
@@ -2522,16 +2522,65 @@
 = null;%0A
+        this.contribution = new BigDecimal(%220%22);%0A
     %7D%0A%0A 
@@ -3248,16 +3248,34 @@
 am Offer
+%0A     *           
  the off
@@ -3687,16 +3687,65 @@
 ount));%0A
+        contribution = contribution.add(amount);%0A
     %7D%0A%0A 
@@ -4419,90 +4419,29 @@
 ry()
-%0A                                      .getCurrentSession()%0A                      
+.getCurrentSession()%0A
     
@@ -4852,90 +4852,29 @@
 ry()
-%0A                                      .getCurrentSession()%0A                      
+.getCurrentSession()%0A
     
@@ -5220,24 +5220,102 @@
 nt);%0A    %7D%0A%0A
+    public BigDecimal getContribution() %7B%0A        return contribution;%0A    %7D%0A%0A
     // =====
@@ -5987,24 +5987,24 @@
 comments) %7B%0A
-
         this
@@ -6032,10 +6032,121 @@
 %0A    %7D%0A%0A
+    protected void setContribution(BigDecimal contribution) %7B%0A        this.contribution = contribution;%0A    %7D%0A%0A
 %7D%0A

add a contribution counter on demands