'''
Created on 30-Jun-2020

@author: bthakur

'''

import re
import pathlib
from pathlib import Path
import os
import sys
import time
import random
import traceback
import csv
import inspect
import matplotlib as mpl
import matplotlib.pyplot as plt
from celluloid import Camera
from xvgui import _Base
from enum import Enum
from abc import ABC, abstractmethod

class Container(_Base):
    def __init__(self, **args):
        super().__init__(**args)
        self.items = list()

        pass
    #def __init__(self, verbose = False):

    pass
# class Container(_Base):

class Shape(_Base, ABC):
    class Type(Enum):
        POINT = "Point"
        LINESEGMENT = "LineSegment"
        POLYGON = "Polygon"
        TRIANGLE = "Triangle"
        QUADRILATERAL = "Quadrilateral"

    def __init__(self, **args):
        super().__init__(**args)
        self.X = []
        self.Y = []
        self.validate()
        self.initXY()

        pass
    #def __init__(self, verbose = False):

    @abstractmethod
    def validate(self):
        pass

    @abstractmethod
    def initXY(self):
        pass

    def draw(self, **args):
        fig, ax = plt.subplots()

        if len(self.X) < 1:
            raise Exception("The X and Y are missing")
            pass

        if len(self.X) != len(self.X) :
            raise Exception("The X and Y are not having same length")
            pass

        if len(self.X) == 1:
            ax.plot(self.X[0], self.Y[0], 'bo')
            pass

        for i in range(len(self.X) - 1):
            x = [self.X[i], self.X[i+1]]
            y = [self.Y[i], self.Y[i+1]]
            plt.plot(x, y)

        ax.axis('off')
        fig.canvas.set_window_title(type(self).__name__)
        plt.show()
        pass

    #@abstractmethod
    def animate(self, **args):
        pass
    #def animate(self):

    pass
#class Shape(ABC):

class Polygon(Shape):

    def validate(self):
        if not "x" in self.keys:
            raise Exception("x is missing.")

        if not "y" in self.keys:
            raise Exception("y is missing.")

        type_x = type(self.x).__name__
        type_y = type(self.y).__name__

        if type_x  not in ['list', 'tuple']:
            raise Exception("x must be a list or a tuple.")

        if type_y  not in ['list', 'tuple']:
            raise Exception("y must be a list or a tuple.")

        if len(self.x) != len(self.y):
            raise Exception("x and y must have same length.")

        if len(self.x) < 3:
            raise Exception("x and y must have at least three values.")

        return True
        pass
    #def validate(self):

    def initXY(self):
        self.X = list(self.x)
        self.X.append(self.X[0])

        self.Y = list(self.y)
        self.Y.append(self.Y[0])
        pass
    #def initXY(self):

    pass
#class Polygon(Shape):

class Point(Shape):
    def validate(self):
        if not "x" in self.keys:
            raise Exception("x is missing.")

        if not "y" in self.keys:
            raise Exception("y is missing.")

        return True
        pass

    def initXY(self):
        self.X = [self.x]
        self.Y = [self.y]
        pass
    #def initXY(self):


    def animate(self):
        plt.style.use('ggplot')

        #fig = plt.figure()
        fig, ax = plt.subplots()

        camera = Camera(fig)
        for i in range(20):

            self.x = self.x + 1
            self.y = self.y + 1

            t = self.draw(ax = ax, camera = camera)


            camera.snap()
        animation = camera.animate()
        animation.save('celluloid_legends.mp4')
        pass
    #def animate(self):


    pass

class LineSegment(Shape):
    def validate(self):

        if not "x1" in self.keys:
            raise Exception("x1 is missing.")

        if not "y1" in self.keys:
            raise Exception("y1 is missing.")

        if not "x2" in self.keys:
            raise Exception("x2 is missing.")

        if not "y2" in self.keys:
            raise Exception("y2 is missing.")


        pass

    def initXY(self):
        self.X = [self.x1, self.x2]
        self.Y = [self.y1, self.y2]
        pass
    #def initXY(self):


    pass
#sclass LineSegment(Shape):

class Triangle(Polygon):
    def initXY(self):
        super().initXY()

        if not (len(self.X) == 4 or len(self.Y)  == 4):
            raise Exception("The x and y should have exactly three coordinates each.")
        pass
    #def initXY(self):

    pass

