import numpy as np
import torch
from torch import nn
import torch.nn.functional as F
from gym_loop.agents.layers.noisy_layer import NoisyLinear
from .base_policy import BasePolicy

class CategoricalNet(BasePolicy, nn.Module):
    def __init__(
        self, in_dim: int, out_dim: int, atom_size: int, support: torch.Tensor
    ):
        """Initialization."""
        nn.Module.__init__(self)

        self.support = support
        self.out_dim = out_dim
        self.atom_size = atom_size

        # set common feature layer
        self.feature_layer = nn.Sequential(nn.Linear(in_dim, 128), nn.ReLU())

        # set advantage layer
        self.advantage_hidden_layer = NoisyLinear(128, 128)
        self.advantage_layer = NoisyLinear(128, out_dim * atom_size)

        # set value layer
        self.value_hidden_layer = NoisyLinear(128, 128)
        self.value_layer = NoisyLinear(128, atom_size)

    def forward(self, x: np.ndarray) -> torch.Tensor:
        """Forward method implementation."""
        x = torch.FloatTensor(x)
        dist = self.distribution(x)
        return {"q_values": torch.sum(dist * self.support, dim=2)}

    def distribution(self, x: torch.Tensor) -> torch.Tensor:
        """Get distribution for atoms."""
        feature = self.feature_layer(x)
        adv_hid = F.relu(self.advantage_hidden_layer(feature))
        val_hid = F.relu(self.value_hidden_layer(feature))

        advantage = self.advantage_layer(adv_hid).view(-1, self.out_dim, self.atom_size)
        value = self.value_layer(val_hid).view(-1, 1, self.atom_size)
        q_atoms = value + advantage - advantage.mean(dim=1, keepdim=True)

        dist = F.softmax(q_atoms, dim=-1)
        dist = dist.clamp(min=1e-3)  # for avoiding nans

        return dist

    def __call__(self, *args):
        nn.Module.__call__(self, *args)

    def reset_noise(self):
        for module in self.value_layer.modules():
            if hasattr(module, "reset_noise"):
                module.reset_noise()
        for module in self.advantage_layer.modules():
            if hasattr(module, "reset_noise"):
                module.reset_noise()


"""
bunch of helpful functions
CK 2014
"""
import numpy as np
from matplotlib import pyplot as plt
from sys import stdout
import hcPlotting

def slug(s):
    replacements = [('.','_'), (',','_'), (' ','_'), ('^',''), ('$',''), ('{', ''), ('}', ''), ('=', '_')]
    for a,b in replacements:
        s = s.replace(a,b)
    return s

def printprogress(msg,n,of):
    stdout.write("\r\033[2K%s %d of %d (%d%%)" %(msg,n,of,n*100/of))
    stdout.flush()

def centerdot(M,N):
    inputc = np.zeros((M,N))
    inputc[M/2-1,N/2-1] = 1
    inputc[M/2,N/2-1] = 1
    inputc[M/2-1,N/2] = 1
    inputc[M/2,N/2] = 1
    #inputc = inputc*2-1
    return M,N,inputc

def horzline(M,N):
    inputc = np.zeros((M,N))
    for j in range(N):
        inputc[M/2-1,j] = 1
        #inputc[M/2,j] = 1
    #inputc = inputc*2-1
    return M,N,inputc

def cornerdots(M,N,where,margin=2):
    inputc = np.zeros((M,N))
    sz = 2 # size of dot
    for w in where:
        pos = None
        if w == "tl":
            pos = (margin,margin)
        if w == "tr":
            pos = (margin,N-margin-sz)
        if w == "bl":
            pos = (M-margin-sz,margin)
        if w == "br":
            pos = (M-margin-sz,N-margin-sz)

        inputc[pos[0],   pos[1]  ] = 1
        inputc[pos[0]+1, pos[1]  ] = 1
        inputc[pos[0],   pos[1]+1] = 1
        inputc[pos[0]+1, pos[1]+1] = 1
    return M,N,inputc

def idxtoimg(M,N,nodelist):
    img = np.zeros((M,N))
    coords = np.array(zip(*nodelist))
    img[coords[0],coords[1]] = 1
    return img

