#!/usr/bin/env python3
import collections
import io
import itertools
import json
import logging
import math
import os
import sqlite3

import Bio.Phylo
import dendropy

from JSON_templates import write_assessment_dataset
from helpers import auto_open, load_json_file

logger = logging.getLogger("SP-Benchmark")

def get_swissprot_entries(mapping_path, sp_file):
    mapping = load_json_file(mapping_path)
    sp_entries = {}
    excluded = 0
    with auto_open(sp_file, 'rt') as fh:
        for line in fh:
            # line contains >sp|A0A0R4IKJ1|CAPAM_DANRE
            sp = line.strip().split('|')[-1]
            try:
                enr = mapping['mapping'][sp]
                sp_entries[enr] = sp
            except KeyError:
                if 'excluded_ids' not in mapping or sp not in mapping['excluded_ids']:
                    raise
                excluded += 1
    logger.info("{} swissprot IDs from excluded species ignored".format(excluded))
    logger.info("found {} swissprot entries".format(len(sp_entries)))
    return sp_entries

def get_idpart(sp):
    return sp.rsplit('_', 1)[0]

def get_species(sp):
    s = sp.rsplit('_', 1)[1]
    if s == "RAT":
        s = "RATNO"
    return s

class SwissProtComparerSimple:
    def __init__(self, sp_entries, **kwargs):
        self.sp_entries = sp_entries
        self._init_relations()

    def are_orthologs(self, en1, en2):
        #sp_id1, sp_id2 = tuple(sp_entries[enr].rsplit('_', 1)[0] for enr in (en1, en2))
        #return sp_id1
        return (min(en1, en2), max(en1, en2)) in self.true_orthologs

    def are_non_orthologs(self, en1, en2, **kwargs):
        # check that they have non common prefix, e.g. the sp id starts with a different character
        return self.sp_entries[en1][0] != self.sp_entries[en2][0]

    def _init_relations(self):
        self.true_orthologs = frozenset(self._get_true_orthologs_by_identical_ids())

    def _get_true_orthologs_by_identical_ids(self):
        res = set([])
        sorted_sp_entries = sorted(((v, k) for k, v in self.sp_entries.items()), key=lambda x: get_idpart(x[0]))
        for grp, entries in itertools.groupby(sorted_sp_entries, key=lambda x: get_idpart(x[0])):
            res.update({(min(x[1], y[1]), max(x[1], y[1])) for x, y in itertools.combinations(entries, 2)})
        return res

class SwissProtComparerTaxRangeLimited(SwissProtComparerSimple):
    def __init__(self, sp_entries, species_tree_fn, **kwargs):
        super().__init__(sp_entries, **kwargs)
        self.species_tree = self._load_species_tree(species_tree_fn)
        self._per_fam_species_to_consider = self._extract_per_fam_species_set()

    def _load_species_tree(self, tree_fn):
        with open(tree_fn, 'rt') as fh:
            tree = Bio.Phylo.read(fh, 'phyloxml')
        for n in tree.get_terminals():
            n.name = n.taxonomy.code
        for n in tree.get_nonterminals():
            n.name = n.taxonomy.scientific_name
        buf = io.StringIO()
        Bio.Phylo.write(tree, buf, 'newick')
        tree = dendropy.Tree.get(data=buf.getvalue(), schema="newick")
        return tree

    def _extract_per_fam_species_set(self):
        swissprot_sorted = sorted(self.sp_entries.values(), key=get_idpart)
        per_fam_species = collections.defaultdict(set)
        for fam, prot in itertools.groupby(swissprot_sorted, key=get_idpart):
            prots = list(prot)
            sps = set([get_species(x) for x in prots])
            assert (len(sps) == len(prots))
            if len(prots) > 1:
                mrca = self.species_tree.mrca(taxon_labels=sps)
                tax_range_species = set(l.taxon.label for l in mrca.leaf_iter())
                # remove direct clades from mrca if no annoations and at least 3 leaves
                rem_clades = []
                for c in mrca.child_node_iter():
                    sub_clade_leaves = set(l.taxon.label for l in c.leaf_iter())
                    if len(sub_clade_leaves) > 2 and len(sps.intersection(sub_clade_leaves)) == 0:
                        tax_range_species -= sub_clade_leaves
                        rem_clades.append(c.label)
                per_fam_species[fam] = tax_range_species
                logger.debug("SwissProt \"{}\": present in {}. Propagating to {} excluding {}"
                             .format(fam, sps, mrca.label, rem_clades))
        return per_fam_species

    def are_non_orthologs(self, en1, en2, info1, info2, **kwargs):
        id1, id2 = (get_idpart(sp_entries[en]) for en in (en1, en2))
        org1, org2 = (i.Species for i in (info1, info2))
        res = id1[0] != id2[0] and \
              org2 in self._per_fam_species_to_consider[id1] and \
              org1 in self._per_fam_species_to_consider[id2]
        if id1[0] != id2[0] and logger.isEnabledFor(logging.DEBUG):
            logger.debug("check non-ortholog: {} vs {}; {}:{}; {}:{}; return: {}".
                         format(sp_entries[en1], sp_entries[en2], id1,
                                self._per_fam_species_to_consider[id1],
                                id2,
                                self._per_fam_species_to_consider[id2],
                                res))
        return res