class Quadrilateral(Polygon):
    def initXY(self):
        super().initXY()

        if not (len(self.X) == 5 or len(self.Y)  == 5):
            raise Exception("The x and y should have exactly four coordinates each.")
        pass
    #def initXY(self):
    pass

class ShapeFactory():
    @classmethod
    def get(cls, shapeType = None,  **args):

        if shapeType:
            if type(shapeType).__name__ != "Type":
                raise Exception("Invalid shapeType {}".format(type(shapeType)))

            print("You want a shape of {} ".format(shapeType.value))

            shapeClass = getattr(sys.modules[__name__], shapeType.value)

            print(shapeClass)

            return shapeClass(**args)

        return Polygon(**args)
        pass

    pass
#class ShapeFactory():

class _TEST_ShapeManager:

    @classmethod
    def test_POINT(cls):
        print("\nFunction {} start\n#############\n".format(inspect.stack()[0][3]))

        sf = ShapeFactory.get(shapeType = Shape.Type.POINT,
                              x = 10,
                              y = 7
                            )


        sf.draw()
        #sf.animate()
        #Point(x = 2, y = 3).draw()

        print("\nFunction {} over\n#############\n".format(inspect.stack()[0][3]))
        pass
    #def test_POINT():


    @classmethod
    def test_LINESEGMENT(cls):
        print("\nFunction {} start\n#############\n".format(inspect.stack()[0][3]))

        sf = ShapeFactory.get(shapeType = Shape.Type.LINESEGMENT,
                              x1 = 10,
                              y1 = 7,
                              x2 = 15,
                              y2 = 10
                            )

        sf.draw()

        print("\nFunction {} over\n#############\n".format(inspect.stack()[0][3]))
        pass
    #def test_LINESEGMENT():

    @classmethod
    def test_POLYGON(cls):
        print("\nFunction {} start\n#############\n".format(inspect.stack()[0][3]))

        x = [1, 2, 3, 4, 3]
        y = [1, 3, 2, 1, 7]

        sf = ShapeFactory.get(shapeType = Shape.Type.POLYGON,
                              x = x,
                              y = y
                            )

        sf.draw()

        print("\nFunction {} over\n#############\n".format(inspect.stack()[0][3]))
        pass
    #def test_POLYGON():

    @classmethod
    def test_TRIANGLE(cls):
        print("\nFunction {} start\n#############\n".format(inspect.stack()[0][3]))

        x = [1, 2, 3]
        y = [1, 3, 2]

        sf = ShapeFactory.get(shapeType = Shape.Type.TRIANGLE,
                              x = x,
                              y = y
                            )

        sf.draw()

        print("\nFunction {} over\n#############\n".format(inspect.stack()[0][3]))
        pass
    #def test_TRIANGLE():

    @classmethod
    def test_QUADRILATERAL(cls):
        print("\nFunction {} start\n#############\n".format(inspect.stack()[0][3]))

        x = [1, 2, 3, 2]
        y = [1, 3, 2, -1]

        sf = ShapeFactory.get(shapeType = Shape.Type.QUADRILATERAL,
                              x = x,
                              y = y
                            )

        sf.draw()

        print("\nFunction {} over\n#############\n".format(inspect.stack()[0][3]))
        pass
    #def test_QUADRILATERAL():

    pass
#class _TEST_ShapeManager:

if __name__ == "__main__":
    #print(type(Shape.Type.TRIANGLE))

    #_TEST_ShapeManager.test_POINT()
    #_TEST_ShapeManager.test_LINESEGMENT()
    #_TEST_ShapeManager.test_TRIANGLE()
    #_TEST_ShapeManager.test_QUADRILATERAL()
    #_TEST_ShapeManager.test_POLYGON()


    #Point(x = 10, y =2).draw()
    #LineSegment(x1 = 10, y1 =2, x2 = 5, y2 = 3).draw()
    #Triangle(x = [1, 2, 5], y = [-1, 2, 3]).draw()
    #Polygon(x = [1, 2, 5], y = [-1, 2, 3]).draw()
    #Quadrilateral(x = [1, 2, 3, 5], y = [-1, 2, 3, 6]).draw()
    #Polygon(x = [1, 2, 5, 5], y = [-1, 2, 3, 6]).draw()
    Polygon(x = [1, 2, -3, 5, -3], y = [-1, 2, 3, 6, -3]).draw()


    pass