class naivebayes:
    """
    repeated gaussian naive bayes for growing window increments.

    assumes in each condition & at each window increment step, measured values from different
    trials i are iid following a normal distribution: N(x_i|mu_(class,step) , var_(class,step)).
    this distribution is first estimated by MLE, so for each step & condition, its parameters
    are the sample mean and variance of (a training subset of) the measured values from that condition
    at that step. This happens at initialisation.
    These estimated densities are used as likelihood functions for each window step and for
    each class, which yields posterior probabilities of each condition, at each step & for each trial.

    """
    def __init__(self,experiments,measurename,train_trials=None,plotmodels=False):
        """
        Args:
            experiments: List of two experiments - each experiment treated as one class.
            measurename: Name of the growing window measure from which to get values.
            train_trials: List of indices of trials to use to estimate parameters.
                          If None, use all trials.
            plotmodels: Plot the likelihood function for each class.
        """
        self.experiments = experiments
        self.measurename = measurename
        self.classes = [e.name for e in experiments]
        assert len(set(self.classes)) == len(self.classes) # experiments should have different names
        assert len(self.classes) == 2 # plotting code down below can't deal with multiple classes.
        self.means = {}
        self.varcs = {}
        self.likelihoods = {}
        self.model = {}
        self.normpdf = lambda x,m,v: np.exp(-((x-m)**2)/(2.0*v)) / np.sqrt(2*np.pi*v)
        # MLE estimation:
        for e in experiments:
            C = e.name
            results = np.array(e.getresults(measurename))
            # self.measurements[C] = results # assumed to be of shape trials x time
            if not train_trials:
                train_trials = range(results.shape[0])
            self.means[C] = np.mean(results[train_trials,:],axis=0) # the sample mean at each step
            self.varcs[C] =  np.var(results[train_trials,:],axis=0) # the sample var at each step

            if plotmodels:
                self.model[C] = np.zeros((400,results.shape[1]))
                for step in range(results.shape[1]):
                    self.model[C][:,step] = self.normpdf(np.arange(0, 1, 0.0025),
                                                         self.means[C][step], self.varcs[C][step])
                    plt.figure()
                    plt.imshow(model[C],interpolation='nearest',origin='lower',aspect='auto',cmap='bone')
                    plt.colorbar()
                    plt.title(C)

    def posteriors(self,experiment):
        """
        For each trial in the given experiment, return the posterior probabilities that it
        belongs to each of the conditions used in training the classifier.

        The likelihood that the result in trial i at time t was generated by experiment C is
        L(r_(i,t)|C,t) = N(r_(i,t)|mu_C(t),var_C(t))

        Thus the posterior probability of class C given the result in trial i at time t is
        P(C|r_(i,t),t) = L(r_(i,t)|C,t)*P(C) / sum_classes(L(r_(i,t)|c_i,t)P(c_i))

        Arg:
            experiment object

        Returns:
            dict p, whose keys C are all the classes used in training the classifier,
            and where p[C] is a (trials x steps) array of probabilities that at step s,
            trial i of the given experiment belong to class C.
        """
        # so the likelihood that the result in trial i at time t was generated by experiment C is
        # L(r_(i,t)|C,t) = normpdf(r_(i,t),mean_C(t),var_C(t))
        # So we evaluate a probability _density_ at particular _points_... permissible, since a
        # constant scaling factor of L disappears in the normalization involved in calculating posteriors.
        results = np.array(experiment.getresults(self.measurename))
        likelihoods = {}
        for C in self.classes:
            likelihoods[C] = np.zeros(results.shape)
            for step in range(results.shape[1]):
                likelihoods[C][:,step] = self.normpdf(results[:, step],
                                                      self.means[C][step], self.varcs[C][step])
        # Thus the posterior probability of class C given the result in trial i at time t is
        # P(C|r_(i,t),t) = L(r_(i,t)|C,t)*P(C) / sum_classes(L(r_(i,t)|c_i,t)P(c_i))
        posteriors = {}
        prior_c = 1.0/len(self.classes) # assume flat prior over classes
        for C in self.classes:
            posteriors[C] = np.zeros(results.shape)
            for step in range(results.shape[1]):
                normalize = np.sum([likelihoods[ci][:,step] * prior_c for ci in self.classes], axis=0)
                posteriors[C][:,step] = likelihoods[C][:,step] * prior_c / normalize
        return posteriors

    def plot_posteriors(self,C=None):
        if not C:
            C = self.classes[0]
        postrs = [self.posteriors(e)[C] for e in self.experiments]
        plt.figure(figsize=hcPlotting.fig_size)
        plt.imshow(np.vstack(postrs),cmap='RdBu',interpolation='nearest',aspect='auto')
        plt.xticks([],[])
        plt.xlabel("simulation time step")
        plt.ylabel("trial")
        plt.colorbar()
        plt.title("posterior of class "+C+" for trials from classes "+", ".join(self.classes))

    def classification_MAP(self):
        """
        Classify each trial from each experiment at each time, by argmax_C P(C|r_(i,t),t).

        Returns:
            dict of arrays
        """
        classification = {}
        for e in self.experiments:
            pstrs = np.dstack([self.posteriors(e)[c] for c in self.classes])
            classification[e.name] = np.array(self.classes)[np.argmax(pstrs,axis=2)]
        return classification

    def evaluation(self,C=None,doplot=True):
        """ Return the evolution of true classifications as C vs. false classifications as C """
        assert len(self.classes) == 2
        if not C:
            C = self.classes[0]
        assert C in self.classes
        clss = self.classification_MAP()
        for e in self.experiments:
            if e.name == C:
                tp = clss[e.name] == C
            else:
                fp = clss[e.name] == C

        if doplot:
            plt.figure(figsize=hcPlotting.fig_size)
            plt.plot(np.mean(tp, axis=0),color='g',label='TP')
            plt.plot(np.mean(fp,axis=0),color='r',label='FP')
            plt.xlabel("time")
            #plt.legend(loc=5)
            plt.title("correct/incorrect classification")
        return tp,fp

    def time_to_correct_decision(self,experiment=None,thr=0.5,stay_above=True,unit="spikes",spikemeasure="growing_spikecount"):
        """ time until posterior for the correct class either stays above threshold or first crosses it"""
        assert len(self.classes) == 2
        if not experiment:
            experiment = self.experiments[0]
        pstrs = self.posteriors(experiment)[experiment.name] # posteriors for the experiment's true class
        nr_trials,nr_steps = pstrs.shape

        nr_spikes_fired = experiment.getresults(spikemeasure)
        assert len(nr_spikes_fired) == nr_trials
        assert np.all([len(nr_spikes_fired[i]) == nr_steps for i in range(nr_trials)])

        # either go backwards and take note when posterior drops below threshold,
        # (..and if it never drops below threshold, note a decision at the first window)
        # or go forwards and take note when it goes above.
        decisiontimes = np.empty(nr_trials)
        decisiontimes[:] = np.Inf
        if stay_above:
            for tr in range(nr_trials):
                if pstrs[tr,nr_steps-1]<thr:
                    # no sense in looking when it drops below threshold when it never reaches it
                    pass
                else:
                    for s in range(nr_steps-1,-1,-1):
                        if s == 0 or (pstrs[tr,s-1] < thr):
                            decisiontimes[tr] = nr_spikes_fired[tr][s] if unit=="spikes" else s*unit
                            break
        else:
            for tr in range(nr_trials):
                for s in range(nr_steps):
                    if pstrs[tr,s] > thr:
                        decisiontimes[tr] = nr_spikes_fired[tr][s] if unit=="spikes" else s*unit
                        break
        return decisiontimes

    def plot_all_times_to_correct_decision(self,thr=0.5,stay_above=True,unit="spikes",spikemeasure="growing_spikecount", do_title=True):
        times = np.array([self.time_to_correct_decision(e,thr,stay_above,unit,spikemeasure) for e in self.experiments]).flatten()
        # times[30:50] = np.Inf
        maximum = int(np.ceil(max(times[times!=np.Inf])))
        plt_inf = maximum+2 # for unsuccessful trials (time=Inf), set time to some value distinct from any actual decision time.
        times[times==np.Inf] = plt_inf

        fig = plt.figure(figsize=(hcPlotting.fig_width,hcPlotting.fig_height/3))

        bins = np.hstack([np.arange(0.25,maximum+1,0.5),[plt_inf,plt_inf+1]])
        n,_,_ = plt.hist(times,bins,color='k',edgecolor='w')

        ax = plt.gca()
        ax.set_xlim((0,plt_inf+1))
        ax.set_ylim(ax.get_ylim()[0],ax.get_ylim()[1]+1)
        plt.plot((plt_inf,plt_inf),(0,ax.get_ylim()[1]),'r')

        ax.set_xticks(range(maximum+1)+[plt_inf+0.5])
        ax.set_xticklabels([str(i) for i in range(maximum+1)]+[r'$\infty$'])

        ax.set_ylabel("nr. of trials")
        ax.set_xlabel("spikes observed before classification")
        if do_title:
            plt.title("thr = "+str(thr)+", stay_above = "+str(stay_above)+", classes: " +" vs. ".join(self.classes))


from django.conf.urls import url
from django.views.generic.edit import CreateView
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import views as auth_views

from . import views

urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^favicon\.ico$', views.favico, name='favico'),
    url(r'^authors/(?P<letter>[a-zA-Z]+)/$', views.authors_index, name='authors'),
    url(r'^publications/$', views.pub_index, name='publications'),
    url(r'^publications/new$', views.pub_add, name='publication_add'),
    url(r'^authors/(?P<author_id>[0-9]+)/$', views.author_detail, name='author_detail'),
    url(r'^publications/(?P<pub_id>[0-9]+)/$', views.publication_detail, name='publication_detail'),
    url(r'^register/$', views.register, name='register'),
    url(r'^author_create/$', views.author_create, name='author_create'),
    url(r'^pub_create/$', views.pub_create, name='pub_create'),
    url(r'^login/$', auth_views.login, {'template_name':'pubman/login.html'}),
]


import maya.cmds as cmds
from functools import partial

