package cc.util.bossshop.config;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.Validate;
import org.bukkit.Color;
import org.bukkit.OfflinePlayer;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.NumberConversions;
import org.bukkit.util.Vector;

import cc.util.bossshop.Function;

/**
 * 支持注释的节点
 * @author 聪聪
 *
 */
public class CommentedSection implements ConfigurationSection,ICommentedSection{

    /**值存放处,类型只能是{@link CommentedValue}*/
    protected final Map<String,CommentedValue> map=new LinkedHashMap<>();
    private final CommentedConfiguration root;
    private final CommentedSection parent;
    private final String path;
    private final String fullPath;

    protected CommentedSection(){
        if(!(this instanceof CommentedConfiguration)) throw new IllegalStateException("Cannot construct a root MemorySection when not a Configuration");
        this.path="";
        this.fullPath="";
        this.parent=null;
        this.root=(CommentedConfiguration)this;
    }

    protected CommentedSection(CommentedSection parent,String path){
        Validate.notNull(parent,"父节点不能为null");
        Validate.notNull(path,"路径不能为null");

        this.path=path;
        this.parent=parent;
        this.root=parent.getRoot();
        Validate.notNull(path,"父节点的根节点不能为空");
        this.fullPath=createPath(parent,path);
    }

    @Override
    public Set<String> getKeys(boolean deep){
        Set<String> result=new LinkedHashSet<String>();

        Configuration root=getRoot();
        if(root!=null&&root.options().copyDefaults()){
            ConfigurationSection defaults=getDefaultSection();
            if(defaults!=null){
                result.addAll(defaults.getKeys(deep));
            }
        }
        mapChildrenKeys(result,this,deep);

        return result;
    }

    @Override
    public Map<String,Object> getValues(boolean deep){
        Map<String,Object> result=new LinkedHashMap<String,Object>();

        CommentedConfiguration root=getRoot();
        if(root!=null&&root.options().copyDefaults()){
            ConfigurationSection defaults=getDefaultSection();
            if(defaults!=null){
                result.putAll(defaults.getValues(deep));
            }
        }
        this.mapChildrenValues(result,this,deep);
        return result;
    }

    @Override
    public boolean contains(String path){
        return this.get(path)!=null;
    }

    @Override
    public boolean isSet(String path){
        return true;
    }

    public String getCurrentPath(){
        return this.fullPath;
    }

    public String getName(){
        return this.path;
    }

    @Override
    public CommentedConfiguration getRoot(){
        return this.root;
    }

    @Override
    public CommentedSection getParent(){
        return this.parent;
    }

    @Override
    public Object get(String pPath){
        Validate.notNull(pPath,"获取值的路径不能为null");
        String separator=root.options().getPathSeparatorRegx();
        String[] childPathes=pPath.split(separator);
        if(childPathes.length==0) return this;

        CommentedSection section=this;
        for(int i=0;i<childPathes.length-1;i++){
            String sChildPath=childPathes[i];
            section=section.getConfigurationSection(sChildPath);
            if(section==null) return null;
        }

        String key=childPathes[childPathes.length-1];
        CommentedValue result=section.map.get(key);
        if(result==null){
            return null;
        }else{
            return result.getValue();
        }
    }

    @Override
    public Object get(String pPath,Object pDefaultValue){
        Object value=this.get(pPath);
        if(value==null) return pDefaultValue;
        else return value;
    }

    public CommentedValue getCommentedValue(String pPath){
        Validate.notEmpty(pPath,"获取包装值的路径不能为empty");
        String separator=root.options().getPathSeparatorRegx();
        String[] childPathes=pPath.split(separator);
        Validate.notEmpty(childPathes,"获取包装值的路径不能为empty");

        CommentedSection section=this;
        for(int i=0;i<childPathes.length-1;i++){
            String sChildPath=childPathes[i];
            section=section.getConfigurationSection(sChildPath);
            if(section==null) return null;
        }

        String key=childPathes[childPathes.length-1];
        return section.map.get(key);
    }
    
    public CommentedValue removeCommentedValue(String pPath){
        Validate.notEmpty(pPath,"移除包装值的路径不能为empty");
        String separator=root.options().getPathSeparatorRegx();
        String[] childPathes=pPath.split(separator);
        Validate.notEmpty(childPathes,"移除包装值的路径不能为empty");

        CommentedSection section=this;
        for(int i=0;i<childPathes.length-1;i++){
            String sChildPath=childPathes[i];
            section=section.getConfigurationSection(sChildPath);
            if(section==null) return null;
        }

        String key=childPathes[childPathes.length-1];
        return section.map.remove(key);
    }

