/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.rya.indexing.pcj.fluo.api;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.apache.fluo.api.client.FluoClient;
import org.apache.fluo.api.client.Snapshot;
import org.apache.rya.api.client.CreatePCJ.ExportStrategy;
import org.apache.rya.api.client.CreatePCJ.QueryType;
import org.apache.rya.indexing.pcj.fluo.app.query.FluoQueryMetadataDAO;
import org.apache.rya.indexing.pcj.fluo.app.query.QueryMetadata;
import org.eclipse.rdf4j.query.parser.ParsedQuery;
import org.eclipse.rdf4j.query.parser.sparql.SPARQLParser;
import org.eclipse.rdf4j.queryrender.sparql.SPARQLQueryRenderer;

import com.google.common.base.Preconditions;

/**
 * Class for retrieving a List containing a String representation of each query maintained by Fluo.
 *
 */
public class ListFluoQueries {

    private static final FluoQueryMetadataDAO dao = new FluoQueryMetadataDAO();

    /**
     * Retrieve a list of String representations of each query maintained by Fluo
     * 
     * @param fluo - FluoClient for interacting with Fluo
     * @return - List of String representations of queries maintained by Fluo.
     * @throws Exception 
     */
    public List<String> listFluoQueries(FluoClient fluo) throws Exception {

        List<String> queryStrings = new ArrayList<>();
        Snapshot sx = fluo.newSnapshot();

        List<String> ids = new ListQueryIds().listQueryIds(fluo);
        for (String id : ids) {
            queryStrings.add(extractString(dao.readQueryMetadata(sx, id)));
        }

        return queryStrings;
    }

    private static String extractString(QueryMetadata metadata) throws Exception {
        FluoQueryStringBuilder builder = new FluoQueryStringBuilder();
        return builder.setQueryId(metadata.getNodeId()).setQueryType(metadata.getQueryType())
                .setExportStrategies(metadata.getExportStrategies()).setQuery(metadata.getSparql()).build();
    }
    
    private static String getPrettyPrintSparql(String sparql, int indent) throws Exception {
        SPARQLParser parser = new SPARQLParser();
        ParsedQuery pq = parser.parseQuery(sparql, null);
        SPARQLQueryRenderer render = new SPARQLQueryRenderer();
        String renderedQuery = render.render(pq);
        
        //remove extra quotes generated by query renderer
        String[] splitRender = renderedQuery.split("\"\"\"");
        StringBuilder builder = new StringBuilder();
        for(String s: splitRender) {
            builder.append(s).append("\"");
        }
        builder.replace(builder.length() - 1, builder.length(), "");
        
        //add indent to all lines following newline char
        String[] newLineRender = builder.toString().split("\n");
        builder = new StringBuilder();
        String prefix = getVariableIndent(indent);
        for(int i = 0; i < newLineRender.length; i++) {
            if(i != 0) {
                builder.append(prefix);
            }
            builder.append(newLineRender[i]).append("\n");
        }
        
        return builder.toString();
    }

    private static String getVariableIndent(int len) {
        return new String(new char[len]).replace('\0', ' ');
    }

    public static class FluoQueryStringBuilder {

        private String queryId;
        private String sparql;
        private QueryType queryType;
        private Set<ExportStrategy> strategies;

        public FluoQueryStringBuilder setQueryId(String queryId) {
            this.queryId = Preconditions.checkNotNull(queryId);
            return this;
        }

        public FluoQueryStringBuilder setQuery(String query) {
            this.sparql = Preconditions.checkNotNull(query);
            return this;
        }

        public FluoQueryStringBuilder setExportStrategies(Set<ExportStrategy> strategies) {
            this.strategies = Preconditions.checkNotNull(strategies);
            return this;
        }

        public FluoQueryStringBuilder setQueryType(QueryType queryType) {
            this.queryType = Preconditions.checkNotNull(queryType);
            return this;
        }

        public String build() throws Exception {

            int valueAlign = 20;
            String sparqlHeader = "SPARQL: ";
            String idHeader = "QUERY ID: ";
            String typeHeader = "QUERY TYPE: ";
            String strategiesHeader = "EXPORT STRATEGIES: ";
            
            StringBuilder builder = new StringBuilder();
            builder.append(idHeader).append(getVariableIndent(valueAlign - idHeader.length())).append(queryId).append("\n")
                   .append(typeHeader).append(getVariableIndent(valueAlign - typeHeader.length())).append(queryType).append("\n")
                   .append(strategiesHeader).append(getVariableIndent(valueAlign - strategiesHeader.length())).append(strategies).append("\n")
                   .append(sparqlHeader).append(getVariableIndent(valueAlign - sparqlHeader.length())).append(getPrettyPrintSparql(sparql, valueAlign)).append("\n");

            return builder.toString();
        }

    }

}
