/*******************************************************************************
 * Copyright (c) 2007 Intel Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Intel Corporation - Initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.build.internal.core.scannerconfig;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
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 org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry;
import org.eclipse.cdt.core.settings.model.ICSettingBase;
import org.eclipse.cdt.core.settings.model.extension.CConfigurationData;
import org.eclipse.cdt.core.settings.model.extension.CFileData;
import org.eclipse.cdt.core.settings.model.extension.CFolderData;
import org.eclipse.cdt.core.settings.model.extension.CLanguageData;
import org.eclipse.cdt.core.settings.model.extension.CResourceData;
import org.eclipse.cdt.core.settings.model.util.CDataUtil;
import org.eclipse.cdt.core.settings.model.util.IPathSettingsContainerVisitor;
import org.eclipse.cdt.core.settings.model.util.PathSettingsContainer;
import org.eclipse.cdt.make.core.scannerconfig.IDiscoveredPathManager;
import org.eclipse.cdt.make.core.scannerconfig.PathInfo;
import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;

public class PerFileSettingsCalculator {
	private static final String[] EMPTY_STRING_ARRAY  = new String[0];
//	private static class ListIndex {
//	int fIndex;
//	List fList;
//
//	public ListIndex(int index, List list) {
//		fIndex = index;
//		fList = list;
//	}
//}

	public interface IRcSettingInfo {
		CResourceData getResourceData();

		ILangSettingInfo[] getLangInfos();
	}

	public interface ILangSettingInfo {
		CLanguageData getLanguageData();

		PathInfo getFilePathInfo();
	}

	private static class RcSettingInfo implements IRcSettingInfo{
		private ArrayList<ILangSettingInfo> fLangInfoList;
		private CResourceData fRcData;

		RcSettingInfo(CResourceData rcData){
			fRcData = rcData;
		}

		@Override
		public ILangSettingInfo[] getLangInfos() {
			if(fLangInfoList != null && fLangInfoList.size() != 0)
				return fLangInfoList.toArray(new ILangSettingInfo[fLangInfoList.size()]);
			return new ILangSettingInfo[0];
		}

		@Override
		public CResourceData getResourceData() {
			return fRcData;
		}

		void add(ILangSettingInfo info){
			if(fLangInfoList == null)
				fLangInfoList = new ArrayList<ILangSettingInfo>();
			fLangInfoList.add(info);
		}
	}

	private static class LangSettingInfo implements ILangSettingInfo {
		private CLanguageData fLangData;
		private PathInfo fPathInfo;

		LangSettingInfo(CLanguageData langData, PathInfo info){
			fLangData = langData;
			fPathInfo = info;
		}

		@Override
		public PathInfo getFilePathInfo() {
			return fPathInfo;
		}

		@Override
		public CLanguageData getLanguageData() {
			return fLangData;
		}

	}

	private static class ListIndexStore {
		private int fMaxIndex;
		private List<PathFilePathInfo>[] fStore;

		@SuppressWarnings("unchecked")
		private List<PathFilePathInfo>[] emptyStore(int size) {
			return new List[size];
		}

		public ListIndexStore(int size){
			if(size < 0)
				size = 0;

			fStore = emptyStore(size);
		}

		public void add(int index, PathFilePathInfo value){
			List<PathFilePathInfo> list = checkResize(index) ? new ArrayList<PathFilePathInfo>() : fStore[index];
			if(list == null){
				list = new ArrayList<PathFilePathInfo>();
				fStore[index] = list;
			}

			if(fMaxIndex < index)
				fMaxIndex = index;

			list.add(value);
		}

		private boolean checkResize(int index){
			if(index >= fStore.length){
				int newSize = ++index;
				List<PathFilePathInfo> resized[] = emptyStore(newSize);
				if(fStore != null && fStore.length != 0){
					System.arraycopy(fStore, 0, resized, 0, fStore.length);
				}
				fStore = resized;
				return true;
			}
			return false;
		}

		public List<PathFilePathInfo>[] getLists(){
			int size = fMaxIndex + 1;
			List<List<PathFilePathInfo>> list = new ArrayList<List<PathFilePathInfo>>(size);
			List<PathFilePathInfo> l;
			for(int i = 0; i < size; i++){
				l = fStore[i];
				if(l != null)
					list.add(l);
			}

			return list.toArray(emptyStore(list.size()));
		}
	}

	private static class PathFilePathInfo {
		IPath fPath;
		PathInfo fInfo;

		PathFilePathInfo(IPath path, PathInfo info){
			fPath = path;
			fInfo = info;
		}
	}

	private static class ExtsSetSettings {
//		String[] fExts;
//		HashSet fExtsSet;
		private ExtsSet fExtsSet;
		Map<PathInfo, List<PathFilePathInfo>> fPathFilePathInfoMap;
		CLanguageData fBaseLangData;
		boolean fIsDerived;
		private PathInfo fMaxMatchInfo;
		private List<PathFilePathInfo> fMaxMatchInfoList;
		private int fHash;

		public ExtsSetSettings(CLanguageData baseLangData, ExtsSet extsSet, boolean isDerived) {
			fExtsSet = extsSet;
			fBaseLangData = baseLangData;
			fIsDerived = isDerived;
		}

		void add(ExtsSetSettings setting){
			if(setting.fPathFilePathInfoMap != null){
				Collection<List<PathFilePathInfo>> values = setting.fPathFilePathInfoMap.values();
				for (List<PathFilePathInfo> list : values) {
					for (PathFilePathInfo pInfo : list) {
						add(pInfo);
					}
				}
			}
		}

//		void updateLangData(CLanguageData lData, boolean isDerived){
//			fBaseLangData = lData;
//			fIsDerived = lData != null ? isDerived : false;
//		}

		public void add(PathFilePathInfo pInfo){
			if(fPathFilePathInfoMap == null)
				fPathFilePathInfoMap = new HashMap<PathInfo, List<PathFilePathInfo>>(3);

			PathInfo fileInfo = pInfo.fInfo;
			List<PathFilePathInfo> list = fileInfo == fMaxMatchInfo ? fMaxMatchInfoList : fPathFilePathInfoMap.get(fileInfo);
			if(list == null){
				List<PathFilePathInfo> emptyList = new ArrayList<PathFilePathInfo>();
				fPathFilePathInfoMap.put(fileInfo, emptyList);
				if(fMaxMatchInfo == null){
					fMaxMatchInfo = fileInfo;
					fMaxMatchInfoList = emptyList;
				}
//				else {
//					fIsMultiple = true;
//				}
			} else if(fMaxMatchInfoList != list){
//				fIsMultiple = true;

				if(fMaxMatchInfoList.size() == list.size()){
					fMaxMatchInfoList = list;
					fMaxMatchInfo = fileInfo;
				}
			}
			list.add(pInfo);
		}

		public boolean isMultiple(){
			return fPathFilePathInfoMap != null && fPathFilePathInfoMap.size() > 1;
		}

//		public PathInfo getMaxMatchPathInfo(){
//			return fMaxMatchInfo;
//		}

		@Override
		public boolean equals(Object obj) {
			if(obj == this)
				return true;

			if(isMultiple())
				return false;

			if(!(obj instanceof ExtsSetSettings))
				return false;

			ExtsSetSettings other = (ExtsSetSettings)obj;
			if(other.isMultiple())
				return false;

			if(!fExtsSet.equals(other.fExtsSet))
				return false;

			if(!CDataUtil.objectsEqual(fMaxMatchInfo, other.fMaxMatchInfo))
				return false;

			return true;
		}

		@Override
		public int hashCode() {
			int hash = fHash;
			if(hash == 0){
				if(isMultiple())
					hash = super.hashCode();
				else {
					hash = fExtsSet.hashCode();

					if(fMaxMatchInfo != null)
						hash += fMaxMatchInfo.hashCode();
				}

				fHash = hash;
			}
			return hash;
		}

	}

	private static class ExtsSet {
		private String[] fExts;
		private HashSet<String> fExtsSet;
		private int fHash;

		public ExtsSet(String[] exts){
			fExts = exts == null || exts.length == 0 ? EMPTY_STRING_ARRAY : (String[])exts.clone();
		}

		@Override
		public boolean equals(Object obj) {
			if(this == obj)
				return true;

			if(!(obj instanceof ExtsSet))
				return false;

			ExtsSet other = (ExtsSet)obj;
			if(fExts.length != other.fExts.length)
				return false;

			if(fExts.length != 0){
				@SuppressWarnings("unchecked")
				HashSet<String> set = (HashSet<String>)calcExtsSet().clone();
				set.removeAll(other.calcExtsSet());
				if(set.size() != 0)
					return false;
			}
			return true;
		}

//		public String[] getExtensions(){
//			return fExts.clone();
//		}

		@Override
		public int hashCode() {
			int hash = fHash;
			if(hash == 0){
				hash = 47;
				for(int i = 0; i < fExts.length; i++){
					hash += fExts[i].hashCode();
				}
				fHash = hash;
			}
			return hash;
		}

		private HashSet<String> calcExtsSet(){
			if(fExtsSet == null)
				fExtsSet = new HashSet<String>(Arrays.asList(fExts));
			return fExtsSet;
		}

		@Override
		public String toString() {
			if(fExts.length == 0)
				return "<empty>"; //$NON-NLS-1$

			StringBuffer buf = new StringBuffer();

			for(int i = 0; i < fExts.length; i++){
				if(i != 0)
					buf.append(","); //$NON-NLS-1$
				buf.append(fExts[i]);
			}
			return buf.toString();
		}
	}

	private static class RcSetSettings {
		private CResourceData fRcData;
		private HashMap<String, ExtsSetSettings> fExtToExtsSetMap;
		private HashMap<ExtsSet, ExtsSetSettings> fExtsSetToExtsSetSettingsMap;
		private PathSettingsContainer fContainer;
		private boolean fIsDerived;

		RcSetSettings(PathSettingsContainer cr, CResourceData rcData, boolean isDerived){
			this.fContainer = cr;
			this.fRcData = rcData;
			this.fIsDerived = isDerived;
			cr.setValue(this);
		}

		public RcSetSettings getChild(IPath path, boolean exactPath){
			PathSettingsContainer cr = fContainer.getChildContainer(path, false, exactPath);
			if(cr != null)
				return (RcSetSettings)cr.getValue();
			return null;
		}

		public RcSetSettings getChild(IPath path){
			PathSettingsContainer cr = fContainer.getChildContainer(path, false, true);
			if(cr != null)
				return (RcSetSettings)cr.getValue();
			return null;
		}

//		public CResourceData getResourceData() {
//			return fRcData;
//		}

		public RcSetSettings createChild(IPath path, CResourceData data, boolean isDerived){
			PathSettingsContainer cr = fContainer.getChildContainer(path, true, true);
			RcSetSettings child = (RcSetSettings)cr.getValue();
			if(child == null){
				child = new RcSetSettings(cr, data, isDerived);
//				cr.setValue(child);
			}
			return child;
		}

		void updateRcData(CResourceData data, boolean isDerived){
			fRcData = data;
			fIsDerived = data != null ? isDerived : false;
			updateLangDatas();
		}

		private void updateLangDatas(){
			if(fRcData.getType() == ICSettingBase.SETTING_FILE){
				CLanguageData lData = ((CFileData)fRcData).getLanguageData();
				ExtsSetSettings extSetting = fExtToExtsSetMap.get(getFileExt(fRcData.getPath()));
				if(extSetting != null){
					extSetting.fBaseLangData = lData;
					extSetting.fIsDerived = lData != null ? fIsDerived : false;
				}

				if(extSetting != null ?
						fExtsSetToExtsSetSettingsMap.size() > 1
						: fExtsSetToExtsSetSettingsMap.size() > 0){
					Collection<ExtsSetSettings> values = fExtsSetToExtsSetSettingsMap.values();
					for (ExtsSetSettings s : values) {
						if(s != extSetting){
							s.fBaseLangData = null;
							s.fIsDerived = false;
						}
					}
				}
			} else {
				CLanguageData[] lDatas = ((CFolderData)fRcData).getLanguageDatas();
				@SuppressWarnings("unchecked")
				Map<ExtsSet, ExtsSetSettings> map = (HashMap<ExtsSet, ExtsSetSettings>)fExtsSetToExtsSetSettingsMap.clone();

				CLanguageData lData;
				for(int i = 0; i < lDatas.length; i++){
					lData = lDatas[i];
					ExtsSetSettings extSetting = map.remove(new ExtsSet(lData.getSourceExtensions()));
					if(extSetting != null){
						extSetting.fBaseLangData = lData;
						extSetting.fIsDerived = this.fIsDerived;
					}
				}

				if(map.size() != 0){
					Collection<ExtsSetSettings> values = map.values();
					for (ExtsSetSettings extSetting : values) {
						extSetting.fBaseLangData = null;
						extSetting.fIsDerived = false;
					}
				}
			}
		}

		public IPath getPath(){
			return fContainer.getPath();
		}

		public RcSetSettings getParent(){
			PathSettingsContainer cr = fContainer.getParentContainer();
			if(cr != null)
				return (RcSetSettings)cr.getValue();
			return null;
		}

		void internalSetSettingsMap(HashMap<ExtsSet, ExtsSetSettings> map){
			fExtsSetToExtsSetSettingsMap = map;
			fExtToExtsSetMap = calcExtToExtSetSettingsMap(map);
		}

		void internalAdd(ExtsSetSettings setting){
			if(fExtsSetToExtsSetSettingsMap == null){
				fExtsSetToExtsSetSettingsMap = new HashMap<ExtsSet, ExtsSetSettings>();
			}

			ExtsSetSettings cur = fExtsSetToExtsSetSettingsMap.get(setting.fExtsSet);
			if(cur != null){
				cur.add(setting);
			} else {
				fExtsSetToExtsSetSettingsMap.put(setting.fExtsSet, setting);
				fExtToExtsSetMap = addExtsInfoToMap(setting, fExtToExtsSetMap);
			}
		}

		void internalAddSettingsMap(HashMap<ExtsSet, ExtsSetSettings> map){
//			ExtsSetSettings  thisSetting;
//			ExtsSet extsSet;
			Collection<ExtsSetSettings> values = map.values();
			for (ExtsSetSettings setting : values) {
				internalAdd(setting);
//				extsSet = setting.fExtsSet;
//				thisSetting = (ExtsSetSettings)fExtsSetToExtsSetSettingsMap.get(extsSet);
//				if(thisSetting != null){
//					thisSetting.add(setting);
//				} else {
//					fExtsSetToExtsSetSettingsMap.put(extsSet, setting);
//					fExtToExtsSetMap = addExtsInfoToMap(setting, fExtToExtsSetMap);
//				}
			}
		}

//		public boolean settingsEqual(RcSetSettings other){
//			return fExtsSetToExtsSetSettingsMap.equals(other.fExtsSetToExtsSetSettingsMap);
//		}

		public RcSetSettings[] getChildren(final boolean includeCurrent){
			final List<RcSetSettings> list = new ArrayList<RcSetSettings>();
			fContainer.accept(new IPathSettingsContainerVisitor(){

				@Override
				public boolean visit(PathSettingsContainer container) {
					if(includeCurrent || container != fContainer){
						RcSetSettings value = (RcSetSettings)container.getValue();
						list.add(value);
					}
					return true;
				}

			});

			return list.toArray(new RcSetSettings[list.size()]);
		}

		public boolean containsEqualMaxMatches(RcSetSettings other, boolean ignoreGenerated){
			if(!ignoreGenerated && fExtsSetToExtsSetSettingsMap.size() < other.fExtsSetToExtsSetSettingsMap.size())
				return false;

			Set<Entry<ExtsSet, ExtsSetSettings>> entrySet = other.fExtsSetToExtsSetSettingsMap.entrySet();
			for (Entry<ExtsSet, ExtsSetSettings> entry : entrySet) {
				ExtsSetSettings otherSetting = entry.getValue();
				if(ignoreGenerated && otherSetting.fBaseLangData == null)
					continue;

				ExtsSetSettings thisSetting = fExtsSetToExtsSetSettingsMap.get(entry.getKey());
				if(thisSetting == null)
					return false;

				if(otherSetting.fMaxMatchInfo != null && !otherSetting.fMaxMatchInfo.equals(thisSetting.fMaxMatchInfo))
					return false;
			}

			return true;
		}

		void removeChild(RcSetSettings setting){
			IPath path = setting.fContainer.getPath();
			IPath thisPath = fContainer.getPath();
			if(!thisPath.isPrefixOf(path))
				throw new IllegalArgumentException();

			path = path.removeFirstSegments(thisPath.segmentCount());
			fContainer.removeChildContainer(path);
		}
	}

	private static HashMap<String, ExtsSetSettings> calcExtToExtSetSettingsMap(Map<ExtsSet, ExtsSetSettings> extsSetMap){
		HashMap<String, ExtsSetSettings> result = null;
		Collection<ExtsSetSettings> values = extsSetMap.values();
		for (ExtsSetSettings setting : values) {
			result = addExtsInfoToMap(setting, result);
		}
		return result;
	}

	private static HashMap<String, ExtsSetSettings> addExtsInfoToMap(ExtsSetSettings setting, HashMap<String, ExtsSetSettings> map){
		boolean forceAdd = false;
		String[] exts = setting.fExtsSet.fExts;
		String ext;
		if(map == null){
			map = new HashMap<String, ExtsSetSettings>();
			forceAdd = true;
		}

		for(int i = 0; i < exts.length; i++){
			ext = exts[i];
			if(forceAdd || !map.containsKey(ext)){
				map.put(ext, setting);
			}
		}
		return map;
	}

	private RcSetSettings createRcSetInfo (CConfigurationData data){
		CFolderData rootData = data.getRootFolderData();
		PathSettingsContainer container = PathSettingsContainer.createRootContainer();
		RcSetSettings rcSet = new RcSetSettings(container, rootData, false);
		rcSet.internalSetSettingsMap(createExtsSetSettingsMap(rootData));
//		rcSet.fExtToExtsSetMap = new HashMap();
//		rcSet.fExtsSetToExtsSetSettingsMap = new HashMap();

		CResourceData[] rcDatas = data.getResourceDatas();
		CResourceData rcData;
		RcSetSettings curRcSet;
		HashMap<ExtsSet, ExtsSetSettings> fileMap;
		ExtsSetSettings fileSetting;
		IPath path;

		for(int i = 0; i < rcDatas.length; i++){
			rcData = rcDatas[i];
			if(rcData == rootData)
				continue;

			if(!includeRcDataInCalculation(data, rcData))
				continue;

			path = rcData.getPath();
			curRcSet = rcSet.createChild(path, rcData, false);
			if(rcData.getType() == ICSettingBase.SETTING_FILE){
				fileMap = new HashMap<ExtsSet, ExtsSetSettings>(1);
				fileSetting = createExtsSetSettings(path, (CFileData)rcData);
				fileMap.put(fileSetting.fExtsSet, fileSetting);
				curRcSet.internalSetSettingsMap(fileMap);
			} else {
				curRcSet.internalSetSettingsMap(createExtsSetSettingsMap((CFolderData)rcData));
			}
		}

		return rcSet;
	}

	protected boolean includeRcDataInCalculation(CConfigurationData cfgData, CResourceData rcData){
		return true;
	}

	protected CFileData createFileData(CConfigurationData cfgData, IPath path, CFileData base) throws CoreException{
		return cfgData.createFileData(path, base);
	}

	protected CFileData createFileData(CConfigurationData cfgData, IPath path, CFolderData base, CLanguageData langBase) throws CoreException{
		return cfgData.createFileData(path, base, langBase);
	}

	protected CFolderData createFolderData(CConfigurationData cfgData, IPath path, CFolderData base) throws CoreException{
		return cfgData.createFolderData(path, base);
	}

	private RcSetSettings createRcSetSettings(CConfigurationData data, IDiscoveredPathManager.IPerFileDiscoveredPathInfo2 discoveredInfo){
		RcSetSettings rcSet = createRcSetInfo(data);
		Map<IResource, PathInfo> map = discoveredInfo.getPathInfoMap();
		PathFilePathInfo pInfos[] = createOrderedInfo(map);
		mapDiscoveredInfo(rcSet, pInfos);
		checkRemoveDups(rcSet);
		return rcSet;
	}

	/*
	 * utility method for creating empty IRcSettingInfo
	 */
	public static IRcSettingInfo createEmptyRcSettingInfo(CFolderData data){
		RcSettingInfo rcInfo = new RcSettingInfo(data);
		CLanguageData[] lDatas = data.getLanguageDatas();
		addEmptyLanguageInfos(rcInfo, lDatas);
		return rcInfo;
	}

	private static void addEmptyLanguageInfos(RcSettingInfo rcInfo, CLanguageData[] lDatas){
		ArrayList<ILangSettingInfo> list = rcInfo.fLangInfoList;
		if(list == null){
			list = new ArrayList<ILangSettingInfo>(lDatas.length);
			rcInfo.fLangInfoList = list;
		} else {
			list.ensureCapacity(lDatas.length);
		}

		for(int i = 0; i < lDatas.length; i++){
			list.add(new LangSettingInfo(lDatas[i], PathInfo.EMPTY_INFO));
		}
	}

	private IRcSettingInfo[] mapFileDiscoveredInfo(IProject project, CConfigurationData data, RcSetSettings rcSet, PathFilePathInfo[] pfpis){
//		IResource rc;
		PathInfo pInfo;
		IPath projRelPath;
		CResourceData rcData;
//		RcSetSettings dataSetting;
		List<IRcSettingInfo> list = new ArrayList<IRcSettingInfo>(pfpis.length);
		RcSettingInfo rcInfo;
		LangSettingInfo lInfo;
		CLanguageData lData;
		ArrayList<ILangSettingInfo> tmpList;
		PathFilePathInfo pfpi;

		for(int i = 0; i < pfpis.length; i++){
			pfpi = pfpis[i];
			projRelPath = pfpi.fPath;
			pInfo = pfpi.fInfo;
			if(pInfo.isEmpty())
				continue;

			if(projRelPath.segmentCount() == 0){
				CFolderData rootData = (CFolderData)rcSet.fRcData;
				CLanguageData lDatas[] = rootData.getLanguageDatas();
				IPath[] incPaths = pInfo.getIncludePaths();
				IPath[] quotedIncPaths = pInfo.getQuoteIncludePaths();
				IPath[] incFiles = pInfo.getIncludeFiles();
				IPath[] macroFiles = pInfo.getMacroFiles();
				Map<String, String> symbolMap = pInfo.getSymbols();
				int kinds = 0;

				if(incPaths.length != 0 || quotedIncPaths.length != 0)
					kinds |= ICLanguageSettingEntry.INCLUDE_PATH;
				if(incFiles.length != 0)
					kinds |= ICLanguageSettingEntry.INCLUDE_FILE;
				if(macroFiles.length != 0)
					kinds |= ICLanguageSettingEntry.MACRO_FILE;
				if(symbolMap.size() != 0)
					kinds |= ICLanguageSettingEntry.MACRO;

				rcInfo = null;
				for(int k = 0; k < lDatas.length; k++){
					lData = lDatas[k];
					if((lData.getSupportedEntryKinds() & kinds) == 0)
						continue;

					if(rcInfo == null){
						rcInfo = new RcSettingInfo(rootData);
						tmpList = new ArrayList<ILangSettingInfo>(lDatas.length - k);
						rcInfo.fLangInfoList = tmpList;
					}

					lInfo = new LangSettingInfo(lData, pInfo);
					rcInfo.add(lInfo);
				}

				if(rcInfo != null)
					list.add(rcInfo);

				continue;
			}
//			switch(rc.getType()){
//			case IResource.FILE:
//				projRelPath = rc.getProjectRelativePath();
//				dataSetting = rcSet.getChild(projRelPath, false);
//				rcData = dataSetting.fRcData;
				rcData = rcSet.getChild(projRelPath, false).fRcData;
				if(!rcData.getPath().equals(projRelPath)){
					if(rcData.getType() == ICSettingBase.SETTING_FOLDER){
						CFolderData foData = (CFolderData)rcData;
						lData = CDataUtil.findLanguagDataForFile(projRelPath.lastSegment(), project, (CFolderData)rcData);
						try {
							rcData = createFileData(data, projRelPath, foData, lData);
						} catch (CoreException e) {
							rcData = null;
							ManagedBuilderCorePlugin.log(e);
						}
					} else {
						try {
							rcData = createFileData(data, projRelPath, (CFileData)rcData);
						} catch (CoreException e) {
							rcData = null;
							ManagedBuilderCorePlugin.log(e);
						}
					}
//					if(rcData != null)
//						dataSetting = rcSet.createChild(projRelPath, rcData, false);
//					else
//						dataSetting = null;
				}

				if(rcData != null){
					if(rcData.getType() == ICSettingBase.SETTING_FILE){
						lData = ((CFileData)rcData).getLanguageData();
					} else {
						lData = CDataUtil.findLanguagDataForFile(projRelPath.lastSegment(), project, (CFolderData)rcData);

					}

					if(lData != null){
						rcInfo = new RcSettingInfo(rcData);
						lInfo = new LangSettingInfo(lData, pInfo);
						tmpList = new ArrayList<ILangSettingInfo>(1);
						tmpList.add(lInfo);
						rcInfo.fLangInfoList = tmpList;
						list.add(rcInfo);
					}

				}

//				break;
//			}
		}
		return list.toArray(new RcSettingInfo[list.size()]);
	}

	public IRcSettingInfo[] getSettingInfos(IProject project, CConfigurationData data, IDiscoveredPathManager.IPerFileDiscoveredPathInfo2 discoveredInfo, boolean fileDataMode){
		if(fileDataMode){
			RcSetSettings rcSettings = createRcSetInfo(data);
			PathFilePathInfo pInfos[] = createOrderedInfo(discoveredInfo.getPathInfoMap());
			return mapFileDiscoveredInfo(project, data, rcSettings, pInfos);
		}
		RcSetSettings settings = createRcSetSettings(data, discoveredInfo);
		return createInfos(data, settings);
	}

	private IRcSettingInfo[] createInfos(CConfigurationData data, RcSetSettings rootSetting){
		RcSetSettings settings[] = rootSetting.getChildren(true);
		RcSetSettings setting;
		CResourceData rcData;
		List<IRcSettingInfo> resultList = new ArrayList<IRcSettingInfo>();
		LangSettingInfo langInfo;
		RcSettingInfo rcInfo;
		PathInfo pathInfo;
		for(int i = 0; i < settings.length; i++){
			setting = settings[i];
			rcData = setting.fRcData;
			if(rcData == null)
				continue;
			if(setting.fIsDerived){
//				rcData = null;
				try {
					rcData = createFolderData(data, rcData, setting);
				} catch (CoreException e) {
					rcData = null;
					ManagedBuilderCorePlugin.log(e);
				}
				if(rcData != null){
					setting.updateRcData(rcData, false);
				} else {
					//TODO:
					continue;
				}
			}

			if(rcData.getType() == ICSettingBase.SETTING_FILE){
				ExtsSetSettings extSetting = setting.fExtToExtsSetMap.get(getFileExt(rcData.getPath()));
				if(extSetting != null){
					pathInfo = extSetting.fMaxMatchInfo;
					if(pathInfo != null){
						langInfo = new LangSettingInfo(extSetting.fBaseLangData, pathInfo);
						rcInfo = new RcSettingInfo(rcData);
						rcInfo.fLangInfoList = new ArrayList<ILangSettingInfo>(1);
						rcInfo.fLangInfoList.add(langInfo);
						resultList.add(rcInfo);
					}
				}
			} else {
				if(setting.fExtsSetToExtsSetSettingsMap.size() != 0 ){
					rcInfo = new RcSettingInfo(rcData);
					rcInfo.fLangInfoList = new ArrayList<ILangSettingInfo>(setting.fExtsSetToExtsSetSettingsMap.size());
					resultList.add(rcInfo);

					Collection<ExtsSetSettings> values = setting.fExtsSetToExtsSetSettingsMap.values();
					for (ExtsSetSettings extSetting : values) {
						if(extSetting.fMaxMatchInfo == null)
							continue;

						if(extSetting.fBaseLangData == null)
							continue;

						if(extSetting.fIsDerived){
							throw new IllegalStateException();
						}


						rcInfo.add(new LangSettingInfo(extSetting.fBaseLangData, extSetting.fMaxMatchInfo));

						if(extSetting.isMultiple()){
							Set<Entry<PathInfo, List<PathFilePathInfo>>> entrySet = extSetting.fPathFilePathInfoMap.entrySet();
							for (Entry<PathInfo, List<PathFilePathInfo>> entry : entrySet) {
								if(entry.getKey().equals(extSetting.fMaxMatchInfo))
									continue;
								List<PathFilePathInfo> piList = entry.getValue();
								for (PathFilePathInfo pi : piList) {
									try {
										CFileData fiData = createFileData(data, pi.fPath, (CFolderData)rcData, extSetting.fBaseLangData);
										CLanguageData fiLangData = fiData.getLanguageData();
										if(fiLangData != null){
											RcSettingInfo fiInfo = new RcSettingInfo(fiData);
											fiInfo.add(new LangSettingInfo(fiLangData, pi.fInfo));
											resultList.add(fiInfo);
										}
									} catch (CoreException e) {
										ManagedBuilderCorePlugin.log(e);
									}
								}
							}
						}
					}
				}
			}
		}
//		}

		return resultList.toArray(new RcSettingInfo[resultList.size()]);
	}

	private CFolderData createFolderData(CConfigurationData cfg, CResourceData base, RcSetSettings setting) throws CoreException{
		if(base.getType() == ICSettingBase.SETTING_FOLDER)
			return createFolderData(cfg, setting.getPath(), (CFolderData)base);

		//should not be here
		throw new IllegalStateException();
	}

	private static void checkRemoveDups(RcSetSettings rcSet){
		RcSetSettings settings[] = rcSet.getChildren(true);
		RcSetSettings setting, parent;
		for(int i = 0; i < settings.length; i++){
			setting = settings[i];
			if(!setting.fIsDerived)
				continue;

			parent = setting.getParent();
			if(parent == null)
				continue;

			if(parent.containsEqualMaxMatches(setting, true))
				removeChildAddingChildSettings(parent, setting);
		}
	}

	private static void removeChildAddingChildSettings(RcSetSettings parent, RcSetSettings child){
		parent.internalAddSettingsMap(child.fExtsSetToExtsSetSettingsMap);
		parent.removeChild(child);
	}

	private static void mapDiscoveredInfo(RcSetSettings rcSet, PathFilePathInfo[] pInfos){
		PathFilePathInfo pInfo;
		RcSetSettings child, parent;
		String ext;
		ExtsSetSettings extsSet;
//		boolean isDerived;
		IPath dirPath;
		for(int i = 0; i < pInfos.length; i++){
			pInfo = pInfos[i];
			child = rcSet.getChild(pInfo.fPath);
			if(child == null) {
				dirPath = pInfo.fPath.removeLastSegments(1);
				child = rcSet.getChild(dirPath);
				if(child == null){
					child = rcSet.createChild(dirPath, null, true);
					if(child.fExtToExtsSetMap == null){
						parent = child.getParent();
						child.fRcData = parent.fRcData;
						child.internalSetSettingsMap(createEmptyExtSetMapCopy(parent.fExtsSetToExtsSetSettingsMap));
					}
				}
			}

//			isDerived = child.fIsDerived;

			if(pInfo.fPath.segmentCount() == 0){
				processProjectPaths(child, pInfo);
			} else {
				ext = getFileExt(pInfo.fPath);
				extsSet = child.fExtToExtsSetMap.get(ext);
				if(extsSet == null){
					extsSet = new ExtsSetSettings(null, new ExtsSet(new String[]{ext}), false);
					child.internalAdd(extsSet);
//					child.fExtToExtsSetMap.put(ext, extsSet);
				}
				extsSet.add(pInfo);
			}
		}
	}

	private static void processProjectPaths(RcSetSettings rcSet, PathFilePathInfo pfpi){
		Collection<ExtsSetSettings> settings = rcSet.fExtsSetToExtsSetSettingsMap.values();
		for (ExtsSetSettings setting : settings) {
			setting.add(pfpi);
		}
	}

	private static String getFileExt(IPath path){
		String ext = path.getFileExtension();
		if(ext != null)
			return ext;
		return ""; //$NON-NLS-1$
	}

	private static HashMap<ExtsSet, ExtsSetSettings> createEmptyExtSetMapCopy(HashMap<ExtsSet, ExtsSetSettings> base){
		@SuppressWarnings("unchecked")
		HashMap<ExtsSet, ExtsSetSettings> map = (HashMap<ExtsSet, ExtsSetSettings>)base.clone();
		ExtsSetSettings extsSet;
		Set<Entry<ExtsSet, ExtsSetSettings>> entrySet = map.entrySet();
		for (Entry<ExtsSet, ExtsSetSettings> entry : entrySet) {
			extsSet = entry.getValue();
			extsSet = new ExtsSetSettings(extsSet.fBaseLangData, extsSet.fExtsSet, true);
			entry.setValue(extsSet);
		}
		return map;
	}

	private static ExtsSetSettings createExtsSetSettings(IPath path, CFileData data){
		CLanguageData lData = data.getLanguageData();
		if(lData != null){
			String ext = getFileExt(path);
			return createExtsSetSettings(lData, new String[]{ext});
		}
		return new ExtsSetSettings(null, new ExtsSet(EMPTY_STRING_ARRAY), false);
	}

	private static ExtsSetSettings createExtsSetSettings(CLanguageData lData, String exts[]){
		return new ExtsSetSettings(lData, new ExtsSet(exts), false);
	}

	private static HashMap<ExtsSet, ExtsSetSettings> createExtsSetSettingsMap(CFolderData data){
		CLanguageData[] lDatas = data.getLanguageDatas();
		HashMap<ExtsSet, ExtsSetSettings> map = new HashMap<ExtsSet, ExtsSetSettings>(lDatas.length);
		ExtsSetSettings settings;

		if(lDatas.length != 0) {
			CLanguageData lData;
			for( int i = 0; i < lDatas.length; i++){
				lData = lDatas[i];
				 settings = createExtsSetSettings(lData, lData.getSourceExtensions());
				 map.put(settings.fExtsSet, settings);
			}
		}

		return map;
	}

	private static PathFilePathInfo[] createOrderedInfo(Map<IResource, PathInfo> map){
		IResource rc;
		IPath path;
		PathInfo info, storedInfo;
		ListIndexStore store = new ListIndexStore(10);
		HashMap<PathInfo, PathInfo> infoMap = new HashMap<PathInfo, PathInfo>();
//		LinkedHashMap result;

		Set<Entry<IResource, PathInfo>> entrySet = map.entrySet();
		for (Entry<IResource, PathInfo> entry : entrySet) {
			rc = entry.getKey();
			path = rc.getProjectRelativePath();
			int segCount = path.segmentCount();
//			if(segCount < 1)
//				continue;

//			path = path.removeFirstSegments(1);
//			segCount--;

			info = entry.getValue();
			storedInfo = infoMap.get(info);
			if(storedInfo == null){
				storedInfo = info;
				infoMap.put(storedInfo, storedInfo);
			}

			store.add(segCount, new PathFilePathInfo(path, storedInfo));
		}

		List<PathFilePathInfo> lists[] = store.getLists();
//		result = new LinkedHashMap(map.size());
//		List l;
//		int lSize;
//		PathFilePathInfo pfpi;
//		for(int i = 0; i < lists.length; i++){
//			l = lists[i];
//			lSize = l.size();
//			if(lSize != 0){
//				for(int k = 0; k < lSize; k++){
//					pfpi = (PathFilePathInfo)l.get(k);
//					result.put(pfpi.fPath, pfpi.fInfo);
//				}
//			}
//		}
		int size = 0;
		PathFilePathInfo infos[];
		for(int i = 0; i < lists.length; i++){
			size += lists[i].size();
		}

		infos = new PathFilePathInfo[size];
		int num = 0;
		int listSize;
		List<PathFilePathInfo> list;
		for(int i = 0; i < lists.length; i++){
			list = lists[i];
			listSize = list.size();
			for(int k = 0; k < listSize; k++){
				infos[num++] = list.get(k);
			}
		}

		return infos;
	}
}