    /**
     * 会对值进行包装与转换
     * @param pPath
     * @param pValue
     */
    @Override
    public void set(String pPath,Object pValue){
        Validate.notEmpty(pPath,"设置值的路径不能为empty ");
        String separator=root.options().getPathSeparatorRegx();
        String[] childPathes=pPath.split(separator);
        Validate.notEmpty(childPathes,"设置值的路径不能为empty ");

        CommentedSection section=this,subSection;
        for(int i=0;i<childPathes.length-1;i++){
            String sChildPath=childPathes[i];
            subSection=section.getConfigurationSection(sChildPath);
            if(subSection==null){
                section=section.createSection(sChildPath);
            }else section=subSection;
        }

        String key=childPathes[childPathes.length-1];
        if(pValue==null) section.map.remove(key);
        else{
            // 为了保留注释,不能直接put
            CommentedValue cmtedValue=section.map.get(key);
            if(cmtedValue!=null) cmtedValue.setValue(section,pValue);
            else section.map.put(key,CommentedValue.wrapperValue(section,pValue));
        }
    }

    @Override
    public CommentedSection createSection(String pPath){
        Validate.notEmpty(pPath,"不能在empty路径上创建节点");
        String separator=root.options().getPathSeparatorRegx();
        String[] childPathes=pPath.split(separator);
        Validate.notEmpty(childPathes,"不能在empty路径上创建节点");

        CommentedSection section=this,subSection;
        for(int i=0;i<childPathes.length-1;i++){
            String sChildPath=childPathes[i];
            subSection=section.getConfigurationSection(sChildPath);
            if(subSection==null){
                section=section.createSection(sChildPath);
            }else section=subSection;
        }

        String key=childPathes[childPathes.length-1];
        CommentedSection putSec=new CommentedSection(section,key);
        // 为了保留注释,不能直接put
        CommentedValue cmtedValue=this.map.get(key);
        if(cmtedValue!=null){
            cmtedValue.clearComments();
            cmtedValue.setValue(section,putSec);
        }else section.map.put(key,CommentedValue.wrapperValue(section,putSec));
        return putSec;
    }

    @Override
    public CommentedSection createSection(String pPath,Map<?,?> pMapContents){
        CommentedSection section=this.createSection(pPath);

        for(Map.Entry<?,?> entry : pMapContents.entrySet()){
            if(entry.getValue() instanceof Map){
                section.createSection(entry.getKey().toString(),(Map<?,?>)entry.getValue());
            }else{
                section.set(entry.getKey().toString(),entry.getValue());
            }
        }

        return section;
    }

    @Override
    public String getString(String path){
        Object def=this.getDefault(path);
        return getString(path,def!=null?def.toString():null);
    }

    @Override
    public String getString(String path,String def){
        Object val=this.get(path,def);
        return (val!=null)?val.toString():def;
    }

    @Override
    public boolean isString(String path){
        Object val=this.get(path);
        return val instanceof String;
    }

    public int getInt(String path){
        Object def=getDefault(path);
        return getInt(path,(def instanceof Number)?NumberConversions.toInt(def):0);
    }

    public int getInt(String path,int def){
        Object val=get(path,def);
        return (val instanceof Number)?NumberConversions.toInt(val):def;
    }

    public boolean isInt(String path){
        Object val=get(path);
        return val instanceof Integer;
    }

    public boolean getBoolean(String path){
        Object def=getDefault(path);
        return getBoolean(path,(def instanceof Boolean)?(Boolean)def:false);
    }

    public boolean getBoolean(String path,boolean def){
        Object val=get(path,def);
        return (val instanceof Boolean)?(Boolean)val:def;
    }

    public boolean isBoolean(String path){
        Object val=get(path);
        return val instanceof Boolean;
    }

    public double getDouble(String path){
        Object def=getDefault(path);
        return getDouble(path,(def instanceof Number)?NumberConversions.toDouble(def):0);
    }

    public double getDouble(String path,double def){
        Object val=get(path,def);
        return (val instanceof Number)?NumberConversions.toDouble(val):def;
    }

    public boolean isDouble(String path){
        Object val=get(path);
        return val instanceof Double;
    }

    public long getLong(String path){
        Object def=getDefault(path);
        return getLong(path,(def instanceof Number)?NumberConversions.toLong(def):0);
    }

    public long getLong(String path,long def){
        Object val=get(path,def);
        return (val instanceof Number)?NumberConversions.toLong(val):def;
    }

    public boolean isLong(String path){
        Object val=get(path);
        return val instanceof Long;
    }