class SwissProtComparerExistingIdInBothSpecies(SwissProtComparerSimple):
    def __init__(self, sp_entries, **kwargs):
        super().__init__(sp_entries, **kwargs)
        self.ids_per_species = self._build_idset_per_species()

    def _build_idset_per_species(self):
        sps = collections.defaultdict(set)
        for en, spid in self.sp_entries.items():
            id_, species = spid.rsplit('_', 1)
            sps[species].add(id_)
        return sps

    def are_non_orthologs(self, en1, en2, info1, info2, **kwargs):
        id1, id2 = (get_idpart(sp_entries[en]) for en in (en1, en2))
        org1, org2 = (i.Species for i in (info1, info2))
        res = id1[0] != id2[0] and id1 in self.ids_per_species[org2] and id2 in self.ids_per_species[org1]
        if id1[0] != id2[0] and logger.isEnabledFor(logging.DEBUG):
            logger.debug("check non-ortholog: {} vs {}: {} in {}: {}; {} in {}: {}; return {}"
                         .format(sp_entries[en1], sp_entries[en2],
                                 id1, org2, id1 in self.ids_per_species[org2],
                                 id2, org1, id2 in self.ids_per_species[org1],
                                 res))
        return res

Protein = collections.namedtuple("Protein", ["Acc", "Species"])

def create_one2one_orthologs_table(db_path):
    def load_species():
        cur = con.cursor()
        cur.execute("SELECT species, min(prot_nr), max(prot_nr) FROM proteomes GROUP BY species ORDER BY prot_nr")
        return {z[0]: (z[1], z[2]) for z in cur.fetchall()}

    def create_one2one_table():
        cur = con.cursor()
        cur.execute("""DROP TABLE IF EXISTS one2one_orthologs""")
        cur.execute("""CREATE TABLE one2one_orthologs (
                                   prot_nr1 INT ,
                                   prot_nr2 INT
                               )""")
        con.commit()

    def create_indexes():
        cur = con.cursor()
        cur.execute("DROP INDEX IF EXISTS one2one_pair")
        cur.execute("CREATE INDEX one2one_pair ON one2one_orthologs (prot_nr1, prot_nr2)")
        logger.info("finished indexing")
        con.commit()

    def filter_species_pair(g1, g2):
        cur = con.cursor()
        cur.execute("SELECT * from orthologs WHERE prot_nr1>=? and prot_nr1 <= ? and prot_nr2>=? and prot_nr2<=?",
                    (genomes[g1][0], genomes[g1][1], genomes[g2][0], genomes[g2][1],))
        pairs = cur.fetchall()
        cnt = collections.Counter(itertools.chain.from_iterable(pairs))
        one_to_one_pairs = [rel for rel in pairs if cnt[rel[0]] == 1 and cnt[rel[1]] == 1]
        logger.info("{} vs {}: kept {} 1:1 orthologs from {} initially ({:.1f}%%)".format(
            g1, g2, len(one_to_one_pairs), len(pairs), 100 * len(one_to_one_pairs) / len(pairs)
        ))
        one_to_one_pairs.extend([(z[1], z[0]) for z in one_to_one_pairs])
        cur.executemany("INSERT INTO one2one_orthologs (prot_nr1, prot_nr2) VALUES (?, ?)", one_to_one_pairs)
        con.commit()

    con = sqlite3.connect(db_path)
    genomes = load_species()
    create_one2one_table()
    for g1, g2 in itertools.combinations(genomes, 2):
        filter_species_pair(g1, g2)
    create_indexes()
    con.close()

