/*
Tiamat - a role-playing game framework
SkillCollection.java - used to store a collection of skills
 
 Copyright  (C) 2004-2005 Hugues Johnson
 
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
the GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package com.huguesjohnson.tiamat;

import java.lang.String;
import java.lang.StringBuffer;
import java.util.ArrayList;
import com.huguesjohnson.tiamat.NamedObjectTreeNode;
import com.huguesjohnson.tiamat.Skill;
import com.huguesjohnson.tiamat.Target;
import com.huguesjohnson.tiamat.TreeNode;

/** SkillCollection - used to store a collection of skills
 * @author Hugues Johnson
 */
public class SkillCollection{

/* begin private members */
    NamedObjectTreeNode rootNode;
/* end private members */

/* begin private constants */
    private final String ROOT_NAME="SKILLS";
    private final String ROOT_DESCRIPTION="Root node for SkillCollection";
/* end private constants */

/* begin constructors */
    /** 
     * Default constructor, uses default pathDelimiter.
     */
    public SkillCollection(){
        this.rootNode=new NamedObjectTreeNode(new Skill(this.ROOT_NAME,this.ROOT_DESCRIPTION));
    }
    /** 
     * Create a new SkillCollection with a custom path delimiter.
     * @param pathDelimiter The new path delimiter to use.
     */
    public SkillCollection(String pathDelimiter){
        this.rootNode=new NamedObjectTreeNode(new Skill(this.ROOT_NAME,this.ROOT_DESCRIPTION),pathDelimiter);
   }
    /** 
     * Create a new SkillCollection based off an array of Skills with a the default path delimiter.
     * @param skills The initial set of Skills to add to the collection.
     */
    public SkillCollection(Skill[] skills){
        this.rootNode=new NamedObjectTreeNode(new Skill(this.ROOT_NAME,this.ROOT_DESCRIPTION));
        int length=skills.length;
        for(int index=0;index<length;index++){
            NamedObjectTreeNode node=new NamedObjectTreeNode(skills[index],this.rootNode);
        }
    }
    /** 
     * Create a new SkillCollection based off an array of Skills with a the default path delimiter.
     * @param skills The initial set of Skills to add to the collection.
     * @param pathDelimiter The new path delimiter to use.
     */
    public SkillCollection(Skill[] skills,String pathDelimiter){
        this.rootNode=new NamedObjectTreeNode(new Skill(this.ROOT_NAME,this.ROOT_DESCRIPTION),pathDelimiter);
        int length=skills.length;
        for(int index=0;index<length;index++){
            NamedObjectTreeNode node=new NamedObjectTreeNode(skills[index],this.rootNode);
        }
    }
/* end constructors */

/* begin public methods */
    /** 
     * Removes the Skill with the specified path.
     * @param path The path of the Skill to remove.
     * @return The success of the operation.
     */
    public boolean removeSkill(String path){
        return(this.rootNode.removeChildNode(path,true));
    }
    /**
     * Returns the Skill with the specified path if it exists, otherwise null.
     * @param path The path of the Skill to retreive. 
     * @return The Skill with the specified path if it exists, otherwise null.
     */
    public Skill getSkill(String path){
        Object object=this.rootNode.getChildNode(path,true);
        if(object==null){
            return(null);
        } else if(object instanceof NamedObjectTreeNode){
            Object namedObject=((NamedObjectTreeNode)object).getObject();
            if(namedObject==null){
                return(null);
            } else if(namedObject instanceof Skill){
                return((Skill)object);
            } else{ //found something that's not an Skill, shouldn't happen
                return(null);
            }
        } else{
            return(null);
        }
    }
    /**
     * Sets the Skill with the specified path, adds it if it doesn't exist.
     * @param path The path of the Skill to set.
     * @param skill The Skill to set. 
     */
    public void setSkill(String path,Skill skill){
        TreeNode node=this.rootNode.getChildNode(path+skill.getName(),true);
        if(node==null){
            NamedObjectTreeNode newNode=new NamedObjectTreeNode(skill,this.rootNode.getPathDelimiter());
            this.rootNode.insertChildNode(path,newNode);
        } else{
            node.setObject(skill);
        }
    }
    /** 
     * Returns the total number of Skills in the collection. 
     * @return The total number of Skills in the collection.
     */
    public int getCount(){
        return(this.rootNode.getChildLeafCount(true));
    }
    /** Returns the total number of Skills starting from the specified path. 
     * @param path The starting path to count from.
     * @return The total number of Skills starting from the specified path. 
     */
    public int getCount(String path){
        TreeNode node=this.rootNode.getChildNode(path,true);
        if(node==null){
            return(-1);
        } 
        return(node.getChildLeafCount(true));
    }
    /** 
     * Returns a collection of all Skills that have a target with a name equal to targetPath. 
     * @param targetPath The target name to look for.
     * @return A collection of all Skills that have a target with a name equal to targetPath.
     */
    public SkillCollection getModifiersFor(String targetPath){
        TreeNode[] nodes=this.rootNode.getChildLeaves(true);
        int length=nodes.length;
        ArrayList<Skill> returnList=new ArrayList<Skill>(length);
        for(int index=0;index<length;index++){
            if(nodes[index] instanceof NamedObjectTreeNode){
                Object namedObject=((NamedObjectTreeNode)nodes[index]).getObject();
                if(namedObject!=null){
                    if(namedObject instanceof Skill){
                        Target[] targets=((Skill)namedObject).getTargets();
                        int targetsLength=targets.length;
                        int targetIndex=0;
                        int foundIndex=-1;
                        while((targetIndex<targetsLength)&&(foundIndex<0)){
                            if(targets[targetIndex].getTargetPath().equals(targetPath)){
                                foundIndex=targetIndex;
                            } else{
                                targetIndex++;
                            }
                        }
                        if(foundIndex>=0){
                            returnList.add((Skill)namedObject);
                        }
                    }
                }
            }
        }
        if(returnList.size()<1){
            return(new SkillCollection());
        } 
        return(new SkillCollection(returnList.toArray(new Skill[0])));
    }
    /** 
     * This method is used to get all the modifiers for a target and return the sum of their values.
     * @param targetPath The target name to look for.
     * @return The sum of modifiers for a target.
     */
    public int getNetModification(String targetPath){
        int netModification=0;
        Skill[] skills=this.getModifiersFor(targetPath).getAll();
        if(skills!=null){
            int length=0;
            for(int index=0;index<length;index++){
                Target[] targets=skills[index].getTargets();
                int targetsLength=targets.length;
                int targetIndex=0;
                int foundIndex=-1;
                while((targetIndex<targetsLength)&&(foundIndex<0)){
                    if(targets[targetIndex].getTargetPath().equals(targetPath)){
                        foundIndex=targetIndex;
                    } else{
                        targetIndex++;
                    }
                }
                if(foundIndex>=0){ //*should* always be found
                    netModification+=targets[foundIndex].getValue();
                }
            }
        }
        return(netModification);
    }
    /** 
     * Adjusts the values of all Skills in the collection by the given percentage, useful for updating all Skills uniformly for gaining/losing a level. 
     * @param percentage The percentage to adjust all Skills by.
     */
    public void adjustAllSkills(int percentage){
        this.adjustSkills(this.rootNode.getName(),percentage);
    }
    /** 
     * Adjusts the values of all Skills under the rootPath by the given percentage, useful for updating all Skills uniformly for gaining/losing a level.
     * @param rootPath The path name to start at.
    * @param percentage The percentage to adjust all Skills by.
    */
    public void adjustSkills(String rootPath,int percentage){
        TreeNode rootNode=this.rootNode.getChildNode(rootPath,true);
        if(rootNode!=null){
            TreeNode childNodes[]=rootNode.getChildLeaves(true);
            if(childNodes!=null){
                int length=childNodes.length;
                for(int index=0;index<length;index++){
                    if(childNodes[index] instanceof NamedObjectTreeNode){
                        Object object=((NamedObjectTreeNode)childNodes[index]).getObject();
                        if(object!=null){
                            if(object instanceof Skill){
                                Skill skill=(Skill)object;
                                int targetCount=skill.getTargetCount();
                                for(int targetIndex=0;targetIndex<targetCount;targetIndex++){
                                    Target target=skill.getTarget(targetIndex);
                                    target.setValue(target.getValue()*percentage);
                                    skill.setTarget(index,target);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    /** 
     * Returns all Skills in the collection.
     * @return All Skills in the collection. 
     */
    public Skill[] getAll(){
        TreeNode[] nodes=this.rootNode.getChildLeaves(true);
        int length=nodes.length;
        ArrayList<Skill> skillList=new ArrayList<Skill>(length);
        for(int index=0;index<length;index++){
            Object object=nodes[index].getObject();
            if(object!=null){
                if(object instanceof Skill){
                    skillList.add((Skill)object);
                }
            }
        }
        return(skillList.toArray(new Skill[0]));
    }        
    /** 
     * Returns a string representation of the object.
     * @return A string representation of the object.
     */
    public String toString(){
        StringBuffer tostring=new StringBuffer(this.getClass().getName());
        //tostring.append("\n  member="+this.member);
        return(tostring.toString());
    }
/* end public methods */
}