package org.firestarterr.dozkantv.web.support;

import org.apache.commons.lang3.StringUtils;
import org.firestarterr.dozkantv.core.pagination.model.OrderBy;
import org.firestarterr.dozkantv.core.pagination.model.Pager;
import org.firestarterr.dozkantv.core.pagination.model.Pagination;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public abstract class BaseListSupport<E, PK> extends BaseSupport<E, PK> {

    private static final int PAGINATION_RANGE = 6;
    private int currentPage = 0;
    private int maxResult = 5;
    private int resultCount = 0;

    private List<E> resultList = null;

    private OrderBy orderBy = new OrderBy(null);

    protected abstract void initResultList();

    public abstract String navToView(E instance);

    public abstract String navToCreate();

    @Override
    protected void callInitFunctions() throws Exception {
        checkSecured("initResultList");
        search();
    }

    protected void applyResult(Pagination<E> pagination) {
        setResultList(pagination.getResult());
        setResultCount(pagination.getCount());
    }

    protected void resetResultList() {
        resultList = null;
        currentPage = 0;
        resultCount = 0;
    }

    public void search() {
        resetResultList();
        initResultList();
    }

    public void reset() {
        resetResultList();
        initResultList();
    }

    public List<E> getResultList() {
        return resultList;
    }


    public void setResultList(List<E> resultList) {
        this.resultList = resultList;
    }

    public boolean hasNext() {
        return currentPage < getPageSize() - 1;
    }

    public boolean hasPrevious() {
        return currentPage > 0;
    }

    public void next() {
        if (hasNext()) {
            currentPage++;
            initResultList();
        }
    }

    public void previous() {
        if (hasPrevious()) {
            currentPage--;
            initResultList();
        }
    }

    public void jumpToPage(int page) {
        currentPage = page;
        initResultList();
    }

    public void initOrderBy(String column) {
        setOrderByColumn(column);
        initResultList();
    }

    public List<PageLink> getPageNums() {
        List<PageLink> nums = new ArrayList<PageLink>();
        for (int i = 0; i < getPageSize(); i++) {
            nums.add(new PageLink(i));
        }
        LinkedList<PageLink> slice = new LinkedList<PageLink>(getSliceContainsIndex(nums, getCurrentPage(), PAGINATION_RANGE, true));
        if (!slice.isEmpty()) {
            PageLink first = slice.get(0);
            PageLink last = slice.get(slice.size() - 1);
            if (first.getPageNum() != 0) {
                slice.add(0, new PageLink("...", first.getPageNum() - PAGINATION_RANGE));
            }
            if (last.getPageNum() != getPageSize() - 1) {
                slice.add(new PageLink("...", last.getPageNum() + 1));
            }
        }
        return slice;
    }


    public int getFirstResult() {
        return getCurrentPage() * getMaxResult();
    }

    public int getPageSize() {
        getResultList();
        return (int) Math.ceil((double) getResultCount() / (double) getMaxResult());
    }

    public int getCurrentPage() {
        return currentPage;
    }

    public void setCurrentPage(int currentPage) {
        this.currentPage = currentPage;
    }

    public int getResultCount() {
        return resultCount;
    }

    public void setResultCount(int resultCount) {
        this.resultCount = resultCount;
    }

    public int getMaxResult() {
        return maxResult;
    }

    public void setMaxResult(int maxResult) {
        this.maxResult = maxResult;
    }

    public class PageLink {
        private String label;
        private int pageNum;

        private PageLink(String label, int pageNum) {
            super();
            this.label = label;
            this.pageNum = pageNum;
        }

        private PageLink(int pageNum) {
            super();
            this.pageNum = pageNum;
            this.label = "" + (this.pageNum + 1);
        }

        public String getLabel() {
            return label;
        }

        public int getPageNum() {
            return pageNum;
        }

    }


    public <T> List<T> getSliceContainsIndex(List<T> list, int current, int sliceSize, boolean mergeResidual) {
        List<T> result = new ArrayList<T>();
        int currentNum = current + 1;
        int remaining = currentNum % sliceSize;
        int start;
        if (currentNum < sliceSize) {
            start = 1;
        } else if (remaining == 0) {
            start = currentNum - sliceSize + 1;
        } else {
            start = 1 + currentNum - remaining;
        }
        int finish = start + sliceSize;
        if (finish > list.size() + 1) {
            finish = list.size() + 1;
        }
        if (mergeResidual) {
            if (start > 1 && finish - start < sliceSize / 2 && list.size() + 1 - finish < sliceSize / 2) {
                start = start - sliceSize;
            }
            if (finish < list.size() + 1) {
                int nextFinish = finish + sliceSize;
                if (nextFinish > list.size() + 1) {
                    nextFinish = list.size() + 1;
                }
                if (nextFinish - finish < sliceSize / 2 && list.size() + 1 - nextFinish < sliceSize / 2) {
                    finish = list.size() + 1;
                }
            }
        }

        if (start > -1 && finish > start) {
            for (int i = start; i < finish; i++) {
                result.add(list.get(i - 1));
            }
        }

        return result;
    }

    public OrderBy getOrderBy() {
        return orderBy;
    }

    public void setOrderBy(OrderBy orderBy) {
        this.orderBy = orderBy;
    }

    private void setOrderByColumn(String column) {
        if (StringUtils.equals(orderBy.getName(), column)) {
            orderBy.setAsc(!orderBy.isAsc());
        } else {
            orderBy.setName(column);
            orderBy.setAsc(true);
        }
    }

    public Pager getPager() {
        return new Pager(getFirstResult(), getMaxResult(), getOrderBy());
    }
}