def compute_sp_benchmark(sp_entries, db_path, raw_out, strategy: SwissProtComparerSimple, orth_tab):

    def get_prot_data_for(proteins):
        data = {}
        cur = con.cursor()
        placeholder = ",".join('?'*len(proteins))
        query = f"SELECT * FROM proteomes WHERE prot_nr IN ({placeholder})"
        cur.execute(query, proteins)
        for row in cur.fetchall():
            data[row[0]] = Protein(row[1], row[2])
        return data

    def get_swissprot_orthologs_of(prot_nr):
        cur = con.cursor()
        cur.execute(
            f"SELECT DISTINCT prot_nr2 FROM {orth_tab} WHERE prot_nr1 == ? AND prot_nr1 < prot_nr2 ORDER BY prot_nr2",
            (prot_nr,))
        return [z[0] for z in cur.fetchall() if z[0] in sp_entries]

    def write_raw_rels(out, rels, typ):
        for en1, en2 in rels:
            out.write("{}\t{}\t{}\t{}\t{}\n".format(sp_entries[en1], sp_entries[en2], typ,
                                                    protein_infos[en1].Species, protein_infos[en2].Species))

    con = sqlite3.connect(db_path)
    nr_true = len(strategy.true_orthologs)
    missing_true_orthologs = set(strategy.true_orthologs)
    protein_infos = get_prot_data_for(list(sp_entries.keys()))

    orthologs_among_sp = 0
    tp = 0
    fp = 0
    for sp_entry in sp_entries:
        orths = get_swissprot_orthologs_of(sp_entry)
        orthologs_among_sp += len(orths)
        false_positives = [(sp_entry, en) for en in orths
                           if strategy.are_non_orthologs(sp_entry, en, info1=protein_infos[sp_entry],
                                                         info2=protein_infos[en])]
        fp += len(false_positives)
        write_raw_rels(raw_out, false_positives, 'FP')

        true_positives = {(sp_entry, en) for en in orths if strategy.are_orthologs(sp_entry, en)}
        tp += len(true_positives)
        write_raw_rels(raw_out, true_positives, 'TP')
        missing_true_orthologs -= true_positives

    con.close()
    write_raw_rels(raw_out, missing_true_orthologs, "FN")
    tpr = tp / nr_true
    ppv = tp / (fp + tp)
    logger.info("TPR: {}; PPV: {}, nr_true: {}".format(tpr, ppv, nr_true))
    metrics = [{"name": "TP", 'value': tp},
               {"name": "Nr orthologs among SwissProt entries", 'value': orthologs_among_sp},
               {"name": "FP", "value": fp},
               {"name": "FN", "value": len(missing_true_orthologs)},
               {"name": "TPR", "value": tpr, "stderr": 1.96 * math.sqrt(tpr * (1 - tpr) / nr_true)},
               {"name": "PPV", "value": ppv, "stderr": 1.96 * math.sqrt(ppv * (1 - ppv) / (fp + tp))},
               ]
    return metrics

def write_assessment_json_stub(fn, community, participant, result):
    challenge = "SwissProtIDs"
    stubs = []
    for metric in result:
        id_ = "{}:{}_{}_{}_A".format(community, challenge, metric['name'],
                                     participant.replace(' ', '-').replace('_', '-'))
        stubs.append(write_assessment_dataset(id_, community, challenge, participant, metric['name'], metric['value'],
                                              metric.get('stderr', 0)))
    with auto_open(fn, 'wt') as fout:
        json.dump(stubs, fout, sort_keys=True, indent=4, separators=(',', ': '))

if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description="SwissProtID conservation benchmark")
    parser.add_argument('--mapping', required=True, help="Path to mapping.json of proper QfO dataset")
    parser.add_argument('--assessment-out', required=True, help="Path where assessment json file will be stored")
    parser.add_argument('--outdir', required=True, help="Folder to store the raw output file in")
    parser.add_argument('--db', required=True, help="Path to sqlite database with pairwise predictions")
    parser.add_argument('--com', required=True, help="community id")
    parser.add_argument('--sp-entries', required=True, help="Path to textfile with SwissProt IDs")
    parser.add_argument('--participant', required=True, help="Name of participant method")
    parser.add_argument('--only-one2one', action="store_true")
    parser.add_argument('--strategy', choices=("simple", "clade_limit", "ids_exist_in_both"),
                        default="clade_limit",
                        help="benchmark strategy to use. Simple: negatives are any non-prefix sharing relation, "
                             "Clade_limit: only within clades where ID is used.")
    parser.add_argument('--lineage-tree', help="path to lineage tree in phyloxml format. Used for clade_limit strategy only")
    parser.add_argument('--log', help="Path to log file. Defaults to stderr")
    parser.add_argument('-d', '--debug', action="store_true", help="Set logging to debug level")
    conf = parser.parse_args()

    log_conf = {'level': logging.INFO, 'format': "%(asctime)-15s %(levelname)-7s: %(message)s"}
    if conf.log is not None:
        log_conf['filename'] = conf.log
    if conf.debug:
        log_conf['level'] = logging.DEBUG
    logging.basicConfig(**log_conf)
    logger.info("running swissprot_benchmark with following arguments: {}".format(conf))

    os.makedirs(conf.outdir, exist_ok=True)
    outfn_path = os.path.join(conf.outdir,
                              "SP_{}_{}_raw.txt.gz".format(conf.participant.replace(' ', '-').replace('_', '-'),
                                                           conf.strategy)
                              )
    sp_entries = get_swissprot_entries(conf.mapping, conf.sp_entries)
    if conf.strategy.lower() == "simple":
        strategy = SwissProtComparerSimple(sp_entries)
    elif conf.strategy.lower() == "clade_limit":
        strategy = SwissProtComparerTaxRangeLimited(sp_entries, species_tree_fn=conf.lineage_tree)
    elif conf.strategy.lower() == "ids_exist_in_both":
        strategy = SwissProtComparerExistingIdInBothSpecies(sp_entries)
    else:
        raise Exception("Invalid strategy")

    orth_tab = "orthologs"
    if conf.only_one2one:
        create_one2one_orthologs_table(conf.db)
        orth_tab = "one2one_orthologs"

    with auto_open(outfn_path, 'wt') as raw_out_fh:
        res = compute_sp_benchmark(sp_entries, conf.db, raw_out_fh, strategy, orth_tab=orth_tab)
    write_assessment_json_stub(conf.assessment_out, conf.com, conf.participant, res)


