from types import *
import logging
import urllib.request, urllib.parse, urllib.error

import requestresponder
from edge.httputility import HttpUtility
import math

class ResponseWriter(requestresponder.RequestResponder):
    def __init__(self, configFilePath, requiredParams = None):
        super(ResponseWriter, self).__init__(configFilePath)
        if requiredParams is None:
            requiredParams = []
        self.requiredParams = requiredParams
        self.searchParameters = {}
        self.pretty = True
        self.variables = {}
    
    def get(self, requestHandler):
        super(ResponseWriter, self).get(requestHandler)
        #check required parameters
        for paramList in self.requiredParams:
            countParamNotFound = 0
            for param in paramList:
                try:
                    requestHandler.get_argument(param)
                except:
                    countParamNotFound += 1
            if countParamNotFound == len(paramList):
                raise Exception("One of the following parameters is required: " + ', '.join(paramList))
    
    def _constructSingleSolrDatasetQuery(self, variables):
        queries = []
        for key, value in variables.items():
            # Only key used for ISO granule record is dataset
            if key == 'datasetId':
                query = 'Dataset-PersistentId:'+self._urlEncodeSolrQueryValue(value)
                queries.append(query)
            elif key == 'shortName':
                query = 'Dataset-ShortName-Full:'+self._urlEncodeSolrQueryValue(value)
                queries.append(query)

        if len(queries) == 0:
            queries.append('*')

        query = 'q='+'+AND+'.join(queries)+'&fq=DatasetPolicy-AccessType-Full:(OPEN+OR+PREVIEW+OR+SIMULATED+OR+REMOTE)+AND+DatasetPolicy-ViewOnline:Y&version=2.2&rows=1&indent=on&wt=json'
        logging.debug('solr query: '+query)
        
        return query
    
    def _getSingleSolrDatasetResponse(self, variables, callback):
        query = self._constructSingleSolrDatasetQuery(variables)
        url = self._configuration.get('solr', 'datasetUrl')

        httpUtility = HttpUtility()
        return httpUtility.getResponse(url+'/select/?'+query, callback)
    
    def _urlEncodeSolrQueryValue(self, value):
        return urllib.parse.quote('"'+value+'"')
    
    def _constructBoundingBoxQuery(self, value):
        coords = value.split(",")
        if len(coords) < 4:
            return None
        try:
            west = float(coords[0])
            south = float(coords[1])
            east = float(coords[2])
            north = float(coords[3])
            
            centerY = (south + north) / 2
            halfHeight = math.fabs(north - south) / 2
            
            #Check if we need to split box into two
            if (east < west):
                west1 = west
                east1 = 180.0
                
                centerX1 = (west1 + east1) / 2
                halfWidth1 = math.fabs(east1 - west1) / 2
                
                west2 = -180.0
                east2 = east
                
                centerX2 = (west2 + east2) / 2
                halfWidth2 = math.fabs(east2 - west2) / 2
                
                return "fq={!frange+l=1+u=2}map(sum(" + self._solrSeparatingXAxisFunctionQueryAggregate(centerX1, halfWidth1, centerX2, halfWidth2) + "," + self._solrSeparatingYAxisFunctionQuery(centerY, halfHeight) + "),0,0,1,0)&fq=CenterY:*"
            else:
                centerX = (west + east) / 2
                halfWidth = math.fabs(east - west) / 2

                return "fq={!frange+l=1+u=2}map(sum(" + self._solrSeparatingXAxisFunctionQuery(centerX, halfWidth) + "," + self._solrSeparatingYAxisFunctionQuery(centerY, halfHeight) + "),0,0,1,0)&fq=CenterY:*"
        except:
            return None

    def _solrSeparatingXAxisFunctionQuery(self, center, width):
        return "map(sum(" + self._solrSeparatingAxixFunction(center, "CenterX1", width, "HalfWidth1", 360) + "," + self._solrSeparatingXAxixFunctionQueryPossibleNullWidth(center, width) + "),0,1,0,1)"

    def _solrSeparatingXAxisFunctionQueryAggregate(self, center1, width1, center2, width2):
        return "map(sum(" + self._solrSeparatingAxixFunction(center1, "CenterX1", width1, "HalfWidth1", 360) + ","+ self._solrSeparatingXAxixFunctionQueryPossibleNullWidth(center1, width1) + "," +self._solrSeparatingAxixFunction(center2, "CenterX1", width2, "HalfWidth1", 360) + "," + self._solrSeparatingXAxixFunctionQueryPossibleNullWidth(center2, width2) +"),0,3,0,1)"
    
    def _solrSeparatingYAxisFunctionQuery(self, center, height):
        return self._solrSeparatingAxixFunction(center, "CenterY", height, "HalfHeight", 180)
    
    def _solrSeparatingAxixFunction(self, center, centerVar, length, lengthVar, span):
        return "map(sub(abs(sub(%.15g,%s)),sum(%.15g,%s)),0,%i,1,0)" % (center, centerVar, length, lengthVar, span)
    
    def _solrSeparatingXAxixFunctionQueryPossibleNullWidth(self, center, length):
        return "map(sum(map(HalfWidth2,0,0,1,0),map(sub(abs(sub(%.15g,CenterX2)),sum(%.15g,HalfWidth2)),0,360,1,0)),0,0,0,1)" % (center, length)

    def _generateOpenSearchResponse(self, solrResponse, searchText, searchUrl, searchParams, pretty):
        pass
    
    def _onSolrResponse(self, response):
        if response.error:
            self._handleException(str(response.error))
        else:
            self._writeResponse(response.body)
    
    def _writeResponse(self, responseText):
        searchText = ''
        if 'keyword' in self.variables:
            searchText = self.variables['keyword']
        try:
            openSearchResponse = self._generateOpenSearchResponse(
                responseText,
                searchText,
                self._configuration.get('service', 'url') + self.requestHandler.request.path,
                self.searchParameters,
                self.pretty
            )
            self.requestHandler.set_header("Content-Type", "application/xml")
            self.requestHandler.write(openSearchResponse)
            self.requestHandler.finish()
        except BaseException as exception:
            self._handleException(str(exception))
    
    def _handleException(self, error):
        self.requestHandler.set_status(404)
        self.requestHandler.write(error)
        self.requestHandler.finish()