#quick UI
def ipMoverUI():
    if (cmds.window('zbw_ipMoverUI', exists=True)):
        cmds.deleteUI('zbw_ipMoverUI', window=True)
        cmds.windowPref('zbw_ipMoverUI', remove=True)
    window=cmds.window('zbw_ipMoverUI', widthHeight=(400,200), title='zbw_imagePlane')
    cmds.columnLayout('zbw_ipmMainColumn')

    cam = cmds.ls(sl=True,dag=True,s=True, type="camera")
    if cam:
        ip = cmds.listConnections(cam[0], type="imagePlane")
        if ip:
            imagePlane = ip[0]
            ipText = imagePlane
        else:
            cmds.warning("there's no image plane on this camera")
            ipText = "no image plane"
    else:
        cmds.warning("select a camera")
        ipText = "no image plane"

    cmds.text(ipText)
    cmds.separator(h=20, style="double")

    if cam and ip:

        #---------------store original values somewhere . . .

        cmds.floatSliderGrp("alphaFSG", label='alpha',cal = [1,"left"], cw3=[50, 50, 300],width=400, field=True, minValue=0, maxValue=1, fieldMinValue=0, fieldMaxValue=1, value=1, pre=3, dc=partial(changeValue, (imagePlane + ".alphaGain"), "alphaFSG"))

        cmds.separator(h=20, style = "double")

        cmds.floatSliderGrp("centerXFSG", label='centerX',cal = [1,"left"], cw3=[50, 50, 300],width=400, field=True, minValue=-20.0, maxValue=20.0, fieldMinValue=-100.0, fieldMaxValue=100.0, value=0, pre=3, dc=partial(changeValue, (imagePlane + ".centerX"), "centerXFSG"))

        cmds.floatSliderGrp("centerYFSG", label='centerY',cal = [1,"left"], cw3=[50, 50, 300],width=400, field=True, minValue=-20.0, maxValue=20.0, fieldMinValue=-100.0, fieldMaxValue=100.0, value=0, pre=3, dc=partial(changeValue, (imagePlane + ".centerY"), "centerYFSG"))

        cmds.floatSliderGrp("centerZFSG", label='centerZ',cal = [1,"left"], cw3=[50, 50, 300],width=400, field=True, minValue=-20.0, maxValue=20.0, fieldMinValue=-100.0, fieldMaxValue=100.0, value=0, pre=3, dc=partial(changeValue, (imagePlane + ".centerZ"), "centerZFSG"))

        cmds.floatSliderGrp("widthFSG", label='width',cal = [1,"left"], cw3=[50, 50, 300],width=400, field=True, minValue=0, maxValue=20.0, fieldMinValue=0, fieldMaxValue=100.0, pre=3, dc=partial(changeValue, (imagePlane + ".width"), "widthFSG"))

        cmds.floatSliderGrp("heightFSG", label='height',cal = [1,"left"], cw3=[50, 50, 300],width=400, field=True, minValue=0, maxValue=20.0, fieldMinValue=0, fieldMaxValue=100.0, pre=3, dc=partial(changeValue, (imagePlane + ".height"), "heightFSG"))

    cmds.showWindow(window)

    cmds.window('zbw_ipMoverUI', e=True, w=400, h=200)

def changeValue(attr, slider, *args):

    value = cmds.floatSliderGrp(slider, q=True, v=True)
    cmds.setAttr(attr, value)

# def focusToCamera():

    #get camera from focus window
    # panel = cmds.getPanel(wf=True)
    # cam = cmds.modelEditor(panel, q=True, camera=True)

#----------get camera from focus . . .
#----------have camera list to select from . . .

# (coverageX)
# (coverageY)

def zbw_ipMover():
    ipMoverUI()


import graphene
from django.db.models import Q
from graphene import relay
from graphene_django.filter import DjangoFilterConnectionField

from ...product import models
from ...product.templatetags.product_images import product_first_image
from ...product.utils import get_availability
from ..core.filters import DistinctFilterSet
from ..core.types import (
    CountableDjangoObjectType, Money, TaxedMoney, TaxedMoneyRange)
from ..utils import get_node
from .filters import ProductFilterSet

class SelectedAttribute(graphene.ObjectType):
    name = graphene.String(
        default_value=None,
        description='Name of an attribute')
    value = graphene.String(
        default_value=None,
        description='Value of an attribute.')

    class Meta:
        description = 'Represents a custom product attribute.'

class ProductVariant(CountableDjangoObjectType):
    stock_quantity = graphene.Int(
        required=True,
        description='Quantity of a product available for sale.')
    price_override = graphene.Field(
        Money,
        description="""Override the base price of a product if necessary.
        A value of `null` indicates the the default product price is used.""")
    attributes = graphene.List(
        SelectedAttribute,
        description='List of attributes assigned to this variant.')

    class Meta:
        description = """Represents a version of a product such as different
        size or color."""
        interfaces = [relay.Node]
        model = models.ProductVariant

    def resolve_stock_quantity(self, info):
        return self.get_stock_quantity()

    def resolve_attributes(self, info):
        return resolve_attribute_list(self.attributes)

class ProductAvailability(graphene.ObjectType):
    available = graphene.Boolean()
    on_sale = graphene.Boolean()
    discount = graphene.Field(TaxedMoney)
    discount_local_currency = graphene.Field(TaxedMoney)
    price_range = graphene.Field(TaxedMoneyRange)
    price_range_undiscounted = graphene.Field(TaxedMoneyRange)
    price_range_local_currency = graphene.Field(TaxedMoneyRange)

    class Meta:
        description = 'Represents availability of a product in the storefront.'

class Product(CountableDjangoObjectType):
    url = graphene.String(
        description='The storefront URL for the product.',
        required=True)
    thumbnail_url = graphene.String(
        description='The URL of a main thumbnail for a product.',
        size=graphene.Argument(
            graphene.String,
            description='Size of a thumbnail, for example 255x255.'))
    availability = graphene.Field(
        ProductAvailability,
        description="""Informs about product's availability in the storefront,
        current price and discounts.""")
    price = graphene.Field(
        Money,
        description="""The product's base price (without any discounts
        applied).""")
    attributes = graphene.List(
        SelectedAttribute,
        description='List of product attributes assigned to this product.')

    class Meta:
        description = """Represents an individual item for sale in the
        storefront."""
        interfaces = [relay.Node]
        model = models.Product

    def resolve_thumbnail_url(self, info, *, size=None):
        if not size:
            size = '255x255'
        return product_first_image(self, size)

    def resolve_url(self, info):
        return self.get_absolute_url()

    def resolve_availability(self, info):
        context = info.context
        availability = get_availability(
            self, context.discounts, context.currency)
        return ProductAvailability(**availability._asdict())

    def resolve_attributes(self, info):
        return resolve_attribute_list(self.attributes)

class ProductType(CountableDjangoObjectType):
    products = DjangoFilterConnectionField(
        Product, filterset_class=ProductFilterSet,
        description='List of products of this type.')

    class Meta:
        description = """Represents a type of product. It defines what
        attributes are available to products of this type."""
        interfaces = [relay.Node]
        model = models.ProductType