# -*- coding: utf-8 -*-
"""Class definitions for YOLO model and Red-Blue line detection model
"""
import sys
import os
from settings import RDT_GIT_ROOT
from settings import FLU_AUDERE_PATH
from settings import FLU_AUDERE_LINE_PATH
from settings import LINE_MODEL_VER
path = os.path.join(os.getcwd(),"tensorflow-yolov3")
sys.path.append(path)

path = os.path.join(RDT_GIT_ROOT,"tensorflow-yolov3")
sys.path.append(path)

jpg1Path = os.path.join(RDT_GIT_ROOT,"1.jpg")
jpg2Path = os.path.join(RDT_GIT_ROOT,"2.jpg")

#sys.path.append("C:\\Users\\developer\\Anaconda_3\\rdt-reader\\tensorflow-yolov3")

import json
import cv2 as cv
import numpy as np
from PIL import Image
from datetime import datetime as dt
from core import utils
from core.config import cfg

#grpc imports
import tensorflow as tf

def adjust_gamma(image, gamma=1.0):
	# build a lookup table mapping the pixel values [0, 255] to
	# their adjusted gamma values
	invGamma = 1.0 / gamma
	table = np.array([((i / 255.0) ** invGamma) * 255
		for i in np.arange(0, 256)]).astype("uint8")

	# apply gamma correction using the lookup table
	return cv.LUT(image, table)

def enhanceImage(img):
    img = np.uint8(img)
    newimg = cv.cvtColor(img, cv.COLOR_RGB2HLS)
    clahe = cv.createCLAHE(10, (5,5))
    # newimg[1]=cv.normalize(newimg[1], 0, 255, cv.NORM_MINMAX)
    lab_planes = cv.split(newimg)
    lab_planes[1] = clahe.apply(lab_planes[1])
    lab = cv.merge(lab_planes)
    result=cv.cvtColor(lab, cv.COLOR_HLS2RGB)
    result = adjust_gamma(result,0.5)
    return result

def gaussBlur(img):
    img = cv.GaussianBlur(img,(1,11),0)
    return img

class YOLO:
    def __init__(self, input_size=512,weightsPath=FLU_AUDERE_PATH):
        """This function initializes the YOLO model and warms it up and returns predictor function handle

            Args:

                input_size (int) : Input size of image eg; (512x512x3)
                numClasses (int) : Number of type of objects being detected
                weightsPath (str) : Path to saved model

        """
        self.input_size = input_size
        self.num_classes =len(utils.read_class_names(cfg.YOLO.CLASSES))
        self.score_threshold  = cfg.TEST.SCORE_THRESHOLD
        self.iou_threshold    = cfg.TEST.IOU_THRESHOLD
        self.weightsPath = weightsPath
        self.predictorFn=self.__hit_model()
        print("Exit Yolo constructor")

    def __hit_model(self):
        """This function tests the tf model and returns the predict function handler
        """
        #temp_inp_yolo = cv.imread("C:\\Users\\developer\\Anaconda_3\\rdt-reader\\1.jpg")
        temp_inp_yolo = cv.imread(jpg1Path)
        org_image = np.copy(temp_inp_yolo)
        org_h, org_w, _ = org_image.shape

        image_data = utils.image_preporcess(temp_inp_yolo, [self.input_size, self.input_size])
        image_data = image_data[np.newaxis, ...]

        try:
            temp_inp_yolo = np.array(image_data,dtype=np.float32)
            predict_fn = tf.contrib.predictor.from_saved_model(self.weightsPath)
            output_data = predict_fn({"input": temp_inp_yolo})
            return predict_fn
        except IOError:
            print("Unable to read either array or weight path")
            output_data = predict_fn({"input": temp_inp_yolo})
            return predict_fn

    def wrapper(self, image):
        """Wrapper method for the whole service for the object Detection service

            Args:

                image (numpy.ndarray) : BGR image

            Returns:

                list: Bounding boxes per category of class detected with confidence score
        """
        org_image = np.copy(image)
        org_h, org_w, _ = org_image.shape

        image_data = utils.image_preporcess(image, [self.input_size, self.input_size])
        image_data = image_data[np.newaxis, ...]

        start_grpc = dt.utcnow()

        result = self.predictorFn({"input":image_data})

        end_grpc = dt.utcnow()

        self.grpc_delta = end_grpc - start_grpc

        pred_sbbox = result["pred_sbbox"]
        pred_mbbox = result["pred_mbbox"]
        pred_lbbox = result["pred_lbbox"]
        # print("af pred",self.num_classes)
        pred_bbox = np.concatenate([np.reshape(pred_sbbox, (-1, 5 + self.num_classes)),
                                    np.reshape(pred_mbbox, (-1, 5 + self.num_classes)),
                                    np.reshape(pred_lbbox, (-1, 5 + self.num_classes))], axis=0)
        bboxes = utils.postprocess_boxes(pred_bbox, (org_h, org_w), self.input_size, self.score_threshold)
        # bboxes = utils.nms(bboxes, self.iou_threshold)
        _image = utils.draw_bbox(org_image, bboxes, show_label=True)
        cv.imwrite("yolopred.jpg", _image)

        return bboxes

