from keras.utils.np_utils import to_categorical
import pymysql, threading, json, math
import numpy as np
from threading import Thread
import queue as q

with open('configSql.json', 'r') as configSqlFile:
    configSql = json.load(configSqlFile)
with open('config.json', 'r') as configFile:
    config = json.load(configFile)

cursor = pymysql.connect(host=configSql['host'], port=configSql['port'], user=configSql['user'], passwd=configSql['passwd'], db=configSql['db']).cursor()

def chooseDatabase(lang):
    negativeTextsTable = configSql[lang+'_negative']
    positiveTextsTable = configSql[lang+'_positive']
    return positiveTextsTable, negativeTextsTable

samplesInBatch = config['samplesInBatch']
batchesInMemory = config['batchesInMemory']
startPosition = config['startPosition']
trainSamples = config['trainSamples']
validSamples = config['validSamples']
testSamples = config['testSamples']
max_text_len = config['textLength']

names = list(map(str, np.random.randint(10000, 9999999, trainSamples+validSamples+testSamples)))
alphabet = list("abcdefghijklmnopqrstuvwxyz"+" 0123456789"+"áčďéěíňóřšťúůž"+"äöüß"+"ñ¡¿"+"!#%&<>*,-./:;=?@[]^_{}°~"+'"\\'+"'"+"£$€")
alphabet_dict = dict(zip(alphabet, range(0, len(alphabet))))


def dbQuery(lowInterval, highInterval, positiveTextsTable, negativeTextsTable):
    positiveTexts = []
    negativeTexts = []
    cursor.execute("SELECT text FROM " + positiveTextsTable + " WHERE id BETWEEN '" + str(lowInterval+1) + "' AND '" + str(highInterval) + "' ")
    for text in cursor:
        positiveTexts.append(text[0])
    cursor.execute("SELECT text FROM " + negativeTextsTable + " WHERE id BETWEEN '" + str(lowInterval+1) + "' AND '" + str(highInterval) + "' ")
    for text in cursor:
        negativeTexts.append(text[0])
    return positiveTexts, negativeTexts

def createBatches(positiveTexts, negativeTexts):
    InMemoryBatches = []
    InMemoryBatchesCats = []
    batch = []
    batchCategory = np.empty(samplesInBatch*2)
    i = 0
    for positiveText, negativeText in zip(positiveTexts, negativeTexts):
        batch.append(positiveText)
        batch.append(negativeText)
        batchCategory[((i*2)%(samplesInBatch*2))] = 1
        batchCategory[((i*2)%(samplesInBatch*2)+1)] = 0
        i = i + 1
        if(i % samplesInBatch == 0):
            InMemoryBatches.append(_convert(batch[:]))
            InMemoryBatchesCats.append(to_categorical(batchCategory[:]))
            batchCategory = np.empty(samplesInBatch*2)
            batch.clear()
    positiveTexts.clear()
    negativeTexts.clear()
    return InMemoryBatches, InMemoryBatchesCats


def _convert(batch):
    transformedBatch = []
    for text in batch:
        text_chars = list(text.lower())
        text_chars_len = len(text_chars)
        matrix = np.zeros((max_text_len, len(alphabet)))
        for i in range(0, max_text_len):
          ch = text_chars[i] if text_chars_len > i else ' '
          if ch in alphabet_dict:
            j = alphabet_dict[ch]
            matrix[i][j] = 1.0
        transformedBatch.append(matrix)
    return transformedBatch


def beginingGen(queryActualNo, startPosition, numOfSamples, positiveTextsTable, negativeTextsTable):
        lowInterval = startPosition + queryActualNo * samplesInBatch * batchesInMemory
        highInterval = startPosition + (queryActualNo + 1) * samplesInBatch * batchesInMemory
        if highInterval > startPosition+numOfSamples:
            highInterval = startPosition+numOfSamples
        positiveTexts, negativeTexts = dbQuery(lowInterval, highInterval, positiveTextsTable, negativeTextsTable)
        return createBatches(positiveTexts, negativeTexts)

class threadsafe_iter(object):

    def __init__(self, it):
        self.it = it
        self.lock = threading.Lock()

    def __iter__(self):
        return self

    def __next__(self):
        with self.lock:
            return next(self.it)

def threadsafe_generator(f):
    def g(*a, **kw):
        return threadsafe_iter(f(*a, **kw))
    return g

@threadsafe_generator
def data_generator(startPosition, numOfSamples, language, type='train'):
    positiveTextsTable, negativeTextsTable = chooseDatabase(language)
    if type == 'test':

        startPosition = startPosition
        numOfSamples = numOfSamples
        numIter = math.ceil(trainSamples/(samplesInBatch*batchesInMemory))

    elif type == 'valid':
        startPosition = startPosition
        numOfSamples = numOfSamples
        numIter = math.ceil(validSamples /(samplesInBatch * batchesInMemory))
    else:

        startPosition = startPosition
        numOfSamples = numOfSamples
        numIter = math.ceil(testSamples / (samplesInBatch * batchesInMemory))
    while True:
        for iter in range (0,numIter):
            X, Y = beginingGen(iter, startPosition, numOfSamples, positiveTextsTable, negativeTextsTable)
            for x, y in zip(X, Y):
                    yield(np.asarray(x), np.asarray(y))

def data_informer():

    data_info = {}
    data_info['data_name'] = 'Text | split 0.5 and 0.5,0.5'
    data_info['list_of_class_names'] = ['classA', 'classB']

    data_info['nb_train_samples'] = trainSamples*2
    data_info['list_of_train_files'] = names

    data_info['nb_valid_samples'] = validSamples*2
    data_info['list_of_valid_files'] = names

    data_info['nb_test_samples'] = testSamples*2
    data_info['list_of_test_files'] = names

    return data_info

def data_provide(language):
    train_valid = True
    test = True

    data_info = data_informer()
    data_name = data_info['data_name']
    list_of_class_names = data_info['list_of_class_names']

    if train_valid:
        traindata_gen = data_generator(startPosition, trainSamples, language, 'train')
        num_trainsamples = data_info['nb_train_samples']
        list_of_train = data_info['list_of_train_files']

        validdata_gen = data_generator(startPosition+trainSamples, validSamples, language, 'valid')
        num_validsamples = data_info['nb_valid_samples']
        list_of_valid = data_info['list_of_valid_files']

    else:
        traindata_gen = num_trainsamples = validdata_gen = num_validsamples = list_of_train = list_of_valid = None

    if test:
        testdata_gen = data_generator(startPosition+validSamples+trainSamples, testSamples, language, 'test')
        num_testsamples = data_info['nb_test_samples']
        list_of_test = data_info['list_of_test_files']
    else:
        testdata_gen = num_testsamples = list_of_test = None

    return traindata_gen, validdata_gen, testdata_gen, \
           num_trainsamples, num_validsamples, num_testsamples, list_of_class_names, \
           list_of_train, list_of_valid, list_of_test, data_name, samplesInBatch*2
