from influxdb import InfluxDBClient as Influx
from datetime import datetime
import os, pytz, math

tw = pytz.timezone('Asia/Taipei')
utc = pytz.timezone('UTC')
env = os.environ
host = env['INFLUX_HOST']      if 'INFLUX_HOST' in env else '192.168.6.240'
port = int(env['INFLUX_PORT']) if 'INFLUX_PORT' in env else 8086
usr  = env['INFLUX_USR']       if 'INFLUX_USR'  in env else 'root'
pwd  = env['INFLUX_PWD']       if 'INFLUX_PWD'  in env else 'root'
db   = env['INFLUX_DB']        if 'INFLUX_DB'   in env else 'ecom'
influx = Influx(host, port, usr, pwd, db)
influx.create_database(db)

class BaseInfluxModel(object):
    def __init__(self):
        self.data = []
    def Add(self, item):
        self.data.append(item.raw)
    def Upload(self):
#         print(self.data[0]['time'], self.data[0]['time'].timetz().tzinfo)
        try:
            influx.write_points(self.data)
            self.data.clear()
            return True
        except:
            return False
class BaseItemInfluxModel(object):
    def __init__(self, measurement, time):
        self.raw = {}
        self.raw['measurement'] = measurement
        print(time)
        self.raw['time'] = tw.localize(time) if time.tzinfo is None else time
        self.raw['tags'] = {}
        self.raw['fields'] = {}
class LogInfluxModel(BaseItemInfluxModel):
    def __init__(self, model):
        super().__init__('log', datetime.now())
        self.raw['tags']['model'] = model
        self.raw['tags']['host'] = None
        self.raw['tags']['user'] = None
        self.raw['fields']['code'] = None
        self.raw['fields']['message'] = None
    def SetHost(self, host):
        self.raw['tags']['host'] = host if isinstance(host, str) else str(host)
    def GetHost(self):
        return self.raw['tags']['host']
    def SetUser(self, user):
        self.raw['tags']['user'] = user if isinstance(user, str) else str(user)
    def SetCode(self, code):
        self.raw['fields']['code'] = code if isinstance(code, int) else int(code)
    def SetMessage(self, message):
        self.raw['fields']['message'] = message
#         self.raw['fields']['message'] = message if isinstance(message, str) else int(message)
    def GetCode(self):
        return self.raw['fields']['code']
    def GetMessage(self, data=None, code=1):
        message = {
            "Time": self.raw['time'].strftime('%Y-%m-%d %H:%M:%S'),
            "Host": self.raw['tags']['host'],
            "User": self.raw['tags']['user'],
            "Method": self.raw['tags']['model'],
            "Message": self.raw['fields']['message'],
            "Code": code,
            "Data": data,
        }
        return message