class LineDetector:
    def __init__(self, input_size=[500,100],numClasses=4,weightsPath=FLU_AUDERE_LINE_PATH):
        """This function initializes the Line detector model and warms it up and returns predictor function handle

            Args:

                input_size (list) : Input size of image eg; [500,100] (500x100x3)
                numClasses (int) : Number of type of objects being detected
                weightsPath (str) : Path to saved model

        """
        self.input_size = input_size
        self.weightsPath = weightsPath
        self.num_classes = numClasses
        self.predictorFn=self.__hit_model()

    def renormalize(self,n, range1, range2):
        delta1 = range1[1] - range1[0]
        delta2 = range2[1] - range2[0]
        return (delta2 * (n - range1[0]) / delta1) + range2[0]

    def returnLOGker(self):
        sigma=48
        scaleConst1=1/(np.pi*(sigma**4))
        scaleConst2=1.0/(2*sigma**2)
        sizeofKernel=200
        sizeofKernel_2=int(sizeofKernel/2)
        LOG=np.zeros((sizeofKernel,sizeofKernel))
        for rows in range(sizeofKernel):
            i=rows-sizeofKernel_2
            for cols in range(sizeofKernel):
                j=cols-sizeofKernel_2
                LOG[rows,cols] = scaleConst1*(1-(i**2+j**2)*scaleConst2)*np.exp(-((i**2+j**2)*scaleConst2))
        LOG_oriented=np.zeros((int((sizeofKernel)/4),sizeofKernel))
        rowsubsample=0
        for rows in range(sizeofKernel):
            try:
                if rows%4==0:
                    for cols in range(sizeofKernel):
                        LOG_oriented[rowsubsample,cols]=LOG[rows,cols]
                    rowsubsample+=1
            except:
                pass
        LOG_oriented=LOG_oriented-np.mean(LOG_oriented)
        LOG_oriented=LOG_oriented*29000
        return LOG_oriented

    def LOG(self,im,kernel):
        img = im/255.0
        img = np.array(img,dtype=np.float32)
        imgYUV=cv.cvtColor(img,cv.COLOR_BGR2YUV)
        imgYUV[:,:,1:]=imgYUV[:,:,1:]-0.5
        filtered_img_GB=cv.filter2D(imgYUV , cv.CV_32F, kernel)*255
        return filtered_img_GB[:,:,1:]

    def gaborFilt(self,im):
        g_kernel = cv.getGaborKernel((9, 51), 6, np.pi/2, 0.2, 0.1, np.pi, ktype=cv.CV_32F)
        img = im/255.0
        img = np.array(img,dtype=np.float32)
        imgYUV=cv.cvtColor(img,cv.COLOR_BGR2YUV)

        filtered_img_GB = cv.filter2D(imgYUV*255, cv.CV_32F, g_kernel)
        renormalized=np.zeros((imgYUV.shape))
        # renormalized=renormalized[:,5:95,:]

        renormalized[:,:,0]=self.renormalize(filtered_img_GB[:,:,0],(np.min(filtered_img_GB[:,:,0]),np.max(filtered_img_GB[:,:,0])),(0,255))
        renormalized[:,:,1]=self.renormalize(filtered_img_GB[:,:,1],(np.min(filtered_img_GB[:,:,1]),np.max(filtered_img_GB[:,:,1])),(-128,127))
        renormalized[:,:,2]=self.renormalize(filtered_img_GB[:,:,2],(np.min(filtered_img_GB[:,:,2]),np.max(filtered_img_GB[:,:,2])),(-128,127))
        U_p=2.0
        V_p=13.0
        k1=U_p/V_p
        k2=1-k1
        newIMG=np.zeros((imgYUV.shape))
        # newIMG=newIMG[:,5:95,:]

        Final_U = k1*(renormalized[:,:,1]-renormalized[:,:,2])
        Final_V = k2*(renormalized[:,:,1]-renormalized[:,:,2])
        newIMG[:,:,1]=Final_U
        newIMG[:,:,2]=Final_V
        newIMG[:,:,0]=renormalized[:,:,0]

        newIMG[:,:,0]=self.renormalize(renormalized[:,:,0],(np.min(renormalized[:,:,0]),np.max(renormalized[:,:,0])),(0,255))
        newIMG[:,:,1]=self.renormalize(Final_U,(np.min(Final_U),np.max(Final_U)),(0,255))
        newIMG[:,:,2]=self.renormalize(Final_V,(np.min(Final_V),np.max(Final_V)),(0,255))
        im = newIMG[:,:,1:]
        return im

    def __hit_model(self):
        """This function tests the tf model and returns the predict function handler
        """

        #img = cv.imread("C:\\Users\\developer\\Anaconda_3\\rdt-reader\\2.jpg")
        img = cv.imread(jpg2Path)
        img=cv.resize(img,(100,2000))
        img = img[1500:1520,:,:]
        img = img/ 255.0
        img = np.array(img,dtype=np.float32)
        img = cv.cvtColor(img, cv.COLOR_BGR2YCrCb)
        if LINE_MODEL_VER==8:
            img = np.reshape(img[:,20:80,1:],(1,20,60,2))
        elif LINE_MODEL_VER>=6:
            img = np.reshape(img[:,:,1:],(1,20,100,2))
        else:
            img = np.reshape(img,(1,20,100,3))
        predict_fn = tf.contrib.predictor.from_saved_model(self.weightsPath)
        result = predict_fn({"input_image": img})
        return predict_fn

    def most_frequent(self,List):
        return max(set(List), key = List.count)

    def normalize(self,data):
        return (data - data.mean()) / data.std()

    def slidingMaxpool(self,truthLabels,windowSize,stride):

        noIterations = int(len(truthLabels)/windowSize + len(truthLabels)/stride)
        y_averaged=[]
        for ind in range(noIterations):
            start = ind*stride
            end = start +windowSize
            tmp_data = truthLabels[start:end]
            if(len(tmp_data)==windowSize):
                m_tmp = self.most_frequent(tmp_data)
                y_averaged.append(m_tmp)
        return y_averaged

    def shredImage(self,img):
        y=[]
