package util;

import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.event.ValueChangeEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

public class SelectionList<T> {
    private ReentrantLock lock;
    private boolean allItemsSelected;
    private List<SelectionWrapper<T>> items;

    public SelectionList () {
        items = new ArrayList<SelectionWrapper<T>>();
        lock = new ReentrantLock();
    }

    public boolean isEmpty () {
        lock.lock();

        try {
            this.items.clear();
            return items.isEmpty();
        }
        finally {
            lock.unlock();
        }
    }

    public List<SelectionWrapper<T>> getItems () {
        lock.lock();

        try {
            return items;
        }
        finally {
            lock.unlock();
        }
    }

    public void setItems (List<T> itemsToWrap) {
        lock.lock();

        try {
            this.items.clear();

            if (itemsToWrap != null) {
                for (T item : itemsToWrap) {
                    this.items.add(new SelectionWrapper<T>(item));
                }
            }
        }
        finally {
            lock.unlock();
        }
    }

    public List<T> getSelectedItems () {
        lock.lock();

        try {
            List<T> selectedItems = new ArrayList<T>();

            for (SelectionWrapper<T> wrappedItem : items) {
                if (wrappedItem.isSelected()) {
                    selectedItems.add(wrappedItem.getObject());
                }
            }

            return selectedItems;
        }
        finally {
            lock.unlock();
        }
    }

    public List<T> getUnwrappedItems () {
        lock.lock();

        try {
            List<T> unwrappedItems = new ArrayList<T>();

            for (SelectionWrapper<T> wrappedItem : items) {
                unwrappedItems.add(wrappedItem.getObject());
            }

            return unwrappedItems;
        }
        finally {
            lock.unlock();
        }
    }

    public void toggleAllItemsSelectedAjax (AjaxBehaviorEvent e) {
        lock.lock();

        try {
            for (SelectionWrapper<T> item : items) {
                item.setSelected(this.allItemsSelected);
            }
        }
        finally {
            lock.unlock();
        }
    }

    public void checkAllSelected (AjaxBehaviorEvent ae) {
        for (int i = 0; i < getItems().size(); i++) {
            if (getItems().get(i).isSelected()) {
                allItemsSelected = true;
            } else {
                allItemsSelected = false;
                break;
            }
        }
    }

    public void selectAllItems () {
        lock.lock();

        try {
            for (SelectionWrapper<T> item : items) {
                item.setSelected(true);
            }
        }
        finally {
            lock.unlock();
        }
    }

    public void unselectAllItems () {
        lock.lock();

        try {
            for (SelectionWrapper<T> item : items) {
                item.setSelected(false);
            }
        }
        finally {
            allItemsSelected = false;
            lock.unlock();
        }
    }

    public void updateAllItemsSelected (ValueChangeEvent e) {
        updateAllItemsSelected();
    }

    public void updateAllItemsSelected () {
        lock.lock();

        try {
            this.allItemsSelected = false;

            if (!items.isEmpty()) {
                for (SelectionWrapper<T> item : items) {
                    if (!item.isSelected()) {
                        return;
                    }
                }

                this.allItemsSelected = true;
            }
        }
        finally {
            lock.unlock();
        }
    }

    public boolean isAllItemsSelected () {
        lock.lock();

        try {
            if (items.isEmpty()) {
                this.allItemsSelected = false;
            }

            return allItemsSelected;
        }
        finally {
            lock.unlock();
        }
    }

    public void setAllItemsSelected (boolean allItemsSelected) {
        this.allItemsSelected = allItemsSelected;
    }

    public void addObject (T item) {
        lock.lock();

        try {
            SelectionWrapper<T> tmpItem = new SelectionWrapper<T>(item);
            tmpItem.setSelected(true);
            items.add(tmpItem);
        }
        finally {
            lock.unlock();
        }
    }

    public void addObjectNotSelect (T item) {
        lock.lock();

        try {
            SelectionWrapper<T> tmpItem = new SelectionWrapper<T>(item);
            tmpItem.setSelected(false);
            items.add(tmpItem);
        }
        finally {
            lock.unlock();
        }
    }

    public void removeObjectAtIndex (int index) {
        lock.lock();

        try {
            items.remove(index);
        }
        finally {
            lock.unlock();
        }
    }

    public void clearItems () {
        if (items != null) {
            items.clear();
        }
    }
}
