# -*- coding: utf-8 -*-
"""
Plik gmshClass.py zawiera klasę Part, której obiekty potrzebne są w celu stworzenia pliku wsadowego do GMSH'a.
"""
from itertools import count
import numpy as np

class Part(dict):
    r"""
    Klasa Part służy do tworzenia obiektów zawierających definicje
    pozwalające napisać plik wsadowy do programu GMSH.

    :param nazwa: nazwa konstrukcji która będzie analizowana
    :type nazwa: string
    """

    def __init__(self,nazwa):
#=====================================================================
=========
#     KONSTRUKTOR    
#==============================================================================
        # Implementacja automatycznego naliczania powstajacych obiektow
        self.p_ind = count(0)
        self.l_ind = count(1)
        self.ll_ind = count(1)
        self.r_ind = count(1)

        # Kontener zawierajacy 
nazwe obiektu     
        self['nazwa'] = nazwa        
        # Kontener zawierajacy definicje geometrii        
        self['czesci'] = []
        # Kontener, ktory zostaje wypelniony po uruchomieniu metody
        # writeInputFile(). Zawiera on caly plik wsadowy do programu GMSH
        self['txt'] = ''
    
#==============================================================================
#     CZESCI GEOMETRII
#==============================================================================
    def point(self,x,y,z):
        """
        Metoda pośrednia służąca tworzeniu punktów na podstawie
        współrzędnych x,y,z

        :param x: współrzędna X punktu
        :param y: współrzędna Y punktu
        :param z: współrzędna Z punktu
        :type x: float
        :type y: float
        :type z: float
        """
        self.p_id = self.p_ind.next()
        p = Point(x,y,z,self.p_id)
        self['czesci'].append(p)
            
    def circle(self,x,y,z):
        """
        Metoda pośrednia służąca do stworzenia pojedynczych łuków.

        :param x: indeks początkowego punktu
        :param y: indeks środkowego punktu
        :param z: indeks końcowego punktu

        :type x: int
        :type y: int
        :type z: int
        """
        self.l_id = self.l_ind.next()
        k = Circle(x,y,z,self.l_id)
        self['czesci'].append(k)
    
    def manyCircles(self,srodek,kola):
        """
        Metoda pośrednia służąca do tworzenia okręgu na podstawie punktu
        środkowego, oraz punktów na obwodzie.

        :param srodek: indeks punktu środkowego
        :type srodek: int
        :param kola: lista zawierająca obiekty typu :class:`Point`.
        :type kola: list
        """
        n = range(0,len(kola))
        for i in n:
            self.l_id = self.l_ind.next()
            t = Circle(kola[i].id,
                       srodek,
                       kola[(i + 1) % len(kola)].id, self.l_id)
            self['czesci'].append(t)

    def line(self,*args):
        """
        Metoda pośrednia służąca do tworzenia obiektów Line.

        :param args: lista zawierająca indeksy punktów, które mają być ze sobą połączone za pomocą linii.
        :type args: list
        """
        self.l_id = self.l_ind.next()
        k = Line(self.l_id,args)
        self['czesci'].append(k)

    def spline(self,*args):
        """
        Metoda pośrednia służąca do tworzenia obiektów Spline.

        :param args: lista zawierająca indeksy punktów, które mają być ze sobą połączone za pomocą splajnu.
        :type args: list
        """
        self.l_id = self.l_ind.next()
        k = Spline(self.l_id,args)
        self['czesci'].append(k)

    def lloop(self,*args):
        """
        Metoda pośrednia służąca do tworzenia obiektów Line Loop.

        :param args: lista zawierająca indeksy, które mają być zawarte w obiekcie LineLoop.
        :type args: list
        """
        self.ll_id = self.ll_ind.next()
        k = LLoop(self.ll_id,args)
        self['czesci'].append(k)

    def psurf(self,*args):
        """
        Metoda pośrednia służąca do tworzenia obiektów Plane Surface.

        :param args: lista zawierająca indeks obiektu LineLoop, który służy jako baza do stworzenia powierzchni.
        :type args: list
        """
        self.r_id = self.r_ind.next()
        k = PSurface(self.r_id,args)
        self['czesci'].append(k)

    def rsurf(self,*args):
        """
        Metoda pośrednia służąca do tworzenia obiektów Ruled Surface.

        :param args: lista zawierająca indeks obiektu LineLoop, który służy jako baza do stworzenia powierzchni.
        :type args: list
        """
        self.r_id = self.r_ind.next()
        k = RSurface(self.r_id,args)
        self['czesci'].append(k)

    def physical(self,nazwa,*args):
        """
        Metoda pośrednia służąca do tworzenia obiektów typu Physical (zbiorów elementów skończonych w pliku wsadowym).

        :param nazwa: nazwa obiektu jaki ma zostać stworzony. Przykładowo, jeżeli nazwa jest równa 'Surface' to zostanie zbiór elementów na płaszczyźnie.
        :type nazwa: string
        :param args: lista zawierająca indeksy obiektów (linii lub powierzchni) dla których ma zostać stworzony zbiór.
        :type args: list
        """
        self.l_id = self.l_ind.next()
        k = Physical(self.l_id,nazwa,args)
        self['czesci'].append(k)
    