#         print("input shape",img.shape)
        if img.shape == (2000,100,3):
            pass
        else:
            img = cv.resize(img,(100,2000))
#             print(img.shape)
        img_inp = img[1000:1500,:,:]
        img_no_sclae = np.copy(img_inp)

        if LINE_MODEL_VER==1:
            img_inp = img_inp/255.0
            img_inp = np.array(img_inp,dtype=np.float32)
            img_inp = cv.cvtColor(img_inp,cv.COLOR_BGR2RGB)
        elif LINE_MODEL_VER==4: #Model 4, 5 ,6 ,7 ,8
            img_inp = np.uint8(img_inp)
            img_inp = cv.cvtColor(img_inp,cv.COLOR_BGR2RGB)
            img_inp=gaussBlur(img_inp)
            img_inp= enhanceImage(img_inp)
            img_inp = img_inp/255.0
            img_inp = np.array(img_inp,dtype=np.float32)
        elif LINE_MODEL_VER==5: #Model 9 10 11 12
            print("MODEL TYPE ",LINE_MODEL_VER)
            img_inp = np.uint8(img_inp)
            img_inp = cv.cvtColor(img_inp,cv.COLOR_BGR2RGB)
            img_inp=gaussBlur(img_inp)
            img_inp = img_inp/255.0
            img_inp = np.array(img_inp,dtype=np.float32)
            img_inp = cv.cvtColor(img_inp,cv.COLOR_RGB2YCrCb)
        elif LINE_MODEL_VER==6: #Model 13
            img_inp= self.gaborFilt(img_inp)
            img_inp = img_inp/255.0
            img_inp = np.array(img_inp,dtype=np.float32)
        elif LINE_MODEL_VER==7:#Model 14 15
            LOGker = self.returnLOGker()
            img_inp = self.LOG(img_inp,LOGker)
            img_inp = img_inp/128.0
            img_inp = np.array(img_inp,dtype=np.float32)
        elif LINE_MODEL_VER==8:#Model 16 17
            img_inp=img_inp[:,20:80,:]
            LOGker = self.returnLOGker()
            img_inp =self.normalize(self.LOG(img_inp,LOGker))
            img_inp = img_inp/128.0
            img_inp = np.array(img_inp,dtype=np.float32)
        else:
            img_inp = img_inp/255.0
            img_inp = np.array(img_inp,dtype=np.float32)
            img_inp = cv.cvtColor(img_inp,cv.COLOR_BGR2YCrCb)

        for i in range(96):
            st = i*5
            end = st+20
            # print(end-st)
            shred_img = img_inp[st:end,:,:]
            shred_img_write = img_no_sclae[st:end,:,:]
            cv.imwrite("./shred/"+str(i)+".jpg",shred_img_write)
            shred_img = shred_img[np.newaxis]
            preds = self.predictorFn({"input_image": shred_img})
            preds=list(preds["predictions"][0])
            feat_class =preds.index(max(preds))
            if feat_class==0:
                y.append(0)
            elif feat_class==1:
                y.append(1)
            elif feat_class==2:
                y.append(2)
        print("sliding window predictions :",y)
        y_avg = self.slidingMaxpool(y,1,1)
        return y_avg

    def wrapper(self, image):
        """Wrapper method for the whole service for the Line Detection service

            Args:

                image (numpy.array) : BGR image

            Returns:

                list: Probability of detection
                list: Y-axis of predicted point
                numpy.ndarray: Image cropped
        """
        blue_detected = 0
        red_detected = 0
        virus_type = 0
        cntFromBlue=0
        cntFromRed=0
        predictions = self.shredImage(image)
        windowSize = 500/len(predictions)
        distanceR_B=[]
        for indexLoc,p in enumerate(predictions):
            if blue_detected==1:
                cntFromBlue+=1
            elif red_detected==1:
                cntFromRed+=1
            if p==2:
                pass
            elif p==1 and indexLoc<90:
                print("red",indexLoc)
                if red_detected==1 and blue_detected==0:
                        cntFromRed=0
                        virus_type=0
                elif blue_detected==0:
                    virus_type = 1
                    red_detected = 1
                else:
                    # print("here")
                    if red_detected==0 and cntFromBlue<20 and cntFromBlue>6:
                        virus_type = 2
                        red_detected=1
                        # if cntFromBlue>=20:
                        #     virus_type=0
                        #     cntFromBlue=0
                        #     blue_detected=0
                        #     red_detected=1
                        print("CENTER FROM BLUE",cntFromBlue)
                    elif cntFromBlue<20 and virus_type==1:
                        virus_type = 3

                image = cv.circle(image,(50,int(indexLoc*windowSize+1010)),5, (0,0,255), 5)
            elif p==0 and indexLoc>28 and indexLoc<65:
                print("blue",indexLoc)
                if red_detected==1:
                    if cntFromRed<15 and (cntFromRed>3 or cntFromRed==0) :
                        virus_type=1
                        print("COUNT FROM RED",cntFromRed)
                    else:
                        virus_type=0
                        red_detected=0

                    cntFromRed=0
                blue_detected=1
                image = cv.circle(image,(50,int(indexLoc*windowSize+1010)),5, (255,0,0), 5)
        return [image,virus_type,blue_detected]