class Category(CountableDjangoObjectType):
    products = DjangoFilterConnectionField(
        Product, filterset_class=ProductFilterSet,
        description='List of products in the category.')
    url = graphene.String(
        description='The storefront\'s URL for the category.')
    ancestors = DjangoFilterConnectionField(
        lambda: Category, filterset_class=DistinctFilterSet,
        description='List of ancestors of the category.')
    children = DjangoFilterConnectionField(
        lambda: Category, filterset_class=DistinctFilterSet,
        description='List of children of the category.')

    class Meta:
        description = """Represents a single category of products. Categories
        allow to organize products in a tree-hierarchies which can be used for
        navigation in the storefront."""
        exclude_fields = ['lft', 'rght', 'tree_id']
        interfaces = [relay.Node]
        filter_fields = ['id', 'name']
        model = models.Category

    def resolve_ancestors(self, info, **kwargs):
        return self.get_ancestors().distinct()

    def resolve_children(self, info, **kwargs):
        return self.children.distinct()

    def resolve_url(self, info):
        ancestors = self.get_ancestors().distinct()
        return self.get_absolute_url(ancestors)

    def resolve_products(self, info, **kwargs):
        qs = models.Product.objects.available_products()
        qs = qs.filter(category=self)
        return qs.distinct()

class ProductImage(CountableDjangoObjectType):
    url = graphene.String(
        required=True, description='',
        size=graphene.String(
            description='Size of an image, for example 255x255.'))

    class Meta:
        description = 'Represents a product image.'
        exclude_fields = ['product', 'productvariant_set', 'variant_images']
        interfaces = [relay.Node]
        model = models.ProductImage

    def resolve_url(self, info, *, size=None):
        if size:
            return self.image.crop[size].url
        return self.image.url

class ProductAttributeValue(CountableDjangoObjectType):
    class Meta:
        description = 'Represents a value of an attribute.'
        exclude_fields = ['attribute']
        interfaces = [relay.Node]
        model = models.AttributeChoiceValue

class ProductAttribute(CountableDjangoObjectType):
    values = graphene.List(
        ProductAttributeValue, description='List of attribute\'s values.')

    class Meta:
        description = """Custom attribute of a product. Attributes can be
        dynamically assigned to products and variants at the product type
        level."""
        exclude_fields = ['product_types', 'product_variant_types']
        interfaces = [relay.Node]
        filter_fields = ['id', 'slug']
        model = models.ProductAttribute

    def resolve_values(self, info):
        return self.values.all()

def resolve_attribute_list(attributes=None):
    """
    :param attributes: dict
    :return: list of objects of type SelectedAttribute
    """
    attribute_list = []
    if attributes:
        product_attributes = dict(
            models.ProductAttribute.objects.values_list('id', 'slug'))
        attribute_values = dict(
            models.AttributeChoiceValue.objects.values_list('id', 'slug'))
        for k, v in attributes.items():
            value = None
            name = product_attributes.get(int(k))
            if v:
                value = attribute_values.get(int(v))
            attribute_list.append(SelectedAttribute(name=name, value=value))
    return attribute_list

def resolve_categories(info, level=None):
    qs = models.Category.objects.all()
    if level is not None:
        qs = qs.filter(level=level)
    return qs.distinct()

def resolve_products(info):
    return models.Product.objects.available_products().distinct()

def resolve_attributes(category_id, info):
    queryset = models.ProductAttribute.objects.prefetch_related('values')
    if category_id:
        # Get attributes that are used with product types
        # within the given category.
        category = get_node(info, category_id, only_type=Category)
        if category is None:
            return queryset.none()
        tree = category.get_descendants(include_self=True)
        product_types = {
            obj[0]
            for obj in models.Product.objects.filter(
                category__in=tree).values_list('product_type_id')}
        queryset = queryset.filter(
            Q(product_types__in=product_types)
            | Q(product_variant_types__in=product_types))
    return queryset.distinct()


import sys

import cv2

# For certain videos cv2.VideoCapture.set(cv2.CAP_PROP_POS_FRAMES, int) # does not work properly.
# If mpf_component_util.VideoCapture detects that cv2.VideoCapture is not setting the frame position
# properly it will fallback to different SeekStrategy.

class SetFramePositionSeek(object):
    # When setting frame position, OpenCV sets the frame position to 16 frames before the requested
    # frame in order to locate the closest key frame. Once OpenCV locates the key frame, it uses
    # cv2.VideoCapture.grab to advance cv2.VideoCapture's position. This means that when you need
    # to advance 16 or fewer frames, it is more efficient to just use cv2.VideoCapture.grab.
    # https://github.com/opencv/opencv/blob/4.5.0/modules/videoio/src/cap_ffmpeg_impl.hpp#L1459
    SET_POS_MIN_FRAMES = 16

    @classmethod
    def change_position(cls, cv_video_cap, current_position, requested_position):
        frame_diff = requested_position - current_position
        if 0 < frame_diff <= cls.SET_POS_MIN_FRAMES:
            return GrabSeek.change_position(cv_video_cap, current_position, requested_position)
        elif cv_video_cap.set(cv2.CAP_PROP_POS_FRAMES, requested_position):
            return requested_position
        else:
            return current_position

    @staticmethod
    def fallback():
        print('SetFramePositionSeek failed: falling back to GrabSeek', file=sys.stderr)
        return GrabSeek()

class _SequentialSeek(object):
    @classmethod
    def change_position(cls, cv_video_cap, current_position, requested_position):
        new_position_in_future = requested_position > current_position
        if new_position_in_future:
            start = current_position
        else:
            # The current position is past the requested position. We need to start reading from the very
            # beginning of the video. If it were possible to set the frame position to a frame in the middle
            #  of the video, then SetFramePositionSeek would not have failed.
            if not cv_video_cap.set(cv2.CAP_PROP_POS_FRAMES, 0):
                return current_position
            start = 0

        num_frames_to_discard = requested_position - start

        num_success = 0
        for i in range(num_frames_to_discard):
            if cls._advance(cv_video_cap):
                num_success += 1
            else:
                break
        return start + num_success

    @staticmethod
    def _advance(cv_video_cap):
        raise NotImplementedError()

class GrabSeek(_SequentialSeek):

    @staticmethod
    def _advance(cv_video_cap):
        return cv_video_cap.grab()

    @staticmethod
    def fallback():
        print('GrabSeek failed: falling back to ReadSeek', file=sys.stderr)
        return ReadSeek()

class ReadSeek(_SequentialSeek):
    @staticmethod
    def _advance(cv_video_cap):
        was_read, _ = cv_video_cap.read()
        return was_read

    @staticmethod
    def fallback():
        print('ReadSeek failed: No more fallbacks', file=sys.stderr)
        return None


import atexit

import requests
from urllib import parse

from unicornsdk.api.captcha import CaptchaAPI
from unicornsdk.api.devicesession import DeviceSession

