package annis.sqlgen;

import static annis.sqlgen.TableAccessStrategy.NODE_TABLE;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;


import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.lang.StringUtils;
import org.springframework.dao.DataAccessException;

import annis.model.QueryNode;
import annis.ql.parser.QueryData;
import org.apache.commons.lang.Validate;

public class AnnotateInnerQuerySqlGenerator extends AbstractUnionSqlGenerator<Object> 
  implements SelectClauseSqlGenerator<QueryData>,
  OrderByClauseSqlGenerator<QueryData>
{

  // sort solutions
  private boolean sortSolutions;
  // annotation graph key generation
  private SolutionKey<?> solutionKey;

  @Override
  public Object extractData(ResultSet rs) throws SQLException,
    DataAccessException
  {
    throw new NotImplementedException(
      "BUG: inner query result is evaluated by outer query");
  }

  @Override
  public String toSql(QueryData queryData, int indentBy)
  {
    StringBuilder sb = new StringBuilder();
    
    String indent = computeIndent(indentBy);
    
    indent(sb, indent);
    sb.append("SELECT row_number() OVER () as n, inn.*");
    indent(sb, indent);
    sb.append("FROM (\n");
    
    sb.append(super.toSql(queryData, indentBy+1));
    sb.append("\n");
    indent(sb, indent);
    sb.append(") AS inn\n");
    
    return sb.toString();
  }
  
  

  @Override
  public String selectClause(QueryData queryData, List<QueryNode> alternative,
    String indent)
  {
    AnnotateQueryData annotateQueryData = getAnnotateQueryData(queryData);

    List<String> selectClauseForNode = new ArrayList<String>();
    for (int i = 1; i <= alternative.size(); ++i)
    {
      QueryNode node = alternative.get(i - 1);
      TableAccessStrategy tables = tables(node);

      List<String> fields = new ArrayList<String>();
      fields.addAll(solutionKey.generateInnerQueryColumns(tables, i));
      fields.add(tables.aliasedColumn(NODE_TABLE, "text_ref") + " AS text" + i);
      fields.add(tables.aliasedColumn(NODE_TABLE, "left_token") + " - "
        + annotateQueryData.getLeft() + " AS min" + i);
      fields.add(tables.aliasedColumn(NODE_TABLE, "right_token") + " + "
        + annotateQueryData.getRight() + " AS max" + i);

      fields.add(tables.aliasedColumn(NODE_TABLE, "corpus_ref") + " AS corpus" + i);
      fields.add(tables.aliasedColumn(NODE_TABLE, "name") + " AS name" + i);
      
      selectClauseForNode.add("\n" + indent + TABSTOP + StringUtils.join(fields, ", "));
    }

    return "DISTINCT" + StringUtils.join(selectClauseForNode, ", ");
  }

  @Override
  protected void appendOrderByClause(StringBuffer sb, QueryData queryData,
    List<QueryNode> alternative, String indent)
  {
    // only use ORDER BY clause if result has to be sorted
    if (!sortSolutions)
    {
      return;
    }
    // don't use ORDER BY clause if there's no LIMIT clause; saves a sort
    List<LimitOffsetQueryData> extensions =
      queryData.getExtensions(LimitOffsetQueryData.class);
    Validate.isTrue(extensions.size() > 0);
    
    if (extensions.get(0).isPaged())
    {
      super.appendOrderByClause(sb, queryData, alternative, indent);
    }
  }

  @Override
  public String orderByClause(QueryData queryData, List<QueryNode> alternative,
    String indent)
  {
    List<String> ids = new ArrayList<String>();
    for (int i = 1; i <= queryData.getMaxWidth(); ++i)
    {
      ids.add("id" + i);
    }
    return StringUtils.join(ids, ", ");
  }

  private AnnotateQueryData getAnnotateQueryData(QueryData queryData)
  {
    // find required information, assume defaults if necessary
    AnnotateQueryData annotateQueryData = null;
    for (Object o : queryData.getExtensions())
    {
      if (o instanceof AnnotateQueryData)
      {
        annotateQueryData = (AnnotateQueryData) o;
      }
    }
    if (annotateQueryData == null)
    {
      annotateQueryData = new AnnotateQueryData(5, 5);
    }
    return annotateQueryData;
  }

  public boolean isSortSolutions()
  {
    return sortSolutions;
  }

  public void setSortSolutions(boolean sortSolutions)
  {
    this.sortSolutions = sortSolutions;
  }

  public SolutionKey<?> getSolutionKey()
  {
    return solutionKey;
  }

  public void setSolutionKey(SolutionKey<?> solutionKey)
  {
    this.solutionKey = solutionKey;
  }
}
@@ -530,17 +530,16 @@
 %3CObject%3E
- 
 %0A  imple
@@ -1078,20 +1078,16 @@
 lder();%0A
-    
 %0A    Str
@@ -1124,20 +1124,16 @@
 entBy);%0A
-    
 %0A    ind
@@ -1254,28 +1254,24 @@
 FROM (%5Cn%22);%0A
-    
 %0A    sb.appe
@@ -1304,17 +1304,19 @@
 indentBy
-+
+ + 
 1));%0A   
@@ -1386,20 +1386,16 @@
 nn%5Cn%22);%0A
-    
 %0A    ret
@@ -1420,14 +1420,8 @@
 %0A  %7D
-%0A  %0A  
 %0A%0A  
@@ -1532,24 +1532,29 @@
 nt)%0A  %7B%0A    
+List%3C
 AnnotateQuer
@@ -1562,62 +1562,192 @@
 Data
- annotateQueryData = getAnnotateQueryData(queryData);%0A
+%3E extensions =%0A      queryData.getExtensions(AnnotateQueryData.class);%0A    Validate.isTrue(extensions.size() %3E 0);%0A    AnnotateQueryData annotateQueryData = extensions.get(0);    %0A    
 %0A   
@@ -2502,16 +2502,24 @@
  corpus%22
+%0A       
  + i);%0A 
@@ -2594,22 +2594,16 @@
 %22 + i);%0A
-      
 %0A      s
@@ -2675,16 +2675,24 @@
 (fields,
+%0A       
  %22, %22));
@@ -3242,20 +3242,16 @@
 ) %3E 0);%0A
-    
 %0A    if 
@@ -3668,498 +3668,10 @@
 %0A  %7D
-%0A%0A  private AnnotateQueryData getAnnotateQueryData(QueryData queryData)%0A  %7B%0A    // find required information, assume defaults if necessary%0A    AnnotateQueryData annotateQueryData = null;%0A    for (Object o : queryData.getExtensions())%0A    %7B%0A      if (o instanceof AnnotateQueryData)%0A      %7B%0A        annotateQueryData = (AnnotateQueryData) o;%0A      %7D%0A    %7D%0A    if (annotateQueryData == null)%0A    %7B%0A      annotateQueryData = new AnnotateQueryData(5, 5);%0A    %7D%0A    return annotateQueryData;%0A  %7D
+  
 %0A%0A  

Remove the method getAnnotateQueryDate because the Object QueryData has already an generic method to do that.