import glob
import os.path
from nikwus import sprite

def test_sprites():
    for directory in glob.glob(os.path.dirname(__file__) + '/*'):
        if os.path.isdir(directory + '/css'):
            yield check_sprites_dir, directory + '/css', directory + '/img'
        elif os.path.isdir(directory):
            yield check_sprites_dir, directory, directory

def check_sprites_dir(css_directory, sprite_directory):
    """Run tests on a single test directory."""
    input_file = os.path.join(css_directory, 'test.css')
    out_file = os.path.join(css_directory, 'test-out.css')
    expected_file = os.path.join(css_directory, 'test-expected.css')
    res = sprite(sprite_directory, input_file, out_file, offset=5)
    assert res, res
    assert os.path.exists(out_file)
    assert_same_contents(out_file, expected_file)

def assert_same_contents(file1, file2):
    """Assert that two files have the same content."""
    line_no = 0
    with open(file1) as f1:
        with open(file2) as f2:
            lines1 = f1.readlines()
            lines2 = f2.readlines()
            while lines1:
                if not lines2:
                    assert False, 'file2 contains more lines than file1 at line {0}'.format(line_no)
                line1 = lines1.pop(0).strip()
                line2 = lines2.pop(0).strip()
                assert line1 == line2, '{0!r} != {1!r} at line {2}'.format(line1, line2, line_no)
                line_no += 1
            if lines2:
                assert False, 'file1 contains more lines than file2 at line {0}'.format(line_no)


import logging
import asyncio
import sys
import cozmo
from cozmo.util import degrees, distance_mm, speed_mmps

from random import randint

from flask import Flask, render_template

from flask_ask import Ask, statement, question, session

app = Flask(__name__)

ask = Ask(app, "/")

logging.getLogger("flask_ask").setLevel(logging.DEBUG)

@ask.launch

def new_game():

    welcome_msg = render_template('welcome')

    return question(welcome_msg)

@ask.intent("YesIntent")

def next_round():

    round_msg = render_template('round')

    return question(round_msg)

@ask.intent("ForwardIntent")

def driveForward(first):

	distance = int(first[0])
	if distance > 5 :
		 distance = 5

	if distance < 1:
		msg = render_template('none')
	else :
		voice_control_cozmo.do_drive(distance)
		msg = render_template('forward')
	return question(msg)

@ask.intent("BackwardIntent")

def driveBackward(first):

	distance = int(first[0])
	if distance > 5 :
		 distance = 5

	if distance < 1:
		msg = render_template('none')
	else :
		voice_control_cozmo.do_drive(-distance)
		msg = render_template('backward')
	return question(msg)

@ask.intent("TurnRightIntent")

def turnRight(first):

	degrees = int(first)
	if degrees > 360 :
		 degrees = 359

	if degrees < 1:
		msg = render_template('none')
	else :
		voice_control_cozmo.do_turn(-degrees)
		msg = render_template('right')
	return question(msg)

@ask.intent("TurnLeftIntent")

def turnLeft(first):

	degrees = int(first)
	if degrees > 360 :
		 degrees = 359

	if degrees < 1:
		msg = render_template('none')
	else :
		voice_control_cozmo.do_turn(degrees)
		msg = render_template('left')
	return question(msg)

@ask.intent("LiftUpIntent")

def	liftUp():
	voice_control_cozmo.do_lift(1)
	msg = render_template('lift')
	return question(msg)

@ask.intent("LiftDownIntent")

def	liftDown():
	voice_control_cozmo.do_lift(-1)
	msg = render_template('lift')
	return question(msg)

