/* $Id: DocumentSearcherDTW.java 125 2011-03-09 09:49:51Z ohura $ */
package jp.ac.hokudai.meme.core_smart_gs.searcher;

import java.util.*;

public class DocumentSearcherDTW extends DocumentSearcher {

    private static final double WARPING_LIMIT = 1.2;

    private AbstractDTWCalculator calculator_ = null;
    
    public DocumentSearcherDTW(int type) {
        super();
        switch (type) {
        case IDocumentSearcher.TYPE_DTW_1:
            calculator_ = new DTWCalculatorType1();
            break;
        case IDocumentSearcher.TYPE_DTW_2:
            calculator_ = new DTWCalculatorType2();
            break;
        default:
            calculator_ = new DTWCalculatorType1();
            break;
        }
    }

    /*
     * (”ñ Javadoc)
     * 
     * @see jp.ac.hokudai.meme.core_smart_gs.searcher.DocumentSearcher#searchStart(double[][],
     *      int[])
     */
    protected ResultIndex searchStart(double[][] dscData, int[] index) {
        int fullTextLength = dscData.length;
        int patLength = index.length;

        double[][] pattern = new double[patLength][];
        for (int i = 0; i < patLength; i++) {
            pattern[i] = dscData[index[i]];
        }
        int uLimitL = (int) Math.ceil(patLength * WARPING_LIMIT);
        int lLimitL = (int) Math.ceil(patLength / WARPING_LIMIT);
        double[][] dtwArray = new double[patLength][uLimitL];
        ArrayList dtwResList = new ArrayList(fullTextLength - patLength);
        calculator_.makeDistTable(dscData, pattern);
        for (int i = 0; i < fullTextLength - uLimitL + 1; i++) {
            if (isContain(index, i, patLength)) {
                continue;
            }
//            for (int j = 0; j < subTextLength; j++) {
//                subText[j] = dscData[i + j];
//            }
            DistanceAndIndex res = calculator_.calcDTWDist(i, i + uLimitL -1, pattern, dtwArray);
            if (res.getLength() >= lLimitL) {
                dtwResList.add(res);
            }

        }

        DistanceAndIndex[] resList = (DistanceAndIndex[]) dtwResList
                .toArray(new DistanceAndIndex[0]);
        Comparator comparator = new Comparator() {
            public int compare(Object arg0, Object arg1) {
                double diff = ((DistanceAndIndex) arg0).getDistance()
                        - ((DistanceAndIndex) arg1).getDistance();
                int res = 0;
                if (diff < 0) {
                    res = -1;
                } else if (diff > 0) {
                    res = 1;
                }
                return res;
            }

        };
        Arrays.sort(resList, comparator);
        return makeResultIndexes(resList);

    }

//    protected DistanceAndIndex calcDTWDist(double[][] text, double[][] pattern, int startIndex, double[][] dtwArray) {
//        return calculator_.calcDTWDist(text, pattern, startIndex, dtwArray);
//    }

    private boolean isContain(int[] index, int i, int pLength) {
    	int s1 = index[0];
    	int e1 = index[index.length - 1];
    	int s2 = i;
    	int e2 = i + pLength - 1;
    	boolean flag = true;
    	return chkContain(s1, e1, s2, e2);
//        int length = index.length;
//        
//        for (int j = 0; j < length; j++) {
//            if (index[j] == i) {
//                return true;
//            }
//        }
//        return false;
    }
    
    private boolean chkContain(int start1, int end1, int start2, int end2) {
    	boolean flag = true;
    	if(start1 < start2 && end1 < start2){
    		flag = false;
    	}else if (start2 < start1 && end2 < start1) {
			flag = false;
		}
    	
    	return flag;
	}

    private ResultIndex makeResultIndexes(DistanceAndIndex[] resList) {
        ResultIndex resultIndex = new ResultIndex();

        for (int i = 0; i < resList.length; i++) {
            DistanceAndIndex candidate = resList[i];
            chkAndAddCandidate(candidate, resultIndex);
            if (resultIndex.size() == maxFound_) {
                break;
            }
        }

        return resultIndex;
    }

    private void chkAndAddCandidate(DistanceAndIndex candidate,
            ResultIndex resultIndex) {
        // TODO check code
        
        boolean addFlag = true;
        for (Iterator iter = resultIndex.iterator(); iter.hasNext();) {
			FoundIndex found = (FoundIndex) iter.next();
			int[] indexes = found.indexList_;
			if (chkContain(indexes[0], indexes[indexes.length - 1], candidate.getStartIndex(), candidate.getEndIndex())) {
				addFlag = false;
            }
		}
        
		if (addFlag) {
			FoundIndex foundIndex = new FoundIndex(candidate.getStartIndex(),
					candidate.getEndIndex(), candidate.getDistance());
			resultIndex.addResult(foundIndex);
		}        
    }

}

class DistanceAndIndex {
    private final double distance_;

    private final int startIndex_;

    private final int endIndex_;

    public DistanceAndIndex(double dist, int start, int end) {
        distance_ = dist;
        startIndex_ = start;
        endIndex_ = end;
    }

    public int getLength() {
        return endIndex_ - startIndex_ + 1;
    }

    public double getDistance() {
        return distance_;
    }

    public int getEndIndex() {
        return endIndex_;
    }

    public int getStartIndex() {
        return startIndex_;
    }

}
