/*
 * ICESOFT COMMERCIAL SOURCE CODE LICENSE V 1.1
 *
 * The contents of this file are subject to the ICEsoft Commercial Source
 * Code License Agreement V1.1 (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.icesoft.com/license/commercial-source-v1.1.html
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * Copyright 2009-2015 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 */

package com.icesoft.faces.demo.poll;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.Collections;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.faces.event.ActionEvent;
import javax.servlet.http.HttpSession;

import org.icefaces.application.PortableRenderer;
import org.icefaces.application.PushMessage;

public class Poll
implements Serializable {
    private static final Logger LOGGER = Logger.getLogger(Poll.class.getName());

    public static final String POLL_GROUP_NAME = "poll";

    private final Set<String> againstSet = new HashSet<String>();
    private final Set<String> forSet = new HashSet<String>();

    private transient final ReentrantLock voteLock = new ReentrantLock();

    private transient final PollCollection pollCollection;
    private transient final PortableRenderer portableRenderer;
    private transient final PollMessageService pollMessageService;

    private String id;
    private String statement;
    private String detail;
    private long lastActivityTime = System.currentTimeMillis();

    public Poll(
        final String statement, final String detail, final PollCollection pollCollection,
        final PortableRenderer portableRenderer, final PollMessageService pollMessageService) {

        this(UUID.randomUUID().toString(), statement, detail, pollCollection, portableRenderer, pollMessageService);
        String _realm = ParticipantView.getInstance().getRealm();
        if (_realm != null) {
            this.id = _realm + ":" + this.id;
        }
    }

    Poll(
        final String id, final String statement, final String detail, final PollCollection pollCollection,
        final PortableRenderer portableRenderer, final PollMessageService pollMessageService) {

        this.id = id;
        setStatement(statement);
        setDetail(detail);
        this.pollCollection = pollCollection;
        this.portableRenderer = portableRenderer;
        this.pollMessageService = pollMessageService;
    }

    public int getAgainstVotes() {
        return againstSet.size();
    }

    public String getDetail() {
        return detail;
    }

    public int getForVotes() {
        return forSet.size();
    }

    public String getId() {
        return id;
    }

    public String getStatement() {
        return statement;
    }

    public boolean isSelected() {
        return ParticipantView.getInstance().getSelectedPoll() == this;
    }

    public boolean isVotedAgainstByParticipant() {
        return Participant.getInstance().hasVotedAgainst(this);
    }

    public boolean isVotedForByParticipant() {
        return Participant.getInstance().hasVotedFor(this);
    }

    public void setDetail(final String detail) {
        this.detail = detail;
    }

    public void setSelected(final boolean selected) {
        if (selected) {
            ParticipantView.getInstance().setSelectedPoll(this);
        }
    }

    public void setStatement(final String statement) {
        this.statement = statement;
    }

    public String toString() {
        return "Poll(id: '" + id + "', statement: '" + statement + "')";
    }

    public void voteAgainst(final ActionEvent event) {
        lastActivityTime = System.currentTimeMillis();
        voteAgainst(Participant.getInstance().getId(), true);
    }

    public void voteFor(final ActionEvent event) {
        lastActivityTime = System.currentTimeMillis();
        voteFor(Participant.getInstance().getId(), true);
    }

    long getLastActivityTime() {
        return lastActivityTime;
    }

    Set<String> getAgainstSet() {
        return Collections.unmodifiableSet(againstSet);
    }

    Set<String> getForSet() {
        return Collections.unmodifiableSet(forSet);
    }

    void voteAgainst(final String participantId, final boolean broadcast) {
        voteLock.lock();
        try {
            boolean _modified = false;
            int _before = new Integer(forSet.size()).compareTo(againstSet.size());
            if (forSet.contains(participantId)) {
                forSet.remove(participantId);
                _modified = true;
            }
            if (!againstSet.contains(participantId)) {
                againstSet.add(participantId);
                _modified = true;
            }
            int _after = new Integer(forSet.size()).compareTo(againstSet.size());
            if (_modified && broadcast) {
                Participant.getInstance().voteAgainst(id);
                if (pollCollection.getPushMode().equalsIgnoreCase(Constants.PushMode.PUSH)) {
                    portableRenderer.render(POLL_GROUP_NAME);
                } else if (pollCollection.getPushMode().equalsIgnoreCase(Constants.PushMode.CLOUD_PUSH)) {
                    if ((_before < 0 && _after < 0) ||
                        (_before == 0 && _after == 0) ||
                        (_before > 0 && _after > 0)) {

                        portableRenderer.render(POLL_GROUP_NAME);
                    } else {
                        if (_after < 0) {
                            portableRenderer.render(
                                POLL_GROUP_NAME,
                                new PushMessage("Poll", "Results changed to against: " + getStatement()));
                        } else if (_after > 0) {
                            portableRenderer.render(
                                POLL_GROUP_NAME,
                                new PushMessage("Poll", "Results changed to for: " + getStatement()));
                        } else {
                            portableRenderer.render(
                                POLL_GROUP_NAME,
                                new PushMessage("Poll", "Results changed to a tie: " + getStatement()));
                        }
                    }
                }
                if (pollMessageService != null) {
                    pollMessageService.publishNow(
                        id + "\r\n" + participantId,
                        PollMessageService.VOTE_AGAINST_MESSAGE_TYPE);
                }
            }
        } finally {
            voteLock.unlock();
        }
    }

    void voteFor(final String participantId, final boolean broadcast) {
        voteLock.lock();
        try {
            boolean _modified = false;
            int _before = new Integer(forSet.size()).compareTo(againstSet.size());
            if (againstSet.contains(participantId)) {
                againstSet.remove(participantId);
                _modified = true;
            }
            if (!forSet.contains(participantId)) {
                forSet.add(participantId);
                _modified = true;
            }
            int _after = new Integer(forSet.size()).compareTo(againstSet.size());
            if (_modified && broadcast) {
                Participant.getInstance().voteFor(id);
                if (pollCollection.getPushMode().equalsIgnoreCase(Constants.PushMode.PUSH)) {
                    portableRenderer.render(Poll.POLL_GROUP_NAME);
                } else if (pollCollection.getPushMode().equalsIgnoreCase(Constants.PushMode.CLOUD_PUSH)) {
                    if ((_before < 0 && _after < 0) ||
                        (_before == 0 && _after == 0) ||
                        (_before > 0 && _after > 0)) {

                        portableRenderer.render(Poll.POLL_GROUP_NAME);
                    } else {
                        if (_after < 0) {
                            portableRenderer.render(
                                POLL_GROUP_NAME,
                                new PushMessage("Poll", "Results changed to against: " + getStatement()));
                        } else if (_after > 0) {
                            portableRenderer.render(
                                POLL_GROUP_NAME,
                                new PushMessage("Poll", "Results changed to for: " + getStatement()));
                        } else {
                            portableRenderer.render(
                                POLL_GROUP_NAME,
                                new PushMessage("Poll", "Results changed to a tie: " + getStatement()));
                        }
                    }
                }
                if (pollMessageService != null) {
                    pollMessageService.publishNow(
                        id + "\r\n" + participantId,
                        PollMessageService.VOTE_FOR_MESSAGE_TYPE);
                }
            }
        } finally {
            voteLock.unlock();
        }
    }
}
