__author__ = 'greg'
import bisect
import math
import abc
import numpy as np
import os
import re
# import ibcc
import csv
import matplotlib.pyplot as plt
import random
import socket
import warnings
import scipy
from shapely.geometry import Polygon
# import panoptes_api


def index(a, x):
    'Locate the leftmost value exactly equal to x'
    i = bisect.bisect_left(a, x)
    if i != len(a) and a[i] == x:
        return i
    raise ValueError


# code from
# http://stackoverflow.com/questions/2130016/splitting-a-list-of-arbitrary-size-into-only-roughly-n-equal-parts
def chunk_it(seq, num):
    avg = len(seq) / float(num)
    out = []
    last = 0.0

    while last < len(seq):
        out.append(dict(seq[int(last):int(last + avg)]))
        last += avg

    return out


# mappings are for use in dimension reduction
def identity_mapping(markings):
    return markings


class Cluster:
    __metaclass__ = abc.ABCMeta

    def __init__(self,shape,additional_params):
        """
        :param project_api: how to talk to whatever project we are clustering for (Panoptes/Ouroboros shouldn't matter)
        :param min_cluster_size: minimum number of points in a cluster to not be considered noise
        :return:
        """
        self.shape = shape
        if "reduction" in additional_params:
            self.dim_reduction_alg = additional_params["reduction"]
        else:
            self.dim_reduction_alg = identity_mapping

        self.retired_lines_3 = 0
        self.retired_lines_4 = 0
        self.retired_lines_5 = 0

        self.stats = {}


    @abc.abstractmethod
    def __cluster__(self,markings,user_ids,tools,reduced_markings,dimensions):
        """
        the main function for clustering
        :param user_ids:
        :param markings:
        :param jpeg_file:
        :return cluster_centers: the center of each cluster - probably just take the average along each dimension
        feel free to try something else but the results might not mean as much
        :return markings_per_cluster: the markings in each cluster
        :return users_per_cluster: the user id of each marking per cluster
        :return time_to_cluster: how long it took to cluster
        """
        cluster_centers = []
        markings_per_cluster = []
        users_per_cluster = []
        time_to_cluster = 0

        return (cluster_centers , markings_per_cluster, users_per_cluster), time_to_cluster

    def __aggregate__(self,raw_markings,image_dimensions):
        """
        the function to call from outside to do the clustering
        override but call if you want to add additional functionality
        :param subject_id: what is the subject (in Ouroboros == zooniverse_id)
        :param jpeg_file: for debugging - to show step by step what is happening
        :return:
        """
        aggregation = {}
        # start by calling the api to get the annotations along with the list of who made each marking
        # so for this function, we know that annotations = markings
        # all_markings =  self.project_api.__get_markings__(subject_id,gold_standard)
        # print all_markings
        # self.clusterResults[subject_id] = {"param":"task_id"}
        for task_id in raw_markings:
            # go through each shape independently
            for shape in raw_markings[task_id].keys():
                # if is this shape does not correspond to the specific shape this clustering algorithm was
                # created for - skip
                if shape != self.shape:
                    continue

                for subject_count,subject_id in enumerate(raw_markings[task_id][shape]):
                    assert raw_markings[task_id][shape][subject_id] != []

                    # remove any "markings" which correspond to the user not making a marking
                    # these are still useful for noting that the user saw that image
                    pruned_markings = [(u,m,t) for u,m,t in raw_markings[task_id][shape][subject_id] if m is not None]
                    all_users,t1,t2 = zip(*raw_markings[task_id][shape][subject_id])
                    all_users = list(set(all_users))

                    # empty image
                    if pruned_markings == []:
                        # no centers, no points, no users per cluster
                        cluster_results = []
                    else:
                        users,markings,tools = zip(*pruned_markings)

                        reduced_markings = self.dim_reduction_alg(markings)

                        # do the actual clustering
                        cluster_results,time_to_cluster = self.__cluster__(markings,users,tools,reduced_markings,image_dimensions[subject_id])

                    # store the results - note we need to store even for empty images
                    if subject_id not in aggregation:
                        aggregation[subject_id] = {}
                    if task_id not in aggregation[subject_id]:
                        aggregation[subject_id][task_id] = {}
                    if shape not in aggregation[subject_id][task_id]:
                        # store the set of all users who have seen this subject/task
                        # used for determining false vs. true positives
                        aggregation[subject_id][task_id][str(shape) + " clusters"] = {"all_users":all_users}

                    for cluster_index,cluster in enumerate(cluster_results):
                        aggregation[subject_id][task_id][shape+ " clusters"][cluster_index] = cluster

        # we should have some results
        # assert aggregation != {"param":"subject_id"}


        return aggregation

@@ -38,235 +38,77 @@
 ort 
-math%0Aimport abc%0Aimport numpy as np%0Aimport os%0Aimport re%0A# import ibcc%0Aimport csv%0Aimport matplotlib.pyplot as plt%0Aimport random%0Aimport socket%0Aimport warnings%0Aimport scipy%0Afrom shapely.geometry import Polygon%0A# import panoptes_api
+abc%0Aimport matplotlib.pyplot as plt%0Aimport matplotlib.cbook as cbook%0A
 %0A%0A%0Ad
@@ -686,24 +686,52 @@
 (markings):%0A
+    print %22identiy mapping%22%0A
     return m
@@ -816,16 +816,24 @@
 f,shape,
+project,
 addition
@@ -1130,16 +1130,17 @@
 = shape%0A
+%0A
         
@@ -1437,16 +1437,47 @@
 ts = %7B%7D%0A
+        self.project = project%0A
 %0A%0A    @a
@@ -3094,16 +3094,57 @@
 sk_id%22%7D%0A
+        print %22clustering %22 + self.shape%0A
         
@@ -3206,226 +3206,177 @@
 ach 
-shape independently%0A            for shape in raw_markings%5Btask_id%5D.keys():%0A                # if is this shape does not corresp
+marking tool - multiple tools may use the desired shape tool%0A            # so pay attenti
 on
-d
  to 
-the specific shape this clustering algorithm was%0A                # created for - skip
+only those markings%0A            for shape in raw_markings%5Btask_id%5D.keys():
 %0A   
@@ -3531,16 +3531,53 @@
 hape%5D):%0A
+                    print subject_id%0A
         
@@ -4491,32 +4491,227 @@
 tual clustering%0A
+                        fname = self.project.__image_setup__(subject_id)%0A                        image_file = cbook.get_sample_data(fname)%0A                        image = plt.imread(image_file)%0A%0A
                 
@@ -4835,16 +4835,28 @@
 ject_id%5D
+,image=image
 )%0A%0A     

clean up code