class UnicornSdk:
    CONFIG = {
        "access_token": None,
        "sdk_proxy": None,
        "api_url": "https://us.unicorn-bot.com",
        "is_debug": False,
        "timeout": 30,
        "tls_forward_url": None
    }

    API_CLIENT = requests.Session()

    def __init__(self):
        self._debug = False

    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = super(UnicornSdk, cls).__new__(cls)
        return cls._instance

    def _get_api_client(self):
        return self.API_CLIENT

    @property
    def api_url(self):
        return UnicornSdk.CONFIG["api_url"]

    @property
    def timeout(self):
        return UnicornSdk.CONFIG.get("timeout", 60)

    @property
    def tls_forward_url(self):
        return UnicornSdk.CONFIG.get("tls_forward_url") or parse.urljoin(self.api_url, "/api/tls/forward/")

    @property
    def is_debug(self):
        return UnicornSdk.CONFIG["is_debug"]

    def _get_authorization(self):
        if not UnicornSdk.CONFIG.get("access_token"):
            raise Exception("you need set access_token first! ")
        return {
            "Authorization": "Bearer " + str(UnicornSdk.CONFIG["access_token"])
        }

    def _get_proxys_for_sdk(self) -> object:
        cur_proxyuri = UnicornSdk.CONFIG["sdk_proxy"]
        proxies = {
            "http": cur_proxyuri,
            "https": cur_proxyuri,
        }
        return proxies if self.is_debug else None

    @classmethod
    def auth(cls, access_token):
        UnicornSdk.CONFIG["access_token"] = access_token

    @classmethod
    def create_device_session(cls, session_id, platform):
        """
        create new DeviceSession
        :return:
        """
        return DeviceSession(UnicornSdk(), session_id=session_id, platform=platform)

    @classmethod
    def config_sdk(cls, access_token=None, debug=None, api_url=None, tls_forward_url=None):
        if access_token:
            cls.CONFIG["access_token"] = access_token
        if debug is not None:
            cls.CONFIG["is_debug"] = bool(debug)
        if api_url:
            cls.CONFIG["api_url"] = api_url
        if tls_forward_url:
            cls.CONFIG["tls_forward_url"] = tls_forward_url

    @classmethod
    def captcha_api(cls) -> "CaptchaAPI":
        return CaptchaAPI(sdk=UnicornSdk())

    @classmethod
    def on_exit(cls):
        if cls.API_CLIENT:
            cls.API_CLIENT.close()

atexit.register(UnicornSdk.on_exit)


from  urllib.request import urlopen as uReq
from bs4 import BeautifulSoup as soup

file = "Details.csv"
f = open(file, "w")
Headers = "Brand,Product_url\n"
f.write(Headers)
for i in range(1,13):
	my_url = 'https://www.stihlusa.com/products/stihl-top-rated-products/?page={}&viewAll=False'.format(i)
	uClient = uReq(my_url)
	page_html = uClient.read()
	page_soup = soup(page_html, "html.parser")
	containers = page_soup.find_all("div", {"class":"top-product-image"})

	for container in containers:
		brand= container.img["title"]
		url_name=container.a["href"]

		print("Brand: "+ brand)
		print("url_name: "+ url_name)

		f.write(brand + "," + url_name+ "\n")

f.close()


import json,urllib.request

URL = "https://socialblade.com/youtube/channel/UC7_YxT-KID8kRbqZo7MyscQ/monthly"
req = urllib.request.Request(URL, headers={'User-Agent': 'Mozilla/5.0'})
page=urllib.request.urlopen(req)
data = page.read()
e=data.decode('utf-8')

f = open("socialblade.txt", "r")
d=f.read()
q=d.split("\n")[-1].split("\t")[0]
#print(q)
f.close()
f = open("socialblade.txt", "a")
#f.write(d)

import html_to_json

output_json = html_to_json.convert(e)
path=output_json["body"][0]["div"][15]["div"][0]["div"][0]["div"]
for i in range(30):
    #print(i)
    path1=path[2+i]["div"][0]["_value"]
    path2=path[2+i]["div"][3]["div"][1]["_value"].replace(",","")
    if(path1>q):
        f.write("\n"+path1+"\t"+path2)
f.close()


import numbers
import numpy as np

import torch

def check_nans(v, name):
    if (v != v).data.any():
        raise ValueError("Found NaNs in %s" % name)

def throws_exception(exception_type, func, *args, **kwargs):
    try:
        func(*args, **kwargs)
        return False
    except exception_type:
        return True

def check_conditions(v, name, *conditions):
    for condition in conditions:
        if not eval(condition % "v"):
            raise ValueError(("%s must meet the following condition: " + condition)
            	% (name, name))
    return v

def make_list(l, length, name, *conditions):
    if throws_exception(TypeError, list, l):
        l = [l]*length
    else:
        l = list(l)
    if len(l) != length:
        raise ValueError("%s must be a list of length %d." % (name, length))
    for i, ll in enumerate(l):
        l[i] = check_conditions(ll, name, *conditions)
    return l

def check_tensor_dims(t, name, dims):
    s = t.size()
    if len(s) != len(dims):
        raise ValueError("%s must be a %d-dimensional tensor." % (name, len(dims)))
    for i in range(len(dims)):
        if dims[i] >= 0 and s[i] != dims[i]:
            raise ValueError("The %dth dimension of %s must have size %d, not %d."
            	% (i, name, dims[i], s[i]))

def list2tensor(l):
    return torch.from_numpy(np.array(l, dtype=np.float32))


import json

from django.http import JsonResponse
from django.shortcuts import render

from .ResponseFood import ResponseFood
from .sampleData import addData
import calories.managers as manager
from calories.services.dateConvertion import *
from calories.services.translation import translate_polish_to_english, translate_english_to_polish
from calories.services.textCoversion import upper_first_letter
from calories.services.wolfram import get_food_data_from_wolfram
from calories.services.weeklyMath import *

__DEFAULT_USER = 'default@user'

# Create your views here.

def main_view(request):
    today = datetime.date.today()
    user = manager.get_user(__DEFAULT_USER)
    return render(request, "index.html", {"prev_date": date_to_string(yesterday(today)),
                                          "act_date": date_to_string(today),
                                          "next_date": date_to_string(tommorow(today)),
                                          "food_list": manager.get_all_foods_from_day_with_count(today),
                                          "user_lang": user.lang,
                                          }
                  )

def sample_data(request):
    addData.add_sample_data()
    return render(request, "sampleData.html", {})

def user_settings_view(request):
    user = manager.get_user(__DEFAULT_USER)
    return render(request, "userSettings.html", {'user': user,
                                                 'user_lang': user.lang})

def weekly_view(request):
    return render(request, "weekly.html", {'user_lang': manager.get_user(__DEFAULT_USER).lang,
                                           'act_week_first': date_list_to_string_list(get_week(datetime.date.today()))[0],
                                           'act_week_last': date_list_to_string_list(get_week(datetime.date.today()))[-1]})

def update_user_settings(request):
    if request.is_ajax and request.method == "POST":
        data = request.POST.dict()

        manager.change_user(__DEFAULT_USER,
                            float(data['calories']),
                            float(data['fat']),
                            float(data['carbohydrates']),
                            float(data['protein']),
                            float(data['height']),
                            float(data['weight']),
                            data['sex'],
                            data['lang'])

        return JsonResponse({}, status=200)

def get_day_food_list(request):
    if request.is_ajax and request.method == "POST":
        data = request.POST.dict()
        this_day = string_to_date(data['date'])

        my_response = []
        for food, unit in manager.get_all_foods_from_day_with_count(this_day):
            my_response.append(ResponseFood(food, unit))

        json_string = json.dumps([ob.__dict__ for ob in my_response])

        return JsonResponse({"food_list": json_string,
                             "prev_date": date_to_string(yesterday(this_day)),
                             "act_date": date_to_string(this_day),
                             "next_date": date_to_string(tommorow(this_day))}, status=200)

def get_food_list(request):
    if request.is_ajax and request.method == "POST":
        json_string = json.dumps([ResponseFood(ob, 0).__dict__ for ob in manager.get_all_foods()])

        return JsonResponse({"food_list": json_string}, status=200)

