package com.apobates.forum.utils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
/**
 * 前端分页的连接地址
 * @author xiaofanku@live.cn
 * @since 20180807
 */
public final class FrontPageURL implements Serializable{
	private static final long serialVersionUID = 8019254639004479518L;
	//包含协议,schema,domain,path
    private final String baseURI;
    //查询字符串,key=查询字符串的名字,value=值
    private final Map<String,List<String>> params = new HashMap<>();
    /**
     * 每页显示记录的最大上限:100
     */
    public final static int MAX_PAGE_SIZE=100;
    /**
     * 每页显示记录的最小下限:5
     */
    public final static int MIN_PAGE_SIZE=5;
    /**
     * 每页显示记录的默认值:50
     */
    public final static int DEF_PAGE_SIZE=50;
    private String pageSizeKeyName;
    
    /**
     * 使用路径初始化
     * @param baseURI 
     */
    public FrontPageURL(String baseURI) {
        this.baseURI = baseURI;
    }
    /**
     * 使用请求的当前地址初始化
     * @param request http 请求
     * @param dropParamterName 希望过滤的查询参数名称
     */
    public FrontPageURL(HttpServletRequest request, String dropParamterName) {
        this.baseURI = request.getRequestURL().toString();
        parseQueryString(request.getQueryString(), dropParamterName);
    }
    /**
     * 为地址增加查询参数值
     * @param parameterName 查询参数名称
     * @param parameterValue 查询参数值
     * @return 
     */
    public FrontPageURL addParameter(String parameterName, String parameterValue){
        this.params.put(parameterName, addIfNoContains(parameterName, parameterValue));
        return this;
    }
    /**
     * 为地址增加查询参数值
     * @param parameterName 查询参数名称
     * @param parameterValues 查询参数值数组
     * @return 
     */
    public FrontPageURL addParameter(String parameterName, String[] parameterValues){
        this.params.put(parameterName, addAllIfNoContains(parameterName, parameterValues));
        return this;
    }
    /**
     * 为地址增加查询参数值
     * @param parameterName 查询参数名称
     * @param parameterValue 查询参数值
     * @return 
     */
    public FrontPageURL addParameter(String parameterName, int parameterValue){
        this.params.put(parameterName, addIfNoContains(parameterName, parameterValue+""));
        return this;
    }
    /**
     * 为地址增加查询参数值
     * @param parameterName 查询参数名称
     * @param parameterValues 查询参数值数组
     * @return 
     */
    public FrontPageURL addParameter(String parameterName, int[] parameterValues){
        this.params.put(parameterName, addAllIfNoContains(parameterName, Arrays.stream(parameterValues).mapToObj(String::valueOf).toArray(String[]::new)));
        return this;
    }
    /**
     * 为地址增加查询参数值
     * @param parameterName 查询参数名称
     * @param parameterValue 查询参数值
     * @return 
     */
    public FrontPageURL addParameter(String parameterName, long parameterValue){
        this.params.put(parameterName, addIfNoContains(parameterName, parameterValue+""));
        return this;
    }
    /**
     * 为地址增加查询参数值
     * @param parameterName 查询参数名称
     * @param parameterValues 查询参数值数组
     * @return 
     */
    public FrontPageURL addParameter(String parameterName, long[] parameterValues){
        this.params.put(parameterName, addAllIfNoContains(parameterName, Arrays.stream(parameterValues).mapToObj(String::valueOf).toArray(String[]::new)));
        return this;
    }
    /**
     * 为地址增加分页时每页显示的记录数
     * @param parameterName 查询参数名称
     * @param pageSize 每页显示的记录数
     * @return 
     */
    public FrontPageURL addPageSize(String parameterName, int pageSize){
        this.params.put(parameterName, addIfNoContains(parameterName, String.valueOf(checkPageSizeRange(pageSize))));
        this.pageSizeKeyName = parameterName;
        return this;
    }
    /**
     * 获取当前实例中的每页显示的记录数
     * @return 如果先前没有调用addPageSize方法返回默认的值:50
     */
    public int getPageSize(){
        if(null==this.pageSizeKeyName){
            return DEF_PAGE_SIZE;
        }
        int ps = DEF_PAGE_SIZE;
        List<String> vs = getKeyOfValue(this.pageSizeKeyName);
        if(vs.size() == 1){
            try{
                ps = Integer.valueOf(vs.get(0));
            }catch(IndexOutOfBoundsException | NumberFormatException e){}
        }
        return ps;
    }
    private List<String> getKeyOfValue(String key){
        List<String> data=null;
        try{
            data = this.params.get(key);
        }catch(ClassCastException | NullPointerException e){}
        if(null==data){
            data = new ArrayList<>();
        }
        return data;
    }
    private List<String> addIfNoContains(String key, String value){
        List<String> vs = getKeyOfValue(key);
        if(vs.isEmpty() || !vs.contains(value)){
            vs.add(value);
        }
        return vs;
    }
    private List<String> addAllIfNoContains(String key, String[] values){
        Set<String> unionRs = new HashSet<>(getKeyOfValue(key));
        unionRs.addAll(Arrays.asList(values));
        return new ArrayList<>(unionRs);
    }
    /**
     * 输出当前地址实例的结构,是一个路径 + ? + 查询字符串
     * html表单多选值,返回查询字符串格式为:key=value1,value2
     * html表单单选值,返回查询字符串格式为:key=value
     * @return 如果无查询字符串返回路径
     */
    @Override
    public String toString(){
        if(this.params.isEmpty()){
            return this.baseURI;
        }
        
        StringBuilder sb = new StringBuilder();
        sb.append(this.baseURI);
        sb.append("?");
        for(Entry<String,List<String>> entry : this.params.entrySet()){
            sb.append(entry.getKey());
            sb.append("=");
            
            List<String> vs = entry.getValue();
            if(entry.getKey().equalsIgnoreCase(pageSizeKeyName)){
                try{
                    sb.append(checkPageSizeRange(Integer.valueOf(vs.get(0))));
                }catch(IndexOutOfBoundsException | NumberFormatException e){
                    sb.append(DEF_PAGE_SIZE);
                }
            }else{
                sb.append(String.join(",", vs));
            }
            sb.append("&");
        }
        //截掉最后一个字符
        try{
            return sb.toString().substring(0, sb.length() - 1);
        }catch(StringIndexOutOfBoundsException e){
            return this.baseURI;
        }
    }

    //将 {a=b&c=d&e=f}放到map中
    //multi select {a=b&a=c&a=d&c=d&e=f&e=g}
    //matrix variables {/owners/42;q=11/pets/21;q=22} or {/owners/42;q=11;r=12/pets/21;q=22;s=23}
    private void parseQueryString(String queryParams, String dropParamterName){
        if(null==queryParams){
            return;
        }
        String[] paramArray = queryParams.split("&");
        for(String param : paramArray){
            String[] _kv = param.split("=");
            try{
                if(_kv.length!=2){
                    continue;
                }
                if(dropParamterName != null && _kv[0].equals(dropParamterName)){
                    continue;
                }
                if(!Commons.isNotBlank(_kv[1])){ //空值
                    continue;
                }
                this.params.put(_kv[0], addIfNoContains(_kv[0], _kv[1]));
            }catch(NullPointerException | IndexOutOfBoundsException e){}
        }
    }
    private int checkPageSizeRange(int pageSize){
        return (pageSize < MIN_PAGE_SIZE || pageSize > MAX_PAGE_SIZE)?DEF_PAGE_SIZE:pageSize;
    }
}
