import math
from typing import Any, Optional, Tuple

import numpy as np
from numpy import ndarray


def cylinder_area(r: float, h: float) -> float:
    """Obliczenie pola powierzchni walca. 
    Szczegółowy opis w zadaniu 1.
    
    Parameters:
    r (float): promień podstawy walca 
    h (float): wysokosć walca
    
    Returns:
    float: pole powierzchni walca 
    """
    nan = float('nan')
    if r > 0 and h > 0:
        p = 2 * math.pi * r * r + 2 * math.pi * r * h
        return p
    else:
        return nan


def fib(n: int) -> np.ndarray or None:
    """Obliczenie pierwszych n wyrazów ciągu Fibonnaciego. 
    Szczegółowy opis w zadaniu 3.
    
    Parameters:
    n (int): liczba określająca ilość wyrazów ciągu do obliczenia 
    
    Returns:
    np.ndarray: wektor n pierwszych wyrazów ciągu Fibonnaciego.
    """
    if type(n) is int:
        vector_of_fib_numbers = np.empty(0, int)
        a_1 = 1
        a_2 = 1
        a_wyn = 0

        if n > 1:
            vector_of_fib_numbers = np.append(vector_of_fib_numbers, [a_1, a_2])
            for i in range(1, n):
                a_wyn = a_2 + a_1

                a_1 = a_2
                a_2 = a_wyn

                vector_of_fib_numbers = np.append(vector_of_fib_numbers, a_2)

            return vector_of_fib_numbers
        elif n == 1:
            vector_of_fib_numbers = np.append(vector_of_fib_numbers, a_1)
            return vector_of_fib_numbers
        else:
            return None

    else:
        return None


def matrix_calculations(a: float) -> Tuple[Any]:
    """Funkcja zwraca wartości obliczeń na macierzy stworzonej 
    na podstawie parametru a.  
    Szczegółowy opis w zadaniu 4.
    
    Parameters:
    a (float): wartość liczbowa 
    
    Returns:
    touple: krotka zawierająca wyniki obliczeń 
    (Minv, Mt, Mdet) - opis parametrów w zadaniu 4.
    """
    matrix = np.array([[a, 1, -a],
                       [0, 1, 1],
                       [-a, a, 1]])
    matrix_det = np.linalg.det(matrix)
    matrix_transponsed = np.transpose(matrix)

    if matrix_det != 0:
        matrix_inv = np.linalg.inv(matrix)
        t = matrix_inv, matrix_transponsed, matrix_det
    else:
        t = float('nan'), matrix_transponsed, matrix_det

    print(t)
    return t


def custom_matrix(m: int, n: int) -> Optional[ndarray]:
    """Funkcja zwraca macierz o wymiarze mxn zgodnie
    z opisem zadania 7.

    Parameters:
    m (int): ilość wierszy macierzy
    n (int): ilość kolumn macierzy

    Returns:
    np.ndarray: macierz zgodna z opisem z zadania 7.
    """
    if m > 0 and n > 0 and type(m) is int and type(n) is int:
        matrix = np.zeros((m, n))
        for i in range(m):
            for j in range(n):
                if i > j:
                    matrix[i, j] = i
                else:
                    matrix[i, j] = j

        return matrix
    else:
        return None