class VoiceControlCozmo:
	def __init__(self, coz):

		self.cozmo = coz
		self.cozmo.say_text("READY.").wait_for_completed()

	def do_lift(self, cmd_args):
		self.cozmo.move_lift(cmd_args)

	def do_drive(self, cmd_args):
		#"""drive X"""
		usage = "'drive X' where X is number of seconds to drive for"
		drive_duration = float(cmd_args)
		if drive_duration > 10.0:
			drive_duration = 10.0

		if drive_duration < -10.0:
			drive_duration = -10.0

		if drive_duration is not None:
			drive_speed = 50
			drive_dir = "forwards"
			if drive_duration < 0:
				drive_speed = -drive_speed
				drive_duration = -drive_duration
				drive_dir = "backwards"
			self.cozmo.drive_wheels(drive_speed, drive_speed, duration=drive_duration)
			print ('I drove ' + drive_dir + ' for ' + str(drive_duration) + ' seconds!')
		return

	def do_turn(self, cmd_args):
	    usage = "'turn X' where X is a number of degrees to turn"
	    drive_angle = float(cmd_args)
	    if drive_angle > 360:
	    	drive_angle = 0

	    if drive_angle is not None:
	    	self.cozmo.turn_in_place(degrees(drive_angle)).wait_for_completed()
	    	print ("I turned " + str(drive_angle) + " degrees!")

	    return "Error: usage = " + usage

	def do_cube(self):
		cube = None
		look_around = self.cozmo.start_behavior(cozmo.behavior.BehaviorTypes.LookAroundInPlace)
		print("looking for cube")
		try:
			cube = self.cozmo.world.wait_for_observed_light_cube(timeout=60)
		except asyncio.TimeoutError:
			print("Didn't find a cube :-(")
			return
		finally:
			look_around.stop()

def run(sdk_conn):
	robot = sdk_conn.wait_for_robot()

	global voice_control_cozmo
	voice_control_cozmo = VoiceControlCozmo(robot)
	app.run(debug=True)

#	for _ in range(4):
#		robot.drive_straight(distance_mm(150), speed_mmps(50)).wait_for_completed()
#		robot.turn_in_place(degrees(90)).wait_for_completed()

if __name__ == '__main__':
    cozmo.setup_basic_logging()
    try:
        cozmo.connect(run)
    except cozmo.ConnectionError as e:
        sys.exit("A connection error occurred: %s" % e)


from matplotlib.pyplot import *
from numpy import *
from scipy import integrate
from scipy.integrate import odeint
from sympy import *
from scipy import linalg

pool = genfromtxt('test.txt',dtype='str')
rePool = pool
rePoolCount = 3

WR = 5
IR = 21
TR = 3
CP = ''
CC = ['' for x in range(WR)]
VC = ['' for x in range(WR)]
IC = ['' for x in range(IR)]
attempts = [[0 for x in range(TR)] for y in range(TR)]

def checkForCorrectChar(guess, sol):
    for i in range(WR):
        if guess[i] == sol[i]:
            CC[i] = guess[i]
        else:
            CC[i] = 0

def checkForValidChar(guess, sol):
    mark = 0
    for i in range(WR):
        for j in range(WR):
            if guess[i] == sol[j]:
                VC[mark] = guess[i]
                mark+=1

def checkForInvalChar(guess, sol):
    print('?')

def removeCand(rePool):
    print('?')

def resize(guess, sol, rePool):
    checkForCorrectChar(guess, sol)
    checkForValidChar(guess, sol)
    checkForInvalChar(guess, sol)

    removeCand(rePool)

def pick(rePool):
    print('?')

def main():
    for i in range(TR):
        for j in range(TR):
            rePool = pool
            guess = pool[i]
            sol = pool[j]

            if guess == sol:
                print("done")
                attempts[i][j] = 1
            else:
                resize(guess, sol, rePool)
                if pick(rePool) == sol:
                    print("done")
                    attempts[i][j] = 2
                else:
                    resize(guess, sol, rePool)
                    if pick(rePool) == sol:
                        print("done")
                        attempts[i][j] = 3
                    else:
                        resize(guess, sol, rePool)
                        if pick(rePool) == sol:
                            print("done")
                            attempts[i][j] = 4
                        else:
                            resize(guess, sol, rePool)
                            if pick(rePool) == sol:
                                print("done")
                                attempts[i][j] = 5
                            else:
                                resize(guess, sol, rePool)
                                if pick(rePool) == sol:
                                    print("done")
                                    attempts[i][j] = 6
    print(attempts)


class Solution(object):
    def uniqueMorseRepresentations(self, words):
        """
        :type words: List[str]
        :rtype: int
        """
        morse = [".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--.."]
        alphabets = "abcdefghijklmnopqrstuvwxyz"
        dict_morse = {}
        for i in range(len(morse)):
            dict_morse[alphabets[i]] = morse[i]

        final_list = []

        for word in words:
            temp_str = ""
            for letter in word:
                temp_str += dict_morse[letter]
            final_list.append(temp_str)

        return len(set(final_list))

#https://leetcode.com/problems/unique-morse-code-words/