from imp import reload
from polypoint import polypoint
import unittest
import time
import random
import numpy as np
import numpy.testing as npt
import pickle
import os

abbr = [
    "AL",
    "AK",
    "AZ",
    "AR",
    "CA",
    "CO",
    "CT",
    "DE",
    "FL",
    "GA",
    "HI",
    "ID",
    "IL",
    "IN",
    "IA",
    "KS",
    "KY",
    "LA",
    "ME",
    "MD",
    "MA",
    "MI",
    "MN",
    "MS",
    "MO",
    "MT",
    "NE",
    "NV",
    "NH",
    "NJ",
    "NM",
    "NY",
    "NC",
    "ND",
    "OH",
    "OK",
    "OR",
    "PA",
    "RI",
    "SC",
    "SD",
    "TN",
    "TX",
    "UT",
    "VT",
    "VA",
    "WA",
    "WV",
    "WI",
    "WY",
]

state_capitals = [
    ("Alabama",	"Montgomery", 32.361538, -86.279118,),
    ("Alaska",	"Juneau", 58.301935, -134.419740,),
    ("Arizona",	"Phoenix", 33.448457, -112.073844,),
    ("Arkansas",	"Little Rock", 34.736009, -92.331122,),
    ("California",	"Sacramento", 38.555605, -121.468926,),
    ("Colorado",	"Denver", 39.7391667, -104.984167,),
    ("Connecticut",	"Hartford", 41.767, -72.677,),
    ("Delaware",	"Dover", 39.161921, -75.526755,),
    ("Florida",	"Tallahassee", 30.4518, -84.27277,),
    ("Georgia",	"Atlanta", 33.76, -84.39,),
    ("Hawaii",	"Honolulu", 21.30895, -157.826182,),
    ("Idaho",	"Boise", 43.613739, -116.237651,),
    ("Illinois",	"Springfield", 39.783250, -89.650373,),
    ("Indiana",	"Indianapolis", 39.790942, -86.147685,),
    ("Iowa",	"Des Moines", 41.590939, -93.620866,),
    ("Kansas",	"Topeka", 39.04, -95.69,),
    ("Kentucky",	"Frankfort", 38.197274, -84.86311,),
    ("Louisiana",	"Baton Rouge", 30.45809, -91.140229,),
    ("Maine",	"Augusta", 44.323535, -69.765261,),
    ("Maryland",	"Annapolis", 38.972945, -76.501157,),
    ("Massachusetts",	"Boston", 42.2352, -71.0275,),
    ("Michigan",	"Lansing", 42.7335, -84.5467,),
    ("Minnesota",	"Saint Paul", 44.95, -93.094,),
    ("Mississippi",	"Jackson", 32.320, -90.207,),
    ("Missouri",	"Jefferson City", 38.572954, -92.189283,),
    ("Montana",	"Helana", 46.595805, -112.027031,),
    ("Nebraska",	"Lincoln", 40.809868, -96.675345,),
    ("Nevada",	"Carson City", 39.160949, -119.753877,),
    ("New Hampshire",	"Concord", 43.220093, -71.549127,),
    ("New Jersey",	"Trenton", 40.221741, -74.756138,),
    ("New Mexico",	"Santa Fe", 35.667231, -105.964575,),
    ("New York",	"Albany", 42.659829, -73.781339,),
    ("North Carolina",	"Raleigh", 35.771, -78.638,),
    ("North Dakota",	"Bismarck", 48.813343, -100.779004,),
    ("Ohio",	"Columbus", 39.962245, -83.000647,),
    ("Oklahoma",	"Oklahoma City", 35.482309, -97.534994,),
    ("Oregon",	"Salem", 44.931109, -123.029159,),
    ("Pennsylvania",	"Harrisburg", 40.269789, -76.875613,),
    ("Rhode Island",	"Providence", 41.82355, -71.422132,),
    ("South Carolina",	"Columbia", 34.000, -81.035,),
    ("South Dakota",	"Pierre", 44.367966, -100.336378,),
    ("Tennessee",	"Nashville", 36.165, -86.784,),
    ("Texas",	"Austin", 30.266667, -97.75,),
    ("Utah",	"Salt Lake City", 40.7547, -111.892622,),
    ("Vermont",	"Montpelier", 44.26639, -72.57194,),
    ("Virginia",	"Richmond", 37.54, -77.46,),
    ("Washington",	"Olympia", 47.042418, -122.893077,),
    ("West Virginia",	"Charleston", 38.349497, -81.633294,),
    ("Wisconsin",	"Madison", 43.074722, -89.384444,),
    ("Wyoming",	"Cheyenne", 41.145548, -104.802042,),
]

