package cn.com.pc.penguin.core;

import cn.com.pc.penguin.service.SearchArgs;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.*;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Created by IntelliJ IDEA.
 * User: huangqinghe
 * To change this template use File | Settings | File Templates.
 */
public class PenguinArgs {
    private String searchType;
    private Query query;
    private Query filterQuery;
    private Filter filter;
    private int offset;    //start
    private int limit = SearchArgs.DEFAULT_SEARCH_COUNT; //default 20  //number
    private Sort sort;
    private String valueField;

    /**
     * for auto completion, auto suggestion, spell correction, partial match etc.
     */
    private String track_keyword;

    public PenguinArgs(SearchArgs args) throws Exception {
        super();
        this.searchType = args.getSearchType().toString();
        this.query = args.getQueryBuilder().buildQuery(args);
        this.filterQuery = args.getQueryBuilder().buildFilter(args);
        if (this.filterQuery != null) {
            this.filter = new QueryWrapperFilter(this.filterQuery);
        }
        this.sort = args.getQueryBuilder().buildSort(args);
        this.offset = args.getOffset();
        this.limit = args.getLimit();
        this.track_keyword = args.getTrack_keyword();
        this.valueField = args.getValueField();
    }

    public PenguinArgs(JSONObject args) throws Exception {
        super();

        this.searchType = args.getString("searchType");
        this.offset = args.getInt("offset");
        this.limit = args.getInt("limit");

        try {
            this.valueField = args.getString("valueField");
        } catch (JSONException e) {
        }
        try {
            this.track_keyword = args.optString("track_keyword");
        } catch (Exception e) {
        }


        JSONObject q = args.getJSONObject("query");
        this.query = this.jsonToQuery(q);
        try {
            JSONObject fq = args.getJSONObject("filterQuery");
            if (fq != null) {
                this.filterQuery = this.jsonToQuery(fq);
                if (this.filterQuery != null) {
                    this.filter = new QueryWrapperFilter(this.filterQuery);
                }
            }
        } catch (Exception e) {
        }

        try {
            JSONArray sorts = args.getJSONArray("sort");
            if (sorts != null && sorts.length() > 0) {
                SortField[] fss = new SortField[sorts.length()];
                for (int i = 0; i < sorts.length(); i++) {
                    JSONObject sort = sorts.getJSONObject(i);
                    String field = null;
                    try {
                        field = sort.optString("field");
                    } catch (Exception e) {
                    }
                    int type = sort.getInt("type");
                    boolean reverse = sort.getBoolean("reverse");
                    fss[i] = new SortField(field, type, reverse);
                }
                this.sort = new Sort(fss);
            }
        } catch (Exception e) {
        }


    }

    private Query jsonToQuery(JSONObject q) throws Exception {
        String queryType = q.getString("queryType").trim();
        Query query = null;
        if ("booleanQuery".equals(queryType)) {
            BooleanQuery booleanQuery = new BooleanQuery();
            JSONArray clauses = q.getJSONArray("clauses");
            for (int i = 0; i < clauses.length(); i++) {
                JSONObject clause = clauses.getJSONObject(i);
                Query bq = this.jsonToQuery(clause.getJSONObject("query"));
                String oc = clause.getString("occur");
                BooleanClause.Occur occur = null;
                if ("+".equals(oc)) {
                    occur = BooleanClause.Occur.MUST;
                } else if ("".equals(oc)) {
                    occur = BooleanClause.Occur.SHOULD;
                } else if ("-".equals(oc)) {
                    occur = BooleanClause.Occur.MUST_NOT;
                }
                BooleanClause bc = new BooleanClause(bq, occur);
                booleanQuery.add(bc);
            }
            booleanQuery.setBoost(q.optInt("boost"));
            query = booleanQuery;
        } else if ("termQuery".equals(queryType)) {
            Term term = new Term(q.getString("field"), q.getString("text"));
            TermQuery termQuery = new TermQuery(term);
            termQuery.setBoost(q.optInt("boost"));
            query = termQuery;
        } else if ("prefixQuery".equals(queryType)) {
            Term term = new Term(q.getString("field"), q.getString("text"));
            PrefixQuery prefixQuery = new PrefixQuery(term);
            prefixQuery.setBoost(q.optInt("boost"));
            query = prefixQuery;
        } else if ("phraseQuery".equals(queryType)) {
            PhraseQuery pq = new PhraseQuery();
            String field = q.getString("field");
            JSONArray terms = q.getJSONArray("terms");
            for (int i = 0; i < terms.length(); i++) {
                Term t = new Term(field, terms.getString(i));
                pq.add(t);
            }
            pq.setBoost(q.optInt("boost"));
            query = pq;
        } else if ("termRangeQuery".equals(queryType)) {
            String field = q.getString("field");
            String lowerTerm = null;
            String upperTerm = null;

            try {
                lowerTerm = q.getString("lowerTerm");
            } catch (Exception e) {
            }
            try {
                upperTerm = q.getString("upperTerm");
            } catch (Exception e) {
            }

            boolean includeLower = q.getBoolean("iL");
            boolean includeUpper = q.getBoolean("iU");
            TermRangeQuery trq = new TermRangeQuery(field, lowerTerm, upperTerm, includeLower, includeUpper);

            trq.setBoost(q.optInt("boost"));
            query = trq;
        } else if ("numericRangeQuery".equals(queryType)) {
            query = this.createNumeicRangeQuery(q);
        } else if ("MatchAllDocsQuery".equals(queryType)) {
            query = new MatchAllDocsQuery();
        } else {
            throw new Exception(queryType + "has not supported yet!");
        }
        return query;
    }

