/* See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Esri Inc. 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 com.esri.gpt.catalog.search;

import java.util.logging.Logger;

import com.esri.gpt.framework.geometry.Envelope;
import com.esri.gpt.framework.request.PageCursor;
import com.esri.gpt.framework.request.QueryResult;
import com.esri.gpt.framework.util.Val;

/**
 * The Class SearchResult. Represents a a result from a search.
 * methods also summarise the records contained within.
 */
public class SearchResult extends QueryResult<SearchResultRecords> {

// class variables =============================================================
/** Class logger. */
private static Logger LOG = 
  Logger.getLogger(SearchResult.class.getCanonicalName());

// instance variables ==========================================================
/** The current envelope in view. */
private Envelope currentEnvelopeInView;

/** The current metadata in view. */
private String currentMetadataXmlInView;

/** The search time in seconds. */
private double searchTimeInSeconds = Integer.MIN_VALUE;

/** The supports content type query. */
private boolean supportsContentTypeQuery;

/** The supports spatial display. */
private boolean supportsSpatialDisplay;

//properties ===================================================================

/**
 * Gets the current metadata xml requested for  view.
 * 
 * @return the current metadata xml in view (trimmed, never null)
 */
public String getCurrentMetadataXmlInView() {
  return Val.chkStr(currentMetadataXmlInView);
}

/**
 * Sets the current metadata xml requested view.
 * 
 * @param currentMetadataInView the new current metadata xml in view
 */
public void setCurrentMetadataXmlInView(String currentMetadataInView) {
  this.currentMetadataXmlInView = currentMetadataInView;
}


/**
 * Gets the current envelope in view.
 * 
 * @return the current envelope in view (never null)
 */
public Envelope getCurrentEnvelopeInView() {
  if(currentEnvelopeInView == null) {
    this.setCurrentEnvelopeInView(this.getEnclosingEnvelope());
  }
  return currentEnvelopeInView;
}

/**
 * Sets the current envelope in view.
 * 
 * @param currentEnvelopeInView the new current envelope in view
 */
public void setCurrentEnvelopeInView(Envelope currentEnvelopeInView) {
  this.currentEnvelopeInView = currentEnvelopeInView;
}


/**
 * Gets the enclosing envelope.
 * 
 * @return the enclosing envelope (never null)
 */
public Envelope getEnclosingEnvelope() {
  
  //TODO: Check with urban about merging envelopes
  Envelope encEnv = null;
  Envelope recordEnvelope = null;
  
  for(SearchResultRecord record : this.getRecords()) {
    recordEnvelope = record.getEnvelope();
    if(encEnv == null) {
      encEnv = new Envelope();
      encEnv.setMaxX(recordEnvelope.getMaxX());
      encEnv.setMaxY(recordEnvelope.getMaxY());
      encEnv.setMinX(recordEnvelope.getMinX());
      encEnv.setMinY(recordEnvelope.getMinY());
      continue;
    }
   encEnv.merge(recordEnvelope);
   
  }
  
  if(encEnv == null) {
    encEnv = SearchConfig.getConfiguredInstance().getDefaultEnvelope();
    
  }

  return encEnv;
  
}

/**
 * Gets the search time in seconds.
 * 
 * @return the search time in seconds
 */
public double getSearchTimeInSeconds() {
  return Math.round(searchTimeInSeconds * 10000)/10000.0;
}

/**
 * Sets the search time in seconds.
 * 
 * @param searchTimeInSeconds the new search time in seconds
 */
public void setSearchTimeInSeconds(double searchTimeInSeconds) {
  this.searchTimeInSeconds = searchTimeInSeconds;
}

/**
 * Checks if is supports content type query.
 * 
 * @return true, if is supports content type query
 */
public boolean isSupportsContentTypeQuery() {
  return supportsContentTypeQuery;
}

/**
 * Sets the supports content type query.
 * 
 * @param supportsContentTypeQuery the new supports content type query
 */
public void setSupportsContentTypeQuery(boolean supportsContentTypeQuery) {
  this.supportsContentTypeQuery = supportsContentTypeQuery;
}

/**
 * Checks if is supports spatial display.
 * 
 * @return true, if is supports spatial display
 */
public boolean isSupportsSpatialDisplay() {
  return supportsSpatialDisplay;
}

/**
 * Sets the supports spatial display.
 * 
 * @param supportsSpatialDisplay the new supports spatial display
 */
public void setSupportsSpatialDisplay(boolean supportsSpatialDisplay) {
  this.supportsSpatialDisplay = supportsSpatialDisplay;
}

// methods =====================================================================
/**
 * Returns records.
 * 
 * @return SearchResultRecords (never null)
 * 
 * @see com.esri.gpt.framework.request.QueryResult#getRecords()
 */
@Override
public SearchResultRecords getRecords() {

  if(super.getRecords() == null) {
    
    this.setRecords(new SearchResultRecords());
  }
  return super.getRecords();
 
      
}

/**
 * Gets the record size.
 * 
 * @return the record size
 */
public int getRecordSize() {
  
  return this.getRecords().size();
  
}

/**
 * Sets the page cursor.
 * 
 * @param cursor the cursor
 * 
 * @see com.esri.gpt.framework.request.QueryResult#setPageCursor(com.esri.gpt.framework.request.PageCursor)
 */
@Override
public void setPageCursor(PageCursor cursor) {
  super.setPageCursor(cursor);
  
}

/**
 * Gets the record as JSON. Will give envelopes & and contenttype
 * 
 * @return the record envelopes as JSON (never null)
 */
public String getRecordsAsJSON() {
  StringBuffer jsRecords = new StringBuffer();
  jsRecords.append("{ \"records\" : [");
  SearchResultRecords records = this.getRecords();
  int index = 0;
  for(SearchResultRecord record : records ) {
    Envelope recEnvelope = record.getEnvelope().clone();
    //Object obj = record.getObjectMap().get(key)
    if(recEnvelope.getMaxY() > 90){
      recEnvelope.setMaxY(90);
    }
    if(recEnvelope.getMinY() < -90) {
      recEnvelope.setMinY(-90);
    }
    jsRecords.append(" {\"bboxes\" : [");
    Envelope[] envs = this.internationalDatelineSplit(recEnvelope);
    int i = 0;
    Envelope enclosingEnvelope = null;
    do {
       
      if(i > 0){
        jsRecords.append(",");
      }
      jsRecords
      .append("{")
      .append( " \"isDefaultGeometry\"  : ").append(record.isDefaultGeometry()).append(",")
      .append( " \"maxX\"  : ").append(envs[i].getMaxX()).append(",")
      .append( " \"maxY\"  : ").append(envs[i].getMaxY()).append(",")
      .append( " \"minX\"  : ").append(envs[i].getMinX()).append(",")
      .append( " \"minY\"  : ").append(envs[i].getMinY())
      .append("}");
      
      if(enclosingEnvelope == null) {
        enclosingEnvelope = envs[i];
      } else {
        enclosingEnvelope.merge(envs[i]);
      }
      i++;
      
    } while(i < envs.length);
    
    jsRecords.append("], \"enclosingEnvelope\" : ")
      .append("{")
      .append( " \"maxX\"  : ").append(enclosingEnvelope.getMaxX()).append(",")
      .append( " \"maxY\"  : ").append(enclosingEnvelope.getMaxY()).append(",")
      .append( " \"minX\"  : ").append(enclosingEnvelope.getMinX()).append(",")
      .append( " \"minY\"  : ").append(enclosingEnvelope.getMinY())
      .append("}");
    
    jsRecords.append(", \"uuid\" : ").append("\"")
      .append(Val.escapeStrForJson(record.getUuid()))
      .append("\"");
    
    jsRecords.append("}");
    enclosingEnvelope = null;
   
    if(index < records.size()-1) {
      jsRecords.append(",");
    }
    
    index++;
  }
  jsRecords.append("]}");
  
  return jsRecords.toString();
}

/**
 * International dateline split. Splits envelope
 * into 2 if envelope goes over international dateline
 * 
 * @param envelope the envelope
 * 
 * @return the envelope[]
 */
private Envelope[] internationalDatelineSplit(Envelope envelope) {
  if(envelope.getMaxX() >= envelope.getMinX()) {
    return new Envelope[]{envelope};
  }
  Envelope leftEnvelope = new Envelope();
  Envelope rightEnvelope = new Envelope();
  leftEnvelope.setMaxY(envelope.getMaxY());
  leftEnvelope.setMinY(envelope.getMinY());
  leftEnvelope.setMinX(-180);
  leftEnvelope.setMaxX(envelope.getMaxX());
  
  rightEnvelope.setMaxY(envelope.getMaxY());
  rightEnvelope.setMinY(envelope.getMinY());
  rightEnvelope.setMaxX(180);
  rightEnvelope.setMinX(envelope.getMinX());
    
  return new Envelope[]{leftEnvelope, rightEnvelope};
}

/**
 * Clear/Reset all instance variables.
 */
public void clear() {
  super.reset();
  currentEnvelopeInView = null;
  this.currentMetadataXmlInView = null;
  super.setMaxQueryHits(Integer.MIN_VALUE);
}








}
