import logging
import sqlite3
import json
import httplib2
import datetime
import time as ttime
from decorators import retry
from os import path

# Zuerich, Basel, Bern, Luzern, Genf
cities = {'Zurich':[47.376848,8.539025],'Basel':[47.549638,7.588567],'Bern':[46.949793,7.436272],'Luzern':[47.049715,8.306252],'Genf':[46.210367,6.13704],'St. Gallen':[47.434358,9.393157], 'Chur':[46.857256,9.525463]}

class Payload(object):

  def generate(self,minutes,c_number,vehicle,location):
    data = {'api_key':'ignore_load_test','api_version':'0.1'}
    constraints = []
    for i in range(c_number):
      constraints.append(Constraint(i,location,minutes,vehicle).toObject())
    data['constraints'] = constraints
    return data

class Constraint(object):

  def  __init__(self, constrainId, location, maxtime = 30, vehicle = "CAR",  starttime = 32400):
    self.__id = constrainId
    self.__location = location
    self.__vehicle = vehicle
    self.__maxtime = maxtime
    self.__starttime = starttime

  def setLocation(self, location):
    self.__location = location

  def toObject(self):
    return {'id':self.__id,'latitude':self.__location[0],'longitude':self.__location[1],'vehicle':self.__vehicle,'maxtime':self.__maxtime,'starttime':self.__starttime,'factor':1}



class Database(object):
  
  def __init__(self, filename = 'database.db', schema = 'schema.sql' ):
    self.__logger = logging.getLogger(__name__)
    self.__filename = filename
    self.__schema = schema
    if path.exists(self.__filename):
      self.__conn = sqlite3.connect(self.__filename, timeout=10)
    else:
      self.__conn = self.__create_table(self.__filename, self.__schema)
    self.__conn.row_factory = self.__dict_factory

  def __dict_factory(self,cursor, row):
    d = {}
    for idx, col in enumerate(cursor.description):
      d[col[0]] = row[idx]
    return d

  def connect(self):
    self.__conn = sqlite3.connect(self.__filename, timeout=10)
    return(self.__conn.cursor())
	
  def close(self):
    """ Commits all queries and closes the database"""
    if self.__conn is not None:
      self.__conn.commit()
      self.__conn.close()
      self.__conn = None

  def commit(self):
    self.__conn.commit()

  def __create_table(self, filename, schema):
    conn = sqlite3.connect(filename, timeout=10)
    f = open(schema,'r')         
    input_schema = f.read()
    conn.executescript(input_schema)
    conn.commit()
    self.__logger.info('New database created: ' + filename)
    return conn
      
  def addEntry(self,test_name,request,response):
    cursor = self.connect()
    sql = 'INSERT into entry (test_name,rqst_id,rqst_time,rqst_maxtime,rqst_vehicle,rqst_cnumber,rsp_size,rsp_time_total,rsp_time_spt,rsp_time_filter,rsp_time_database,rqst_location)  values (?,?,?,?,?,?,?,?,?,?,?,?)'
    cursor.execute(sql,(test_name,request["id"],request["time"],request["maxtime"],request["vehicle"],request["c_number"],response["size"],response["total"],response["spt"],response['filter'],response['database'],request["location"]))
    self.commit()

class Test(object):

  __times = [30,60]
  __numbers = [1]
  __vehicle = 'CAR'
  __requests = 1
  __locations = {'Zurich':[47.376848,8.539025]}

  def __init__(self,name,url,storage):
		self.__logger = logging.getLogger(__name__)
		self.__name = name
		self.__url = url
		self.__storage = storage

  def setLocations(self,locations):
    self.__locations = locations

  def setRequest(self,number):
    self.__requests = number

  def setPublic(self):
    self.__vehicle = 'PUBLIC'

  def setCar(self):
    self.__vehicle = 'CAR'

  def setTimes(self, start = 10, end = 120, steps = 10):
    self.__times = list(range(start,end+1,steps))
    
  def setFixTimes(self,times):
    self.__times = times

  def setNumbers(self, numbers):
    '''
    Set a list of number of constrains
    '''
    self.__numbers = numbers

  def run(self):
    self.__logger.info("Start test '" + self.__name + "' for " + self.__vehicle + ":  0 of " + str(len(self.__times) * len(self.__locations) * len(self.__numbers) * self.__requests))
    counter = 0
    generator = Payload()
    for location_name,location in self.__locations.items():
      for maxtime in self.__times:
        for number in self.__numbers:
          rqst_id = 0
          while (rqst_id < self.__requests):
            payload = generator.generate(maxtime,number,self.__vehicle,location)
            rsp, json_content, time = self.post(self.__url,payload)
            content = json.loads(json_content)
            size = len(content['data'])
            time_total = content['timing']['totalTime']
            time_spt = content['timing']['sptTime']
            time_filter= content['timing']['filterTime']
            time_database = content['timing']['databaseTime']
            #time_spt = 200
            #time_total = 1000
            #time_database = 20
            #time_filter = 1
            response = {'size':size,'total':time_total,'spt':time_spt,'filter':time_filter,'database':time_database}
            request = {'id':rqst_id,'time':time,'maxtime':maxtime,'vehicle':self.__vehicle,'c_number':number,'location':location_name}
            self.__storage.addEntry(self.__name,request,response)
            rqst_id += 1
            counter += 1
            if counter % 200 == 0:
              self.__logger.info('Added '+ str(counter) + ' of '+ str(len(self.__times) * len(self.__numbers) * self.__requests))
        #ttime.sleep(5)
    self.__logger.info("Finish test.")
  
  @retry(Exception, tries=9)
  def post(self,url,payload):
    '''
    Sends the payload in JSON format and POST request to the url
    '''
    http = httplib2.Http()
    data = json.dumps(payload)
    start_time = datetime.datetime.now()
    resp, content = http.request(
      uri=url,
      method='POST',
      headers={'Content-Type': 'application/json; charset=UTF-8'},
      body=data,
    )
    if resp.status != 200:
       raise Exception("Response not OK")
    end_time = datetime.datetime.now()
    rtime = (end_time - start_time)
    self.__logger.debug("Response after " +  str(rtime))
    # Get time in milliseconds
    time = int(round(rtime.seconds * 1E3 + rtime.microseconds / 1E3,0))
    return resp, content, time

class dummyStorage(object):

  def addEntry(self,name,request,response):
    print str(name) + str(request) + str(response)