def add_food_to_day(request):
    if request.is_ajax and request.method == "POST":
        data = request.POST.dict()
        lang = data['lang']
        food_name = data['food_name']
        count = float(data['count'])
        date = string_to_date(data['date'])

        if lang == "pl":
            food_name = translate_polish_to_english(food_name)

        food_name = upper_first_letter(food_name)
        manager.add_food_to_day(manager.get_day(date),
                                manager.get_food(food_name),
                                count)

        return JsonResponse({}, status=200)

def get_food(request):
    if request.is_ajax and request.method == "POST":
        data = request.POST.dict()
        lang = data['lang']
        food_name = data['food_name']

        if lang == "pl":
            food_name = translate_polish_to_english(food_name)

        json_string = json.dumps(ResponseFood(manager.get_food(food_name), 0).__dict__)
        return JsonResponse({"food": json_string}, status=200)

def add_manual_food_to_day(request):
    if request.is_ajax and request.method == "POST":
        data = request.POST.dict()

        lang = data['lang']
        date = string_to_date(data['date'])
        food_name = data['food_name']

        if lang == "pl":
            food_name_pl = upper_first_letter(food_name)
            food_name = upper_first_letter(translate_polish_to_english(food_name))
        else:
            food_name_pl = upper_first_letter(translate_english_to_polish(food_name))
            food_name = upper_first_letter(food_name)

        new_food = manager.add_or_change_food(food_name,
                                              food_name_pl,
                                              data['food_unit'],
                                              float(data['food_calories']),
                                              float(data['food_fat']),
                                              float(data['food_carbohydrates']),
                                              float(data['food_protein']))

        manager.add_food_to_day(manager.get_day(date), new_food, float(data['food_count']))

        return JsonResponse({}, status=200)

def add_wolfram_food_to_day(request):
    if request.is_ajax and request.method == "POST":
        data = request.POST.dict()

        lang = data['lang']
        date = string_to_date(data['date'])
        food_name = data['food_name']
        food_unit = data['food_unit']
        if food_unit == '/ 100g' or food_unit == 'x 100g':
            food_unit = "g"

        if lang == "pl":
            food_name_pl = upper_first_letter(food_name)
            food_name = upper_first_letter(translate_polish_to_english(food_name))
        else:
            food_name_pl = upper_first_letter(translate_english_to_polish(food_name))
            food_name = upper_first_letter(food_name)

        new_food = get_food_data_from_wolfram(food_name, food_unit)
        new_food.name_pl = food_name_pl
        manager.save_food(new_food)

        manager.add_food_to_day(manager.get_day(date), new_food, float(data['food_count']))
        return JsonResponse({}, status=200)

def get_weekly_summary(request):
    if request.is_ajax and request.method == "POST":
        data = request.POST.dict()
        date = string_to_date(data['date'])

        return JsonResponse({"prev_week": date_list_to_string_list(get_prev_week(date)),
                             "act_week": date_list_to_string_list(get_week(date)),
                             "next_week": date_list_to_string_list(get_next_week(date)),
                             "daily_list": get_weekly_nutrition_json(get_week(date)),
                             "popular_foods": get_most_popular_foods_json(get_week(date))},
                            status=200)


import torch
import sys

class KNN(torch.nn.Module):
    def __init__(self, colors, classes, K=1):
        super().__init__()
        self.classes = torch.arange(0, len(classes))
        self.K = K

        if type(colors) == list:
            if type(colors[0]) == list or type(colors[0]) == tuple:
                colors = [torch.tensor(elm)/255 for elm in colors]
            else:
                pass
            colors = torch.stack(colors, dim=0).squeeze(1)
        elif type(colors) == torch.Tensor:
            pass
        else:
            print(f"WRONG INPUT TYPE {type(colors)}, must be one of {torch.Tensor} or {list} of {torch.Tensor}")
            sys.exit(1)
        a_norm = colors.norm(dim=-1, p=2)  # (batch size, 1)
        colors = colors.divide(a_norm.unsqueeze(1))
        self.colors = colors

    def preprocess(self, rgb_mask):
        self.shape = rgb_mask.shape
        rgb_mask = rgb_mask.permute(0, 2, 3, 1)
        rgb_mask = rgb_mask.reshape(rgb_mask.shape[0], rgb_mask.shape[1] * rgb_mask.shape[2],  rgb_mask.shape[3])
        return rgb_mask

    def forward(self, rgb_mask):
        rgb_mask = self.preprocess(rgb_mask)
        rgb_mask_ = torch.zeros_like(rgb_mask)

        for bs in range(rgb_mask.shape[0]):
            for pix in range(rgb_mask.shape[1]):
                rgb_mask_[bs, pix] = self._forward(rgb_mask[bs, pix].unsqueeze(0))

        rgb_mask_ = rgb_mask_.permute(0, 2, 1)
        return rgb_mask_.reshape(self.shape)

    def _forward(self, vector):
        if vector.sum()<=0:
            return torch.tensor([0, 0, 0])

        a_norm = vector.norm(dim=-1, p=2)
        vector = vector.divide(a_norm.unsqueeze(1))

        cos_sim = torch.nn.functional.linear(self.colors, vector).squeeze(1)

        cos_sim_topK = cos_sim.topk(1)
        indexes = cos_sim_topK[1][0].cpu()

        preds_int = self.classes[indexes]

        out = self.colors[preds_int]

        return out


import itertools, time, json
from urllib.request import urlopen, Request
from werkzeug.datastructures import ImmutableMultiDict
grps = ["fw","ddos","ips"]
FCC_URL = "http://localhost:5000/manual"

perms = list(itertools.permutations(grps))
iterate_list = list()
STANDARD_CONF = ("ddos", "fw", "ips")
perms.remove(STANDARD_CONF)

print(perms[0])

for conf in perms:
    time.sleep(10)
    conn = Request(FCC_URL, json.dumps(conf).encode("utf-8"),
                   {'Content-Type': 'application/json'})
    resp = urlopen(conn)
    print(resp.getcode())


import os
import pickle
import random

import keras
import numpy as np
import pandas as pd
from keras.callbacks import EarlyStopping, ModelCheckpoint
from keras.models import Model, load_model
from keras.optimizers import Adam
from keras_preprocessing.image import ImageDataGenerator
from PIL import Image
import tensorflow as tf
from keras import backend as K

from poster_model import get_model

def load_data(image_file, val_proportion, test_proportion,
               shuffle=True, random_state=42):
    """
    Split the data into train, validation and test sets
    and return them as pandas dataframes.

    Input:
    -------
    image_file: Path to the file containing the list of image paths.
    val_proportion: The proportion of the entire dataset which needs to be
                    allocated to the validation set.
    test_proportion: The proportion of the entire dataset which needs to be
                     allocated to the test set.
    shuffle: Boolean value indicating if the data needs to be shuffled prior
             to splitting. Default is True.
    random_state: Value of seed for the random number generator.Default is 42.
    """

    # Read and load the list of all the images.
    with open(image_file, 'r') as file_:
        image_paths = file_.readlines()
    # Strip unnecessary character and drop empty rows
    image_paths = list(filter(len, map(str.strip, image_paths)))

    if shuffle:
        random.seed(random_state)  # set the random generator seed
        random.shuffle(image_paths)  # shuffle if required

    # Get the total size and calculate the split sizes
    total_size = len(image_paths)
    test_size = int(test_proportion * total_size)
    val_size = int(val_proportion * total_size)

    # Split the data
    test_samples = image_paths[:test_size]
    image_paths = image_paths[test_size:]
    val_samples = image_paths[:val_size]
    train_samples = image_paths[val_size:]

    # Convert each to pandas data frame
    train_df = pd.DataFrame(data=train_samples, columns=['filename'])
    val_df = pd.DataFrame(data=val_samples, columns=['filename'])
    test_df = pd.DataFrame(data=test_samples, columns=['filename'])

    return train_df, val_df, test_df

