/*
 * Autopsy Forensic Browser
 *
 * Copyright 2011 Basis Technology Corp.
 * Contact: carrier <at> sleuthkit <dot> org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.sleuthkit.autopsy.keywordsearch;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.windows.TopComponent;
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
import org.sleuthkit.autopsy.datamodel.KeyValue;
import org.sleuthkit.autopsy.keywordsearch.KeywordSearch.QueryType;

/**
 * Query manager responsible for running appropriate queries and displaying results
 * for single, multi keyword queries, with detailed or collapsed results
 */
public class KeywordSearchQueryManager {

    public enum Presentation {

        COLLAPSE, DETAIL
    };
    private List<Keyword> queries;
    private Presentation presentation;
    private List<KeywordSearchQuery> queryDelegates;
    private QueryType queryType;
    private static int resultWindowCount = 0; //keep track of unique window ids to display
    private static Logger logger = Logger.getLogger(KeywordSearchQueryManager.class.getName());

    public KeywordSearchQueryManager(List<Keyword> queries, Presentation presentation) {
        this.queries = queries;
        this.presentation = presentation;
        queryType = QueryType.REGEX;
        init();
    }

    public KeywordSearchQueryManager(String query, QueryType qt, Presentation presentation) {
        queries = new ArrayList<Keyword>();
        queries.add(new Keyword(query, qt==QueryType.REGEX?false:true));
        this.presentation = presentation;
        queryType = qt;
        init();
    }

    public KeywordSearchQueryManager(String query, boolean isLiteral, Presentation presentation) {
        queries = new ArrayList<Keyword>();
        queries.add(new Keyword(query, isLiteral));
        this.presentation = presentation;
        queryType = isLiteral?QueryType.WORD:QueryType.REGEX;
        init();
    }

    private void init() {
        queryDelegates = new ArrayList<KeywordSearchQuery>();
        for (Keyword query : queries) {
            KeywordSearchQuery del = null;
            switch (queryType) {
                case WORD:
                    del = new LuceneQuery(query);
                    break;
                case REGEX:
                    if (query.isLiteral()) {
                        del = new LuceneQuery(query);
                    } else {
                        del = new TermComponentQuery(query);
                    }
                    break;
                default:
                    ;
            }
            if (query.isLiteral()) {
                del.escape();
            }
            queryDelegates.add(del);

        }
        //escape();

    }

    public void execute() {
        //execute and present the query
        //delegate query to query objects and presentation child factories
        //if (queryType == QueryType.WORD || presentation == Presentation.DETAIL) {
        //   for (KeywordSearchQuery q : queryDelegates) {
        //       q.execute();
        //  }
        // } else {
        //Collapsed view
        Collection<KeyValueQuery> things = new ArrayList<KeyValueQuery>();
        int queryID = 0;
        for (KeywordSearchQuery q : queryDelegates) {
            Map<String, Object> kvs = new LinkedHashMap<String, Object>();
            final String queryStr = q.getQueryString();
            things.add(new KeyValueQuery(queryStr, kvs, ++queryID, q));
        }

        Node rootNode = null;

        DataResultTopComponent searchResultWin = DataResultTopComponent.createInstance("Keyword search " + (++resultWindowCount));
        if (things.size() > 0) {
            Children childThingNodes =
                    Children.create(new KeywordSearchResultFactory(queries, things, Presentation.COLLAPSE, searchResultWin), true);

            rootNode = new AbstractNode(childThingNodes);
        } else {
            rootNode = Node.EMPTY;
        }

        final String pathText = "Keyword search";
        
        DataResultTopComponent.initInstance(pathText, rootNode, things.size(), searchResultWin);
        
        searchResultWin.requestActive();
        // }
    }

    public boolean validate() {
        boolean allValid = true;
        for (KeywordSearchQuery tcq : queryDelegates) {
            if (!tcq.validate()) {
                logger.log(Level.WARNING, "Query has invalid syntax: " + tcq.getQueryString());
                allValid = false;
                break;
            }
        }
        return allValid;
    }
}

/**
 * custom KeyValue that also stores query object  to execute
 */
class KeyValueQuery extends KeyValue {

    private KeywordSearchQuery query;

    KeywordSearchQuery getQuery() {
        return query;
    }

    public KeyValueQuery(String name, Map<String, Object> map, int id, KeywordSearchQuery query) {
        super(name, map, id);
        this.query = query;
    }
}

@@ -1345,24 +1345,27 @@
 splaying
+%0A *
  results
 %0A * for 
@@ -1356,19 +1356,16 @@
  results
-%0A *
  for sin
@@ -2287,18 +2287,20 @@
 uery, qt
+ 
 ==
+ 
 QueryTyp
@@ -2310,15 +2310,19 @@
 EGEX
-?
+ ? 
 false
-:
+ : 
 true
@@ -2680,17 +2680,19 @@
 sLiteral
-?
+ ? 
 QueryTyp
@@ -2697,17 +2697,19 @@
 ype.WORD
-:
+ : 
 QueryTyp
@@ -4007,16 +4007,73 @@
 ID = 0;%0A
+        StringBuilder queryConcat = new StringBuilder();%0A
         
@@ -4245,24 +4245,78 @@
 ryString();%0A
+            queryConcat.append(queryStr).append(%22 %22);%0A
             
@@ -4417,16 +4417,357 @@
  null;%0A%0A
+        String queryConcatStr = queryConcat.toString();%0A        final int queryConcatStrLen = queryConcatStr.length();%0A        final String queryStrShort = queryConcatStrLen %3E 15 ? queryConcatStr.substring(0, 14) + %22...%22 : queryConcatStr;%0A        final String windowTitle = %22Keyword search %22 + (++resultWindowCount) + %22 - %22 + queryStrShort;%0A
         
@@ -4849,49 +4849,19 @@
 nce(
-%22Keyword search %22 + (++resultWindowCount)
+windowTitle
 );%0A 
@@ -5235,25 +5235,17 @@
 earch%22;%0A
-        
 %0A
+
         
@@ -5337,17 +5337,9 @@
 n);%0A
-        
 %0A
+
     
@@ -5766,16 +5766,16 @@
 %0A%7D%0A%0A/**%0A
+
  * custo
@@ -5815,17 +5815,16 @@
  object 
- 
 to execu

add query substr to keyword search tab title fixes #225