# ========== punch card data influxdb model ==========
# class PunchInfluxModel(BaseInfluxModel):
#     def __init__(self, host, location):
#         super().__init__('punch')
#         self.tags = {
#             'host': host,
#             'location': location,
#         }
#     def Add(self, time, ID, name, similarity, temperature, position, image):
#         self.data.append({
#             'measurement': self.measurement,
#             'tags': self.tags,
#             'time': time,
#             'fields': {
#                 'id': ID,
#                 'name': name,
#                 'similarity': float(similarity),
#                 'temperature': float(temperature),
#                 'position_x': int(position[0]),
#                 'position_y': int(position[1]),
#                 'position_w': int(position[2]),
#                 'position_h': int(position[0]),
#                 'image': image,
#             }
#         })
class PunchInfluxModel(BaseItemInfluxModel):
    def __init__(self, device, host, location, time):
        super().__init__('punch', time)
        self.raw['tags']['device'] = device
        self.raw['tags']['host'] = host
        self.raw['tags']['location'] = location
        self.raw['tags']['uid'] = "" # uid == id
        self.raw['fields']['id'] = ""
        self.raw['fields']['name'] = ""
        self.raw['fields']['type'] = -1
        self.raw['fields']['similarity'] = 0.0
        self.raw['fields']['temperature'] = 0.0
        self.raw['fields']['position_x'] = 0
        self.raw['fields']['position_y'] = 0
        self.raw['fields']['position_w'] = 0
        self.raw['fields']['position_h'] = 0
        self.raw['fields']['image'] = ""
    def SetUID(self, UID):
        self.raw['tags']['uid'] = str(UID) if isinstance(UID, int) else UID
    def SetID(self, ID):
        self.raw['fields']['id'] = str(ID) if isinstance(ID, int) else ID
    def SetName(self, name):
        self.raw['fields']['name'] = str(name) if isinstance(name, int) else name
    def SetType(self, _type):
        self.raw['fields']['type'] = int(_type) if isinstance(_type, str) else _type
    def SetSimilarity(self, similarity):
        self.raw['fields']['similarity'] = float(similarity)
    def SetTemperature(self, temperature):
        self.raw['fields']['temperature'] = math.floor(float(temperature) * 10) / 10
    def SetPosition(self, position):
        self.SetX(position[0])
        self.SetY(position[1])
        self.SetWidth(position[2])
        self.SetHeight(position[3])
    def SetX(self, x):
        self.raw['fields']['position_x'] = int(x) if isinstance(x, str) else x
    def SetY(self, y):
        self.raw['fields']['position_y'] = int(y) if isinstance(y, str) else y
    def SetWidth(self, width):
        self.raw['fields']['position_w'] = int(width) if isinstance(width, str) else width
    def SetHeight(self, height):
        self.raw['fields']['position_h'] = int(height) if isinstance(height, str) else height
    def SetImage(self, image):
        self.raw['fields']['image'] = str(image)
    def GetID(self):
        return self.raw['fields']['id']
    def GetName(self):
        return self.raw['fields']['name']
    def GetType(self):
        return self.raw['fields']['type']
    def GetDevice(self):
        return self.raw['tags']['device']
    def GetLocation(self):
        return self.raw['tags']['location']
    def GetSimilarity(self):
        return self.raw['fields']['similarity']
    def GetTime(self):
        return self.raw['time']
    def GetImage(self):
        return self.raw['fields']['image']
    def GetTemperature(self):
        return self.raw['fields']['temperature']
    def GetDateTime(self):
        return self.raw['time']
    @staticmethod
    def FromRaw(point):
        # the timezone is empty while convert string to datetime object
        # replace empty timezone to utc, because selected data is utc from influxdb
        time = datetime.strptime(point['time'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=utc)
#         time = utc.localize(time)
#         print(time, time.timetz().tzinfo)
        # change timezone from utc to tw
        time = time.astimezone(tw)
#         time = tw.localize(time)
#         print(time, time.timetz().tzinfo)
        model = PunchInfluxModel(point['device'], point['host'], point['location'], time)
        model.SetID(point['id'])
        model.SetName(point['name'])
        model.SetType(point['type'] if 'type' in point else -1)
        model.SetSimilarity(0 if point['similarity'] is None else point['similarity'])
        model.SetTemperature(0 if point['temperature'] is None else point['temperature'])
        model.SetX(0 if point['position_x'] is None else point['position_x'])
        model.SetY(0 if point['position_y'] is None else point['position_y'])
        model.SetWidth(0 if point['position_w'] is None else point['position_w'])
        model.SetHeight(0 if point['position_h'] is None else point['position_h'])
        model.SetImage(point['image'])
        return model
    @staticmethod
    def AllFromRaw(start=None, end=None, temperature=None):
        query = "SELECT * FROM {0}".format('punch')
        if start is not None:
            start = start.replace(tzinfo=tw)
            query = "{0} WHERE time > {1}".format(query, int(datetime.timestamp(start)) * 1000000000)
            if end is not None:
                end = end.replace(tzinfo=tw)
                query = "{0} AND {1} > time".format(query, int(datetime.timestamp(end)) * 1000000000)
            if temperature is not None:
                query = "{0} AND temperature > {1}".format(query, int(temperature))
        result = influx.query(query)
        models = []
        for point in result.get_points():
            models.append(PunchInfluxModel.FromRaw(point))
        return models
    @staticmethod
    def FirstFromRaw():
        query = "SELECT * FROM {0} ORDER BY time DESC LIMIT 1".format('punch')
        result = influx.query(query)
        models = []
        for point in result.get_points():
            models.append(PunchInfluxModel.FromRaw(point))
        return models[0] if len(models) > 0 else None