def train_model(data_file, batch_size, epochs, val_proportion,
                test_proportion, optimizer, loss, save_dir,
                image_size, shuffle=True, random_state=42):
    # Get the data
    train_df, val_df, test_df = load_data(data_file, val_proportion,
                                          test_proportion, shuffle=shuffle,
                                          random_state=random_state)

    # Setup data generator
    data_generator = ImageDataGenerator(rescale=1./255,
                                        data_format='channels_last')
    train_generator = data_generator.flow_from_dataframe(dataframe=train_df,
                                                         class_mode='input',
                                                         target_size=(image_size,
                                                                      image_size),
                                                         batch_size=batch_size)
    val_generator = data_generator.flow_from_dataframe(dataframe=val_df,
                                                       class_mode='input',
                                                       target_size=(image_size,
                                                                    image_size),
                                                       batch_size=batch_size)
    test_generator = data_generator.flow_from_dataframe(dataframe=test_df,
                                                        class_mode='input',
                                                        target_size=(image_size,
                                                                     image_size),
                                                        batch_size=batch_size)
    STEP_SIZE_TRAIN = train_generator.n // train_generator.batch_size
    STEP_SIZE_VAL = val_generator.n // val_generator.batch_size
    STEP_SIZE_TEST = test_generator.n // test_generator.batch_size

    # Get the model
    model, encoder_output = get_model()

    # Setup early stopping
    early_stopper_cb = EarlyStopping(monitor='val_loss', patience=10, verbose=1)

    # Setup model checkpointing
    chkpt = save_dir + 'AutoEncoder_poster_weights.{epoch:02d}-{loss:.3f}-{val_loss:.3f}.hdf5'
    checkpoint_cb = ModelCheckpoint(filepath = chkpt, monitor='val_loss',
                                    verbose=1, save_best_only=True, mode='auto')

    # Compile the model
    model.compile(optimizer=optimizer, loss=loss)

    # Fit the model
    history = model.fit_generator(generator=train_generator,
                                  steps_per_epoch=STEP_SIZE_TRAIN,
                                  validation_data=val_generator,
                                  validation_steps=STEP_SIZE_VAL,
                                  epochs=epochs, verbose=1,
                                  callbacks=[early_stopper_cb, checkpoint_cb])

    # Test the model
    score = model.evaluate_generator(generator=test_generator,
                                     steps=STEP_SIZE_TEST,
                                     verbose=1)
    print('Score:\n', score)

if __name__ == "__main__":
    # Set the model training parameters
    data_file = '../data/posters/poster.txt'
    test_proportion = 0.15
    val_proportion = 0.1
    batch_size = 2
    epochs = 150
    optimizer = 'adam'
    loss = 'mean_squared_error'
    save_dir = '../weights/'
    image_size = 256

    session_conf = tf.ConfigProto()
    session_conf.gpu_options.allow_growth=True
    session = tf.Session(graph=tf.get_default_graph(), config=session_conf)
    K.set_session(session)

    # Train the model
    train_model(data_file, batch_size, epochs, val_proportion, test_proportion,
                optimizer, loss, save_dir, image_size)


import sys
import youtube_dl
import json

def get_youtube_mp3_link(video_url):
    ydl_opts = {'format': 'bestaudio', 'quiet': True}
    with youtube_dl.YoutubeDL(ydl_opts) as ydl:
        try:
            info = ydl.extract_info(video_url, download=False)
            mp3_link = info.get('formats', [{}])[0].get('url')
            if mp3_link:
                return mp3_link
            else:
                #print(json.dumps({"state": "Không tìm thấy thông tin định dạng audio."}))
                return None
        except youtube_dl.DownloadError as e:
            #print(json.dumps({"state": f"Lỗi khi tải thông tin: {e}"}))
            return None

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print(json.dumps({"state": "Vui lòng cung cấp đường dẫn URL của video YouTube."}))
    else:
        video_url = sys.argv[1]
        mp3_link = get_youtube_mp3_link(video_url)
        if mp3_link is not None:
            print(mp3_link)


import numpy, cv2, copy, time

class ImageMap:

    def __init__(self, size):
        self.height, self.width = size
        self.map = numpy.zeros((self.height, self.width,3), dtype="uint8")
        self.map.fill(255)
        self.streams = []

    def create_stream(self, **kwargs):

        stream = ImageStream(map=self, **kwargs)
        self.streams.append(stream)

        return stream

    def draw(self, image, h):

        width = self.map.shape[1]
        height = self.map.shape[0]

        mask = numpy.zeros((image.shape[0], image.shape[1]), dtype="uint8")
        mask[:] = 255
        mask_warp = cv2.warpPerspective(mask, h, (width, height))
        image_warp = cv2.warpPerspective(image, h, (width, height))

        locs = numpy.where(mask_warp == 255)
        self.map[locs[0], locs[1]] = image_warp[locs[0], locs[1]]

    def warp(self):
        map = copy.deepcopy(self.map)

        for stream in list(self.streams):
            try:
                centers, ground_truth_centers, last_image = stream.warp()

                map = self.draw_centers(map, centers, (255,0,0))
                map = self.draw_centers(map, ground_truth_centers, (0,255,0))

                if stream.last_border and last_image is not None:
                    last_image.estimate_borders().draw(map, (255, 255, 255))

            except StopIteration:
                self.streams.remove(stream)
                map = self.draw_centers(map, stream.warped_centers, (255,0,0))
                map = self.draw_centers(map, stream.ground_truth_centers, (0,255,0))

        return map

    def finished(self):

        return len(self.streams) == 0

    def clear(self):
        self.map = numpy.zeros((self.height, self.width, 3), dtype="uint8")

    def image(self):
        return self.map

    def draw_centers(self, map, centers, color):
        previous_center = None
        for center in centers:
            if previous_center is not None:
                map = cv2.line(map, previous_center, center, color)
            map = cv2.circle(map, center, 3, (255, 255, 255), 1)
            previous_center = center
        return map

class LRUList:

    def __init__(self, capacity):
        self.capacity=capacity
        self.items = []
        self.i = -1

    def add(self, item):
        if len(self.items) == self.capacity:
            self.updateCnt()
            self.items[self.i] = item

        else:
            self.updateCnt()
            self.items.append(item)

    def empty(self):
        return len(self.items) == 0

    def getLatest(self):
        return self.items[self.i]

    def getSecondLatest(self):
        if self.i-1 == -1:
            return self.items[self.capacity-1]
        else:
            return self.items[self.i-1]

    def updateCnt(self):
        if self.i == self.capacity-1:
            self.i = 0
        else:
            self.i += 1

    def __iter__(self):
        self.currentItem = 0
        return self

    def next(self):
        if self.currentItem == self.capacity or self.currentItem >= len(self.items):
            raise StopIteration
        else:
            item = self.items[self.currentItem]
            self.currentItem += 1
            return item

    def images(self):
        h = [item.H for item in self.items]
        images = [item.image for item in self.items]
        warp_images = [item.warp_image for item in self.items]

        return h, images, warp_images

    def __len__(self):
        return len(self.items)