    // Java
    public List<?> getList(String path){
        Object def=getDefault(path);
        return getList(path,(def instanceof List)?(List<?>)def:null);
    }

    public List<?> getList(String path,List<?> def){
        Object val=get(path,def);
        return (List<?>)((val instanceof List)?val:def);
    }

    public List<Map<?,?>> getMapList(String path){
        List<?> list=getList(path);
        List<Map<?,?>> result=new ArrayList<Map<?,?>>();

        if(list==null){
            return result;
        }

        for(Object object : list){
            if(object instanceof Map){
                result.add((Map<?,?>)object);
            }
        }

        return result;
    }

    public boolean isList(String path){
        Object val=get(path);
        return val instanceof List;
    }

    public List<String> getStringList(String path){
        List<?> list=getList(path);

        if(list==null){
            return new ArrayList<String>(0);
        }

        List<String> result=new ArrayList<String>();

        for(Object object : list){
            if((object instanceof String)||(isPrimitiveWrapper(object))){
                result.add(String.valueOf(object));
            }
        }

        return result;
    }

    public List<Integer> getIntegerList(String path){
        List<?> list=getList(path);

        if(list==null){
            return new ArrayList<Integer>(0);
        }

        List<Integer> result=new ArrayList<Integer>();

        for(Object object : list){
            if(object instanceof Integer){
                result.add((Integer)object);
            }else if(object instanceof String){
                try{
                    result.add(Integer.valueOf((String)object));
                }catch(Exception ex){}
            }else if(object instanceof Character){
                result.add((int)((Character)object).charValue());
            }else if(object instanceof Number){
                result.add(((Number)object).intValue());
            }
        }

        return result;
    }

    public List<Boolean> getBooleanList(String path){
        List<?> list=getList(path);

        if(list==null){
            return new ArrayList<Boolean>(0);
        }

        List<Boolean> result=new ArrayList<Boolean>();

        for(Object object : list){
            if(object instanceof Boolean){
                result.add((Boolean)object);
            }else if(object instanceof String){
                if(Boolean.TRUE.toString().equals(object)){
                    result.add(true);
                }else if(Boolean.FALSE.toString().equals(object)){
                    result.add(false);
                }
            }
        }

        return result;
    }

    public List<Double> getDoubleList(String path){
        List<?> list=getList(path);

        if(list==null){
            return new ArrayList<Double>(0);
        }

        List<Double> result=new ArrayList<Double>();

        for(Object object : list){
            if(object instanceof Double){
                result.add((Double)object);
            }else if(object instanceof String){
                try{
                    result.add(Double.valueOf((String)object));
                }catch(Exception ex){}
            }else if(object instanceof Character){
                result.add((double)((Character)object).charValue());
            }else if(object instanceof Number){
                result.add(((Number)object).doubleValue());
            }
        }

        return result;
    }

    public List<Float> getFloatList(String path){
        List<?> list=getList(path);

        if(list==null){
            return new ArrayList<Float>(0);
        }

        List<Float> result=new ArrayList<Float>();

        for(Object object : list){
            if(object instanceof Float){
                result.add((Float)object);
            }else if(object instanceof String){
                try{
                    result.add(Float.valueOf((String)object));
                }catch(Exception ex){}
            }else if(object instanceof Character){
                result.add((float)((Character)object).charValue());
            }else if(object instanceof Number){
                result.add(((Number)object).floatValue());
            }
        }

        return result;
    }

    public List<Long> getLongList(String path){
        List<?> list=getList(path);

        if(list==null){
            return new ArrayList<Long>(0);
        }

        List<Long> result=new ArrayList<Long>();

        for(Object object : list){
            if(object instanceof Long){
                result.add((Long)object);
            }else if(object instanceof String){
                try{
                    result.add(Long.valueOf((String)object));
                }catch(Exception ex){}
            }else if(object instanceof Character){
                result.add((long)((Character)object).charValue());
            }else if(object instanceof Number){
                result.add(((Number)object).longValue());
            }
        }

        return result;
    }

    public List<Byte> getByteList(String path){
        List<?> list=getList(path);

        if(list==null){
            return new ArrayList<Byte>(0);
        }

        List<Byte> result=new ArrayList<Byte>();

        for(Object object : list){
            if(object instanceof Byte){
                result.add((Byte)object);
            }else if(object instanceof String){
                try{
                    result.add(Byte.valueOf((String)object));
                }catch(Exception ex){}
            }else if(object instanceof Character){
                result.add((byte)((Character)object).charValue());
            }else if(object instanceof Number){
                result.add(((Number)object).byteValue());
            }
        }

        return result;
    }