#==============================================================================
#     METODY
#==============================================================================
    def text(self,text):
        """
        Metoda wymagana do implementowania dowolnej komendy lub komentarza w pliku wsadowym.

        :param text: tekst, który zostanie umieszczony w pliku wsadowym
        :type text: string
        """
        self['czesci'].append(text+'\n')
    
    def writeInputFile(self):
        """
        Metoda służąca do stworzenia pliku wsadowego do GMSH'a. Procedura polega na wydrukowaniu kontenera zawartego w obiekcie klasy :class:`Part` do pliku.
        """
        for i in self['czesci']:
            self['txt'] += str(i)
    
    def prStart(self):
        """
        Metoda służąca do wstawienia wskaźnika, dzięki któremu użytkownik
        może grupować powstające obiekty. Wskaźnik zapamiętuje długość słownika
        części

        :return: zwróć długość słownika zawartego w klasie :class:`Part` w konkretnym momencie kodu.
        :rtype: int
        """
        # Ustaw poczatek naliczania
        prStart = len(self['czesci'])
        # Zwróć wskaźnik        
        return prStart
    
    def prEnd(self,prStart):
        """
        Metoda dzięki, dzięki której uzyskujemy dostęp do poszczególnych
        obiektów. Rezultat ten jest otrzymywany poprzez tworzenie wektora
        obiektów na podstawie indeksu początkowego 'prStart', oraz indeksu
        'prEnd.'

        :param prStart: zmienna określająca długość kontenera w momencie gdy został użyty wskaźnik.
        :rtype: int
        """
        
        # Ustaw koniec naliczania
        prEnd = len(self['czesci'])
        # Stworz liste punktow
        vec = self['czesci'][prStart:prEnd]
        return vec
    
    def rotation(self,punkt,iloscPunktow,theta=0.0, z=0.0):
        """
        Stwórz nowe punkty poprzez rotację punktu wokół punktu (0.0,0.0,0.0). Funkcja dodaje nowo powstałe punkty do obiektu Part. Dodatkowe opcje:

        * zmiana wartości 'z' - zmień współrzędna początkową 'z'
        * zmiana wartości :math:`\theta` - rozpocznij rotację z kątem początkowym różnym od zera.

        :param punkt: obiekt zawierający trzy współrzędne punktu który ma zostać obrócony.
        :type punkt: Point / list
        :param iloscPunktow: ilość współrzędnych, które mają zostać zwrócone.
        :type iloscPunktow: int

        """
        # Podziel 360 stopni na ilosc punktow ktore maja powstac        
        ob = (2*np.pi)/float(iloscPunktow)

        # Funkcja służąca rotacji punktow wobec dowolnej osi
        def rot(theta,rotPoint):
            x = rotPoint[0]
            z = rotPoint[1]
            rotPoint[0] = x*np.cos(theta) - z*np.sin(theta)
            rotPoint[1] = x*np.sin(theta) + z*np.cos(theta)
            return rotPoint
        
        # Oblicz wspolrzedne punktow powstalyc