    private Query createNumeicRangeQuery(JSONObject q) throws Exception {
        NumericRangeQuery nrq = null;
        String field = q.getString("field");
        boolean minInclusive = q.getBoolean("iL");
        boolean maxInclusive = q.getBoolean("iU");
        String valueType = q.getString("valueType");
        if ("Long".equals(valueType)) {
            Long min = null;
            Long max = null;
            try {
                min = q.getLong("min");
            } catch (Exception e) {
            }
            try {
                max = q.getLong("max");
            } catch (Exception e) {
            }
            nrq = NumericRangeQuery.newLongRange(field, min, max, minInclusive, maxInclusive);
        } else if ("Integer".equals(valueType)) {
            Integer min = null;
            Integer max = null;
            try {
                min = q.getInt("min");
            } catch (Exception e) {
            }
            try {
                max = q.getInt("max");
            } catch (Exception e) {
            }
            nrq = NumericRangeQuery.newIntRange(field, min, max, minInclusive, maxInclusive);
        } else if ("Double".equals(valueType)) {
            Double min = null;
            Double max = null;
            try {
                min = q.getDouble("min");
            } catch (Exception e) {
            }
            try {
                max = q.getDouble("max");
            } catch (Exception e) {
            }
            nrq = NumericRangeQuery.newDoubleRange(field, min, max, minInclusive, maxInclusive);
        } else if ("Float".equals(valueType)) {
            Float min = null;
            Float max = null;
            try {
                min = Float.valueOf(q.getString("min"));
            } catch (Exception e) {
            }
            try {
                max = Float.valueOf(q.getString("max"));
            } catch (Exception e) {
            }
            nrq = NumericRangeQuery.newFloatRange(field, min, max, minInclusive, maxInclusive);
        }
        nrq.setBoost(q.optInt("boost"));
        return nrq;
    }

    public String getSearchType() {
        return searchType;
    }

    public void setSearchType(String searchType) {
        this.searchType = searchType;
    }

    public Query getQuery() {
        return query;
    }

    public void setQuery(Query query) {
        this.query = query;
    }

    public Query getFilterQuery() {
        return filterQuery;
    }

    public void setFilterQuery(Query filterQuery) {
        this.filterQuery = filterQuery;
    }

    public Filter getFilter() {
        return filter;
    }

    public void setFilter(Filter filter) {
        this.filter = filter;
    }

    public int getOffset() {
        return offset;
    }

    public void setOffset(int offset) {
        this.offset = offset;
    }

    public int getLimit() {
        return limit;
    }

    public void setLimit(int limit) {
        this.limit = limit;
    }

    public Sort getSort() {
        return sort;
    }

    public void setSort(Sort sort) {
        this.sort = sort;
    }

    public String getTrack_keyword() {
        return track_keyword;
    }

    public void setTrack_keyword(String track_keyword) {
        this.track_keyword = track_keyword;
    }

    public String getValueField() {
        return valueField;
    }

    public void setValueField(String valueField) {
        this.valueField = valueField;
    }
}