    public List<Character> getCharacterList(String path){
        List<?> list=getList(path);

        if(list==null){
            return new ArrayList<Character>(0);
        }

        List<Character> result=new ArrayList<Character>();

        for(Object object : list){
            if(object instanceof Character){
                result.add((Character)object);
            }else if(object instanceof String){
                String str=(String)object;

                if(str.length()==1){
                    result.add(str.charAt(0));
                }
            }else if(object instanceof Number){
                result.add((char)((Number)object).intValue());
            }
        }

        return result;
    }

    public List<Short> getShortList(String path){
        List<?> list=getList(path);

        if(list==null){
            return new ArrayList<Short>(0);
        }

        List<Short> result=new ArrayList<Short>();

        for(Object object : list){
            if(object instanceof Short){
                result.add((Short)object);
            }else if(object instanceof String){
                try{
                    result.add(Short.valueOf((String)object));
                }catch(Exception ex){}
            }else if(object instanceof Character){
                result.add((short)((Character)object).charValue());
            }else if(object instanceof Number){
                result.add(((Number)object).shortValue());
            }
        }

        return result;
    }

    public Vector getVector(String path){
        Object def=getDefault(path);
        return getVector(path,(def instanceof Vector)?(Vector)def:null);
    }

    public Vector getVector(String path,Vector def){
        Object val=get(path,def);
        return (val instanceof Vector)?(Vector)val:def;
    }

    public boolean isVector(String path){
        Object val=get(path);
        return val instanceof Vector;
    }

    public OfflinePlayer getOfflinePlayer(String path){
        Object def=getDefault(path);
        return getOfflinePlayer(path,(def instanceof OfflinePlayer)?(OfflinePlayer)def:null);
    }

    public OfflinePlayer getOfflinePlayer(String path,OfflinePlayer def){
        Object val=get(path,def);
        return (val instanceof OfflinePlayer)?(OfflinePlayer)val:def;
    }

    public boolean isOfflinePlayer(String path){
        Object val=get(path);
        return val instanceof OfflinePlayer;
    }

    public ItemStack getItemStack(String path){
        Object def=getDefault(path);
        return getItemStack(path,(def instanceof ItemStack)?(ItemStack)def:null);
    }

    public ItemStack getItemStack(String path,ItemStack def){
        Object val=get(path,def);
        return (val instanceof ItemStack)?(ItemStack)val:def;
    }

    public boolean isItemStack(String path){
        Object val=get(path);
        return val instanceof ItemStack;
    }

    public Color getColor(String path){
        Object def=getDefault(path);
        return getColor(path,(def instanceof Color)?(Color)def:null);
    }

    public Color getColor(String path,Color def){
        Object val=get(path,def);
        return (val instanceof Color)?(Color)val:def;
    }

    public boolean isColor(String path){
        Object val=get(path);
        return val instanceof Color;
    }

    @Override
    public CommentedSection getConfigurationSection(String path){
        Object val=get(path,null);
        if(val!=null){
            return (val instanceof CommentedSection)?(CommentedSection)val:null;
        }

        val=get(path,getDefault(path));
        return (val instanceof ConfigurationSection)?createSection(path):null;
    }

    @Override
    public boolean isConfigurationSection(String path){
        Object val=get(path);
        return val instanceof CommentedSection;
    }

    protected boolean isPrimitiveWrapper(Object input){
        return input instanceof Integer||input instanceof Boolean||input instanceof Character||input instanceof Byte||input instanceof Short||input instanceof Double||input instanceof Long||input instanceof Float;
    }

    @Override
    public void addDefault(String path,Object value){
        Validate.notNull(path,"要添加默认值的路径不能为null");
        if(root==this){
            throw new UnsupportedOperationException("不支持的方法addDefault(String, Object)");
        }
        root.addDefault(createPath(this,path),value);
    }

    public boolean contains(String arg0,boolean arg1){
        return this.get(path)!=null;
    }

    @Override
    public CommentedSection getDefaultSection(){
        CommentedConfiguration root=getRoot();
        CommentedConfiguration defaults=root==null?null:root.getDefaults();

        if(defaults!=null){
            if(defaults.isConfigurationSection(getCurrentPath())){
                return defaults.getConfigurationSection(getCurrentPath());
            }
        }
        return null;
    }

    protected Object getDefault(String path){
        Validate.notNull(path,"获取默认值的路径不能为null");

        CommentedConfiguration root=this.getRoot();
        CommentedConfiguration defaults=root==null?null:root.getDefaults();
        return (defaults==null)?null:defaults.get(createPath(this,path));
    }

