/*
 * TaskBusinessProcess.java
 *
 * Autor: Dominika Pawlik
 */
package dendrarium.core.entities;

import dendrarium.trees.AnswerType;
import java.io.Serializable;
import java.util.EnumSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import org.hibernate.cfg.NotYetImplementedException;

/**
 *
 * @author Dominika Pawlik
 */
@Entity
@Table(name = "TBP", uniqueConstraints = {@UniqueConstraint(columnNames = {
        "sent_id", "grammar_no"})})
@NamedQueries({
    @NamedQuery(name = "asUser1",
                query = "from TaskBusinessProcess tbp where tbp.current = TRUE and tbp.user1Answer.user=:user"),
    @NamedQuery(name = "asUser2",
                query = "from TaskBusinessProcess tbp where tbp.current = TRUE and tbp.user2Answer.user=:user"),
    @NamedQuery(name = "asSuper",
                query = "from TaskBusinessProcess tbp where tbp.current = TRUE and tbp.superAnswer.user=:user"),
    @NamedQuery(name = "collisions",
                query = "from TaskBusinessProcess tbp where tbp.current = TRUE and " +
                "((tbp.state = dendrarium.core.entities.TaskBusinessProcessState.COLLISION and " +
                "tbp.user1Answer.confirmed = TRUE and tbp.user2Answer.confirmed = TRUE) " +
                "or tbp.state = dendrarium.core.entities.TaskBusinessProcessState.INACCEPTANCE)"
                ),
    @NamedQuery(name = "solvedCollisions",
                query = "from TaskBusinessProcess tbp where tbp.current = TRUE and " +
                "tbp.state = dendrarium.core.entities.TaskBusinessProcessState.COLLISION_SOLVED"),
    @NamedQuery(name = "agreements",
                query = "from TaskBusinessProcess tbp where tbp.current = TRUE and " +
                "tbp.state = dendrarium.core.entities.TaskBusinessProcessState.AUTOACCEPTED "),
    @NamedQuery(name = "accepted",
                query = "from TaskBusinessProcess tbp where tbp.current = TRUE and " +
                "tbp.state = dendrarium.core.entities.TaskBusinessProcessState.COLLISION_SOLVED " +
                "or tbp.state = dendrarium.core.entities.TaskBusinessProcessState.AUTOACCEPTED  "),
    @NamedQuery(name = "myTBPs",
                query = "select a.taskBusinessProcess from Answer a where a.user=:user and a.taskBusinessProcess.current = TRUE"),
    @NamedQuery(name = "myInProgress1",
                query = "from TaskBusinessProcess tbp " +
                "where tbp.current = TRUE and (tbp.user1Answer.user=:user and tbp.user1Answer.accepted=true)) "),
    @NamedQuery(name = "myInProgress2",
                query = "from TaskBusinessProcess tbp " +
                "where tbp.current = TRUE and (tbp.user2Answer.user=:user and tbp.user2Answer.accepted=true)) "),
    @NamedQuery(name = "myRecent",
                query = "select tbp from TaskBusinessProcess tbp " +
                "where tbp.current = TRUE and (tbp.user1Answer != null and (tbp.user1Answer.user=:user " +
                "and tbp.user1Answer.accepted=true)) " +
                "and tbp.superAnswer = null " +
                "union " +
                "select tbp from TaskBusinessProcess tbp " +
                "where (tbp.user2Answer != null and (tbp.user2Answer.user=:user and tbp.user2Answer.accepted = true)) " +
                "and   tbp.superAnswer = null " +
                "union " +
                "select tbp from TaskBusinessProcess tbp " +
                "where (tbp.user1Answer != null and (tbp.user1Answer.user=:user and tbp.user1Answer.accepted = true)) " +
                "and   tbp.superAnswer.user = null " +
                "union " +
                "select tbp from TaskBusinessProcess tbp " +
                "where (tbp.user2Answer != null and (tbp.user2Answer.user=:user and tbp.user2Answer.accepted = true)) " +
                "and   tbp.superAnswer.user = null" // FIXME
    //              "where (tbp.user1Answer != null and tbp.user1Answer.user=:user and (tbp.state=DONE_CLEAN or tbp.state=DONE_INPROGRESS or tbp.state=COLLISION or tbp.state=AGREEMENT) "+
    //              "or    (tbp.user2Answer != null and tbp.user2Answer.user=:user and (                        tbp.state=INPROGRESS_DONE or tbp.state=COLLISION or tbp.state=AGREEMENT) "
    ),
    /* poniżej zdefiniowane zbiory są używane przy zbieraniu statystyk
     * superCollisions - liczba wszystkich kolizji do wziecia dla superdendrologa
     * superSolvedCollisions - liczba kolizji rozwiazanych przez superdendrologa
     */
    @NamedQuery(name = "superCollisionsNum",
                query = "select count(tbp) from TaskBusinessProcess tbp where (tbp.state = dendrarium.core.entities.TaskBusinessProcessState.COLLISION " +
    "or tbp.state = dendrarium.core.entities.TaskBusinessProcessState.INACCEPTANCE) and " +
    "tbp.user1Answer.confirmed = TRUE and tbp.user2Answer.confirmed = TRUE"),
    @NamedQuery(name = "superSolvedCollisionsNum",
                query = "select count(tbp) from TaskBusinessProcess tbp where tbp.superAnswer.accepted = true and " +
    "tbp.superAnswer.user = :user")
})
public class TaskBusinessProcess implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    private Task task;

    private Answer user1Answer;

    private Answer user2Answer;

    private Answer superAnswer;

    private TaskBusinessProcessState state;

    private Packet packet;

    private Integer index;

    private boolean current = false;

    @OneToOne //(mappedBy="taskBusinessProcess")
    @JoinColumn(name = "super_answer")
    public Answer getSuperAnswer() {
        return superAnswer;
    }

    @Transient
    public String getSuperAnswerType() {
        if (superAnswer == null || !superAnswer.isAccepted()) {
            return "BRAK";
        } else if (!user1Answer.isEquivalentTo(user2Answer)) {
            if (superAnswer.isEquivalentTo(user1Answer)) {
                return "ODP. 1. DENDROLOGA";
            } else if (superAnswer.isEquivalentTo(user2Answer)) {
                return "ODP. 2. DENDROLOGA";
            } else {
                return "WŁASNA ODPOWIEDŹ";
            }
        } else {
            if (superAnswer.isEquivalentTo(user1Answer)) {
                return "ZGODNOŚĆ";
            } else {
                return "WŁASNA ODPOWIEDŹ";
            }
        }
    }

    public void setSuperAnswer(Answer superAnswer) {
        this.superAnswer = superAnswer;
    }

    @Embedded
    public Task getTask() {
        return task;
    }

    public void setTask(Task task) {
        this.task = task;
    }

    @OneToOne //(mappedBy="taskBusinessProcess")
    @JoinColumn(name = "user1_answer")
    public Answer getUser1Answer() {
        return user1Answer;
    }

    public void setUser1Answer(Answer user1Answer) {
        this.user1Answer = user1Answer;
    }

    @OneToOne //(mappedBy="taskBusinessProcess")
    @JoinColumn(name = "user2_answer")
    public Answer getUser2Answer() {
        return user2Answer;
    }

    public void setUser2Answer(Answer user2Answer) {
        this.user2Answer = user2Answer;
    }

    public Integer getIndex() {
        return index;
    }

    public void setIndex(Integer index) {
        this.index = index;
    }

    @ManyToOne
    public Packet getPacket() {
        return packet;
    }

    public void setPacket(Packet packet) {
        this.packet = packet;
    }

    public void updateState() {
        EnumSet<TaskBusinessProcessState> ok_states = EnumSet.noneOf(TaskBusinessProcessState.class);

        for (TaskBusinessProcessState s : TaskBusinessProcessState.LEGAL_STATES) {
            if (s.check(this)) {
                ok_states.add(s);
            }
        }

        assert ok_states.size() <= 1;

        if (ok_states.isEmpty()) {
            this.state = TaskBusinessProcessState.ILLEGALSTATE;
        } else {
            this.state = (TaskBusinessProcessState) ok_states.toArray()[0];
        }
    }

    @Column(name = "tbp_state")
    public TaskBusinessProcessState getState() {
        if (state == null)
            updateState();
        return this.state;
    }

    public void setState(TaskBusinessProcessState state) {
//        updateState();
        this.state = state;
    }

    @Transient
    public String getDescription() {
        return task.getDescription();
    }

    public boolean inAgreement() {
        if (user1Answer == null || user2Answer == null) {
            throw new RuntimeException();
        }
        return user1Answer.isEquivalentTo(user2Answer);
    }

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public boolean isCurrent() {
        return current;
    }

    public void setCurrent(boolean current) {
        this.current = current;
    }

    public Boolean notEmptyUser1Answer() {
        return (user1Answer != null && user1Answer.isAccepted());
    }

    public Boolean notEmptyUser2Answer() {
        return (user2Answer != null && user2Answer.isAccepted());
    }

    public Boolean notEmptySuperAnswer() {
        return (superAnswer != null && superAnswer.getUser() != null &&
                superAnswer.isAccepted());
    }

    public List<Answer> gramatykTrees() {
        List<Answer> trees = new ArrayList<Answer>();
        if (user1Answer != null && user1Answer.isAccepted() &&
                user1Answer.getType().equals(AnswerType.FULL)) {
            trees.add(user1Answer);
        }
        if (user2Answer != null && user2Answer.isAccepted() &&
                user2Answer.getType().equals(AnswerType.FULL) &&
                !user2Answer.isEquivalentTo(user1Answer)) {
            trees.add(user2Answer);
        }
        if (superAnswer != null && superAnswer.isAccepted() && superAnswer.getUser() != null &&
                superAnswer.getType().equals(AnswerType.FULL) &&
                !superAnswer.isEquivalentTo(user1Answer) &&
                !superAnswer.isEquivalentTo(user2Answer)) {
            trees.add(superAnswer);
        }
        return trees;
    }

    public String answerGivers(Answer a) {
        List<String> givers = new ArrayList<String>();
        if (a.isEquivalentTo(user1Answer))
            givers.add(user1Answer.getUser().getUsername());
        if (a.isEquivalentTo(user2Answer))
            givers.add(user2Answer.getUser().getUsername());
        if (a.isEquivalentTo(superAnswer) && superAnswer.getUser() != null)
            givers.add(superAnswer.getUser().getUsername());
        StringBuffer buffer = new StringBuffer();
        Iterator iter = givers.iterator();
        if (iter.hasNext()) {
            buffer.append(iter.next());
            while (iter.hasNext()) {
                buffer.append(", ");
                buffer.append(iter.next());
            }
        }
        return buffer.toString();
    }

    public String stateDesc() {
        switch (state) {
            case CLEAN:
                return "CZYSTE-CZYSTE";
            case INPROGRESS_CLEAN:
                return "ROBI-CZYSTE";
            case DONE_CLEAN:
                return "ZROBIŁ-CZYSTE";
            case DONE_INPROGRESS:
                return "ZROBIŁ-ROBI";
            case INPROGRESS_DONE:
                return "ROBI-ZROBIŁ";
            case INPROGRESS_INPROGRESS:
                return "ROBI-ROBI";
            case COLLISION:
                return "KOLIZJA";
            case INACCEPTANCE:
                return "SUPERDENDROLOG";
            case COLLISION_SOLVED:
                if (user1Answer.isEquivalentTo(user2Answer))
                    return "INGERENCJA W ZGODNOŚĆ";
                else
                    return "KOLIZJA ROZWIĄZANA";
            case AUTOACCEPTED:
                return "ZGODNOŚĆ";
            case ILLEGALSTATE:
                return "NIEPOPRAWNY";
        }
        return "";
    }

    public String info() {
        return "HISTORIA: TBP " + id + ", packet:" + packet.getId() + ", state:"
               + state + ", user1Answer:" + (user1Answer == null? "null":user1Answer.getId())
               + ", user2Answer:" + (user2Answer == null? "null":user2Answer.getId())
               + ", superAnswer:" + (superAnswer == null? "null":superAnswer.getId())
               + ((user1Answer != null && user2Answer != null && user1Answer.isAccepted()
                   && user2Answer.isAccepted())? (", zgodność:" + inAgreement()):"");
    }

    @Override
    public boolean equals(Object object) {
        if (!(object instanceof TaskBusinessProcess)) {
            return false;
        }
        TaskBusinessProcess other = (TaskBusinessProcess) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 17 * hash + (this.id != null ? this.id.hashCode() : 0);
        hash = 17 * hash + (this.user1Answer != null ? this.user1Answer.hashCode() : 0);
        hash = 17 * hash + (this.user2Answer != null ? this.user2Answer.hashCode() : 0);
        hash = 17 * hash + (this.superAnswer != null ? this.superAnswer.hashCode() : 0);
        return hash;
    }

    public TaskBusinessProcess(Packet packet, Task task) {
        this.packet = packet;
        this.task = task;
    }

    public TaskBusinessProcess() {
    }
}
    