class Tests(unittest.TestCase):


    def test_state_capitals_parallel_disabled(self):
        p = polypoint.PolygonClassifier()
        p.initialize()
        states_alpha = p.conf.name_list
        p.conf.parallel_enabled = False
        capital_points = []
        for e in state_capitals:
            state_name, capital, lat, lng = e
            capital_points.append((lat, lng))


        test_state_list = p.match_points_to_polygon(capital_points)
        test_state_list_ans = p.get_polygon_names(test_state_list)
        self.maxDiff = None
        self.assertEqual(test_state_list_ans, states_alpha)


    def test_state_capitals_parallel_enabled(self):
        p = polypoint.PolygonClassifier()
        p.initialize()
        states_alpha = p.conf.name_list
        capital_points = []
        for e in state_capitals:
            state_name, capital, lat, lng = e
            capital_points.append((lat, lng))

        test_state_list = p.match_points_to_polygon(capital_points)
        test_state_list_ans = p.get_polygon_names(test_state_list)
        self.maxDiff = None
        self.assertEqual(test_state_list_ans, states_alpha)


    def test_parallel_faster(self):
        p = polypoint.PolygonClassifier()
        p.initialize()
        p.conf.parallel_enabled = False
        capital_points = []
        for e in state_capitals:
            state_name, capital, lat, lng = e
            capital_points.append((lat, lng))

        capital_points *= 1000
        begin_seq = time.time()
        test_state_list = p.match_points_to_polygon(capital_points)
        end_seq = time.time()

        p.initialize()
        p.conf.parallel_enabled = True
        begin_parallel = time.time()
        test_state_list = p.match_points_to_polygon(capital_points)
        end_parallel = time.time()

        sequential_time = end_seq - begin_seq
        parallel_time = end_parallel - begin_parallel
        self.maxDiff = None
        self.assertTrue(sequential_time > parallel_time)

    def test_large_list(self):
        p = polypoint.PolygonClassifier()
        p.initialize()
        point_list = []
        answers = []
        if os.path.exists('testing/large_point_list.pickle'):
            with open('testing/large_point_list.pickle', 'rb') as f:
                point_list = pickle.load(f)
        if os.path.exists('testing/answers_list.pickle'):
            with open('testing/answers_list.pickle', 'rb') as f:
                answers = pickle.load(f)

        if (len(point_list) == 0):
            with open("testing/uscities.txt") as f:
                for line in f:
                    words = line.split(",")
                    if words[3] in abbr:
                        t = (float(words[5]), float(words[6][:-1]))
                        point_list.append(t)
            with open('testing/large_point_list.pickle', 'wb') as f:
                pickle.dump(point_list, f)
        if(len(answers) == 0):
            with open("testing/uscities.txt") as f:
                for line in f:
                    words = line.split(",")
                    if words[3] in abbr:
                        answers.append(abbr.index(words[3]))
            with open('testing/answers_list.pickle', 'wb') as f:
                pickle.dump(answers, f)


        test_answers = p.match_points_to_polygon(point_list)

        answers = np.asarray(answers)

        right = 0
        wrong = 0
        not_found = 0
        for i in range(len(answers)):
            if test_answers[i] == -1:
                not_found += 1
            else:
                if test_answers[i] != answers[i]:
                    wrong += 1
                else:
                    right += 1

        success_rate = right/(right + wrong)

        self.assertTrue(success_rate > 0.99)


    def test_large_list_variance(self):
        p = polypoint.PolygonClassifier()
        p.initialize()
        point_list = []
        answers = []
        with open("testing/uscities.txt") as file:
            for line in file:
                words = line.split(",")
                if words[3] in abbr:
                    t = (float(words[5]), float(words[6][:-1]))
                    point_list.append(t)
                    answers.append(abbr.index(words[3]))

        sample_times = []

        for i in range(100):
            r_sample = random.sample(point_list, int(len(point_list)/100))
            beg = time.time()
            test_answers = p.match_points_to_polygon(r_sample)
            end = time.time()
            sample_times.append(end - beg)

        variance = np.var(sample_times)
        
        self.assertTrue(variance < 0.01)
    

    def test_min_initialize(self):
        p = polypoint.PolygonClassifier()
        p.initialize_min()

        if os.path.exists('testing/large_point_list.pickle'):
            with open('testing/large_point_list.pickle', 'rb') as f:
                point_list = pickle.load(f)
        if os.path.exists('testing/answers_list.pickle'):
            with open('testing/answers_list.pickle', 'rb') as f:
                answers = pickle.load(f)
        
        test_answers = p.match_points_to_polygon(point_list[:100])
        self.assertCountEqual(test_answers, answers[:100])

if __name__ == "__main__":
    unittest.main()