    protected void mapChildrenKeys(Set<String> output,CommentedSection section,boolean deep){
        if(section instanceof CommentedSection){
            CommentedSection sec=(CommentedSection)section;

            for(Map.Entry<String,CommentedValue> entry : sec.map.entrySet()){
                output.add(createPath(section,entry.getKey(),this));
                if((deep)&&(entry.getValue().getValue() instanceof CommentedSection)){
                    CommentedSection subsection=(CommentedSection)entry.getValue().getValue();
                    mapChildrenKeys(output,subsection,deep);
                }
            }
        }else{
            Set<String> keys=section.getKeys(deep);
            for(String key : keys){
                output.add(createPath(section,key,this));
            }
        }
    }

    protected void mapChildrenValues(Map<String,Object> output,CommentedSection section,boolean deep){
        if(section instanceof CommentedSection){
            CommentedSection sec=(CommentedSection)section;

            for(Map.Entry<String,CommentedValue> entry : sec.map.entrySet()){
                output.put(createPath(section,entry.getKey(),this),entry.getValue().getValue());
                if(entry.getValue().getValue() instanceof CommentedSection){
                    if(deep){
                        mapChildrenValues(output,(CommentedSection)entry.getValue().getValue(),deep);
                    }
                }
            }
        }else{
            Map<String,Object> values=section.getValues(deep);

            for(Map.Entry<String,Object> entry : values.entrySet()){
                output.put(createPath(section,entry.getKey(),this),entry.getValue());
            }
        }
    }

    public static String createPath(CommentedSection section,String key){
        return createPath(section,key,(section==null)?null:section.getRoot());
    }

    public static String createPath(CommentedSection section,String key,CommentedSection relativeTo){
        Validate.notNull(section,"不能从null节点创建路径");
        
        CommentedConfiguration root=section.getRoot();
        char separator=root.options().pathSeparator();

        StringBuilder builder=new StringBuilder();
        if(section!=null){
            for(ConfigurationSection parent=section;(parent!=null)&&(parent!=relativeTo);parent=parent.getParent()){
                if(builder.length()>0){
                    builder.insert(0,separator);
                }

                builder.insert(0,parent.getName());
            }
        }

        if((key!=null)&&(key.length()>0)){
            if(builder.length()>0){
                builder.append(separator);
            }

            builder.append(key);
        }

        return builder.toString();
    }

    @Override
    public String toString(){
        CommentedConfiguration root=this.getRoot();
        StringBuilder builder=new StringBuilder("{path='"+getCurrentPath()+"'");
        builder.append(", root='").append(root==null?null:root.getClass().getSimpleName());
        builder.append(", MapValue=").append(this.map).append("}");
        return builder.toString();
    }

    @Override
    public void addComments(String pPath,String...pComments){
        CommentedValue tValue=this.getCommentedValue(pPath);
        if(tValue!=null) tValue.addComments(pComments);
        else this.setComments(pPath,pComments);
    }

    @Override
    public void addDefaultComments(String pPath,String...pComments){
        CommentedValue tValue=this.getCommentedValue(pPath);
        if(tValue!=null) tValue.addDefaultComments(pComments);
        else this.setComments(pPath,pComments);
    }
    

    @Override
    public void setComments(String pPath,String...pComments){
        CommentedValue tValue=this.getCommentedValue(pPath);
        if(tValue!=null) tValue.setComments(pComments);
        else if(pComments!=null) this.set(pPath,Function.asList(pComments));
    }
    
    public void setComments(String pPath,Collection<String> pComments){
        CommentedValue tValue=this.getCommentedValue(pPath);
        if(tValue!=null) tValue.setComments(pComments);
        else if(pComments!=null){
            CommentedSection newSec=this.createSection(pPath);
            tValue=newSec.getParent().map.get(newSec.path);
            tValue.setComments(pComments);
        }
    }

    @Override
    public ArrayList<String> getComments(String pPath){
        CommentedValue tValue=this.getCommentedValue(pPath);
        if(tValue!=null) return tValue.getComments();
        else return new ArrayList<>();
    }

    @Override
    public boolean hasComments(String pPath){
        CommentedValue tValue=this.getCommentedValue(pPath);
        if(tValue!=null) return tValue.hasComments();
        else return false;
    }

    @Override
    public ArrayList<String> clearComments(String pPath){
        CommentedValue tValue=this.getCommentedValue(pPath);
        if(tValue!=null) return tValue.clearComments();
        else return new ArrayList<>();
    }

}
