/*
* Copyright 2014 - LAit SpA
*				 Realizzato da Icona Management srl su commessa LAit SpA
* Copyright 2015- LAit SpA
*                                                  Aggiornato da LAit SpA
*
* Concesso in licenza a norma dell'EUPL, versione 1.1 o
* successive dell'EUPL (la "Licenza")– non appena saranno
* approvate dalla Commissione europea;
* Non è possibile utilizzare l'opera salvo nel rispetto della Licenza.
* È possibile ottenere una copia della Licenza al seguente indirizzo:
*
* http://ec.europa.eu/idabc/eupl
*
* Salvo diversamente indicato dalla legge applicabile o
* concordato per iscritto, il software distribuito secondo
* i termini della Licenza è distribuito "TAL QUALE",
* SENZA GARANZIE O CONDIZIONI DI ALCUN TIPO,
* esplicite o implicite.
* Si veda la Licenza per la lingua specifica che disciplina
* le autorizzazioni e le limitazioni secondo i termini della
* Licenza.
*/
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package it.laitspa.cpf.model;

/**
 *
 * @author Andrea
 */
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;

public abstract class SortableQueryPaging
{
  private int       firstResult;
  private int       maxResults;

  protected Session session;
  
  protected Map<String, SortableProperty> sortableProperties;


  public SortableQueryPaging(Session session)
  {
    firstResult = 0;
    maxResults = 10;
    this.session = session;
    this.sortableProperties = null;    
  }

  public ScrollableResults scroll()
  {
    // Check query arguments.
    //
    if(!checkQueryArguments())
      throw new IllegalArgumentException();

    // Initialize hibernate session and query objects.
    //
    // Execute the query.
    //
    String queryHql = getQueryHql();
    org.hibernate.Query q = session.createQuery(queryHql);
    setQueryLimits(q);
    setQueryArguments(q);
    return q.scroll(ScrollMode.FORWARD_ONLY);
  }

  @SuppressWarnings("unchecked")
  public QueryResult query()
  {
    // Check query arguments.
    //
    if(!checkQueryArguments())
      throw new IllegalArgumentException();

    // Initialize hibernate session and query objects.
    //
    QueryResult result = new QueryResult();
    org.hibernate.Query q;

    // Count the query results.
    //
    String listHql = buildListIdQuery();
    q = session.createQuery(listHql);
    setQueryArguments(q);
    List<Object> listId = q.list();

    int count = listId.size();

    result.setCount(count);

    // If no items will be returned by the query,
    // we spare the effort of querying.
    //
    if(count <= 0)
      return result;


    // Retrieve Object by storedId.
    //
    if (firstResult < 0 || firstResult >= listId.size())
      return result;

    int maxRes = firstResult + maxResults;
    
    List<Object> list = new ArrayList<Object>();
    int index = firstResult;
    while (index < listId.size() && index < maxRes)
    {
      Object obj = listId.get(index);
      Object id = null;
      
      if (obj instanceof Object[])
      {
        Object[] ar = (Object[]) obj;
        id = ar[0];
      }
      else
        id = obj;
      String queryHql = getQueryHql();
      q = session.createQuery(queryHql);
      setQueryId(q, id);
      
      Object res = q.uniqueResult();
      list.add(res);
      index++;
    }
    
    result.setItems(list);

    // Return the results.
    //
    return result;
  }

  private void setQueryLimits(org.hibernate.Query q)
  {
    q.setFirstResult(firstResult);

    if(maxResults >= 0)
      q.setMaxResults(maxResults);
  }

  protected abstract boolean checkQueryArguments();

  protected abstract String getListHql();

  protected abstract String getQueryHql();

  protected abstract void setQueryArguments(org.hibernate.Query q);

  protected void setQueryId(org.hibernate.Query q, Object id)
  {
    q.setParameter("id", id);
  }
  
  public int getFirstResult()
  {
    return firstResult;
  }

  public void setFirstResult(int firstResult)
  {
    this.firstResult = firstResult;
  }

  public int getMaxResults()
  {
    return maxResults;
  }

  public void setMaxResults(int maxResults)
  {
    this.maxResults = maxResults;
  }
  
 protected abstract Map<String, SortableProperty> getSortableProperties();
  
  @SuppressWarnings({ "rawtypes", "unchecked" })
  public void setSortableProperties(Map orderCriteria)
  {
    // Force casting
    //
    Map<String, Boolean> properties = (Map<String, Boolean>) orderCriteria;
    Map<String, SortableProperty> availables = getSortableProperties();
    
    this.sortableProperties = new HashMap<String, SortableProperty>();
    
    if (availables == null 
        || availables.size() == 0
        || properties == null
        || properties.size() == 0)
      return;
    
    // Reset sort properties
    //
    unsetSortableProperties(); 
    
    for (String key : properties.keySet())
    {
      SortableProperty prop = availables.get(key);
      if (prop != null)
      {
        prop.setAscending(properties.get(key));
        this.sortableProperties.put(key, prop);
      }
    }
  }
  
  public String getOrderBy()
  {
    if (sortableProperties == null || sortableProperties.size() == 0)
      return "order by " + getListIdentifier();
    
    // Build order by string.
    //
    String orderby = "";
    for (SortableProperty p : sortableProperties.values())
    {
      if (p.getAscending() == null)
        continue;
      
      if (orderby.trim().length() > 0)
        orderby += ", ";
      
      orderby += p.getName();
      
      if (!p.getAscending())
        orderby += " desc";
    }
    return orderby.trim().length() > 0 ? " order by " + orderby : "";
  }
  
  
  public String getReadableFields()
  {
    if (sortableProperties == null || sortableProperties.size() == 0)
      return "";
    
    String orderby = "";
    for (SortableProperty p : sortableProperties.values())
    {
      orderby += ", " + p.getName();
    }
    
    return orderby.trim().length() > 0 ? orderby : "";
  }
  
  
  private void unsetSortableProperties()
  {
    if (sortableProperties == null)
      return;
    
    for (SortableProperty p : sortableProperties.values())
      p.setAscending(null);
  }
  
  protected abstract String getListIdentifier();
  
  private String buildListIdQuery()
  {
    String identifier = getListIdentifier();
    String bodyQuery = getListHql();
    
    String query = "select distinct " + identifier + " ";
    query += getReadableFields() + " ";
    query += bodyQuery;
    query += getOrderBy();
    
    return query;
  }

}