class Border:

    def __init__(self, p1, p2, p3, p4):
        self.p1 = p1
        self.p2 = p2
        self.p3 = p3
        self.p4 = p4

    def __sub__(self, other):

        diff = numpy.sqrt(numpy.sum(numpy.power(numpy.subtract(other.p1, self.p1), 2)))
        diff += numpy.sqrt(numpy.sum(numpy.power(numpy.subtract(other.p2, self.p2), 2)))
        diff += numpy.sqrt(numpy.sum(numpy.power(numpy.subtract(other.p3, self.p3), 2)))
        diff += numpy.sqrt(numpy.sum(numpy.power(numpy.subtract(other.p4, self.p4), 2)))

        return diff/4

    @staticmethod
    def avg_border_distance(borders):

        if len(borders) < 2:
            return 0

        ref_border = borders[0];
        total_border_distance = 0
        for border in borders[1:]:
            total_border_distance += ref_border - border;

        return total_border_distance / (len(borders) -1);

    def rectangle(self):

        x1, y1 = self.p1
        x2, y2 = self.p2
        x3, y3 = self.p3
        x4, y4 = self.p4

        return Border((max(x1,x2),max(y1,y4)),(max(x1,x2),min(y2,y3)),(min(x3,x4),min(y2,y3)),(min(x3,x4),max(y1,y4)))

    def draw(self, image, color):

        cv2.line(image, self.p1, self.p2, color)
        cv2.line(image, self.p2, self.p3, color)
        cv2.line(image, self.p3, self.p4, color)
        cv2.line(image, self.p4, self.p1, color)

        return image

class MatchedImage:

    def __init__(self, image, warp_image, H):
        self.image = image
        self.warp_image = warp_image
        self.H = H

    def estimate_borders(self):

        width = self.warp_image.shape[1]
        height = self.warp_image.shape[0]

        m1 = numpy.dot(numpy.array([0,0,1]), cv2.transpose(self.H))
        m2 = numpy.dot(numpy.array([0,height,1]), cv2.transpose(self.H))
        m3 = numpy.dot(numpy.array([width,height,1]), cv2.transpose(self.H))
        m4 = numpy.dot(numpy.array([width,0,1]), cv2.transpose(self.H))

        p1 = tuple(numpy.rint(numpy.divide(m1[0:2], m1[2])).astype(int))
        p2 = tuple(numpy.rint(numpy.divide(m2[0:2], m2[2])).astype(int)[0:2])
        p3 = tuple(numpy.rint(numpy.divide(m3[0:2], m3[2])).astype(int)[0:2])
        p4 = tuple(numpy.rint(numpy.divide(m4[0:2], m4[2])).astype(int)[0:2])

        return Border(p1,p2,p3,p4)

class ImageStream:
    def __init__(self, map, position, func, data, memory=1, distance=10, borders=False, indicate_centers=False, last_border=False):
        self.position = position
        self.map = map
        self.last_images = LRUList(memory)
        self.distance = distance
        self.image_cnt = 0
        self.homography_func = func
        self.data = data
        self.data_position = 0
        self.borders=borders
        self.duration = None
        self.warped_centers = []
        self.ground_truth_centers = []
        self.indicate_centers = indicate_centers
        self.ground_truth_h = None
        self.last_border = last_border
        self.last_image = None
        self.last_patch = None

    def warp(self):
        duration = 0

        image, warp_image, ground_truth_h = self.data.get(self.data_position)
        self.last_image = warp_image
        self.last_patch = image

        center = numpy.array([[warp_image.shape[1]/2],[warp_image.shape[0]/2],[1.0]], dtype="float32")
        self.data_position += 1

        matched_image = None
        h_avg = None
        if self.last_images.empty():
            transl_h = self.position[0]
            transl_w = self.position[1]

            h_avg = numpy.array([[1, 0, transl_w], [0, 1, transl_h], [0, 0, 1]], dtype="float")

            self.map.draw(warp_image, h_avg)

            matched_image = MatchedImage(image, warp_image, h_avg)
            self.last_images.add(matched_image)

            if self.indicate_centers:
                self.warped_centers.append(self.warp_point(h_avg, center))

                if ground_truth_h is not None:

                    self.ground_truth_h = numpy.matmul(ground_truth_h,h_avg)
                    self.ground_truth_centers.append(self.warp_point(self.ground_truth_h, center))

            self.duration = None

        else:
            h_acc = numpy.zeros((3, 3))
            all_borders = []

            last_hs, last_images, last_warp_images = self.last_images.images()

            start = time.time()
            h = self.homography_func(last_images, image, last_warp_images, warp_image)
            duration = time.time() - start

            self.duration = duration

            h_acc = []
            for last_h, last_image, last_warp_image, h in zip(last_hs, last_images, last_warp_images, h):

                if h is not None:
                    h_acc.append(numpy.matmul(last_h, h))
                    current_image = MatchedImage(image, warp_image, h)
                    all_borders.append(current_image.estimate_borders())

            border_distance = Border.avg_border_distance(all_borders)

            if len(h_acc) > 0 and border_distance < self.distance:
                h_avg = numpy.divide(sum(h_acc), len(h_acc))

                matched_image = MatchedImage(image, warp_image, h_avg)

                self.map.draw(warp_image, h_avg)

                if self.borders:
                    matched_image.estimate_borders().draw(self.map.map, (255, 255, 255))

                self.last_images.add(matched_image)

                if self.indicate_centers:
                    self.warped_centers.append(self.warp_point(h_avg, center))

                    if ground_truth_h is not None:
                        self.ground_truth_h = numpy.matmul(ground_truth_h,self.ground_truth_h)
                        self.ground_truth_centers.append(self.warp_point(self.ground_truth_h, center))

        if len(self.last_images) > 1 and h_avg is not None:
            h_return = numpy.matmul(h_avg, numpy.linalg.inv(self.last_images.getSecondLatest().H))
        else:
            h_return = None

        return self.warped_centers, self.ground_truth_centers, matched_image

    def warp_point(self, h, point):
        warped_center = numpy.matmul(h, point)
        warped_center = (warped_center / warped_center[2, 0])
        warped_center = (int(warped_center[0, 0]), int(warped_center[1, 0]))
        return warped_center

    def borders(self, image, warp_image):

        colors = [(255, 0, 0),(0, 255, 0),(0, 0, 255),(255,255,0),(0,255,255),(255,0,255),(255,255,255)]

        map = copy.deepcopy(self.map.map)

        h_acc = numpy.zeros((3, 3))
        all_borders = []

        for i, lastImage in enumerate(self.last_images):
            h = self.homography_func(lastImage.image, image, lastImage.warp_image, warp_image)

            if h is None:
                h_acc = None
                break
            else:
                h = numpy.matmul(lastImage.H, h)
                h_acc += h
                current_image = MatchedImage(image, warp_image, h)
                map = current_image.estimate_borders().draw(map, colors[i])

        return map

    def get_initial_h4pt(self):
        return numpy.array([self.position[1], self.position[0],self.position[1], self.position[0],self.position[1], self.position[0],self.position[1], self.position[0]])