package math.fractals;

import math.matrix.Matrix;
import math.matrix.MatrixException;
import math.matrix.MatrixUtil;
import math.utils.ArrayUtil;
import math.utils.MathUtil;
import math.utils.Tuple2d;

import java.awt.*;

public class FractalUtil {
    private FractalUtil() {
    }

    public static double lapunow(double rx, double ry, int[] tabl, int n) {
        double r, w, lapun;
        double N = 0.1;
        double suma = 0;
        int sum2 = 0;
        int index = 0;
        for (int i = 0; i < n; i++) {
            if (tabl[index] == 0) {
                r = rx;
            } else {
                r = ry;
            }
            index++;
            if (index == tabl.length) {
                index = 0;
            }
            N = N * r * (1 - N);
            w = Math.abs(r - 2 * r * N);
            if (w > 0) {
                suma = suma + Math.log(w);
                sum2++;
            }
        }
        if (sum2 > 0)
            lapun = suma / sum2;
        else
            lapun = 0;
        return lapun;
    }

    public static void drawCantorSet(Graphics2D g2, Color color, int steps,
                                     int width) {
        int x = 5;// punkt startu
        int y = 4;// punkt startu
        int wt = width - 2 * x;// szerokosc rysunku
        g2.setColor(color);
        for (int i = 0; i < steps; i++) {
            y += 20;
            double w = wt / Math.pow(3, i);// dlugosc odcinka
            int[] coeffs = coeffTable(i + 1);// tablica wspolczynnikow
            for (int coeff : coeffs) {
                g2.drawLine((int) (x + coeff * w), y, (int) (x + coeff
                        * w + w), y);
            }
        }
        g2.setColor(Color.BLACK);
    }

    // funkcja pomocnicza dla drawCantorSet
    private static int[] coeffTable(int steps) {
        int[] arr = null;
        if (steps == 1) {
            arr = new int[1];
            arr[0] = 0;
        } else {
            arr = new int[2];
            arr[0] = 0;
            arr[1] = 2;
            int x = 2;
            for (int i = 2; i < steps; i++) {
                int[] arr2 = ArrayUtil.clone(arr);
                for (int j = 0; j < arr2.length; j++) {
                    arr2[j] += x * 3;
                }
                arr = ArrayUtil.concat(arr, arr2);
                x *= 3;
            }
        }
        return arr;
    }

    public static void drawDragon(Graphics2D g2, Color color, Point start,
                                  Point end, int steps) {
        g2.setColor(color);
        BasicStroke stroke = new BasicStroke(2);
        g2.setStroke(stroke);
        if (steps == 0) {
            g2.drawLine(start.x, start.y, end.x, end.y);
        } else {
            Point v1 = new Point((start.x + end.x) / 2, (start.y + end.y) / 2);
            Point v2 = new Point(v1.x + end.y - v1.y, v1.y + start.x - v1.x);
            drawDragon(g2, color, end, v2, steps - 1);
            drawDragon(g2, color, start, v2, steps - 1);
        }
        g2.setColor(Color.BLACK);
    }

    public static Tuple2d[] calculateBarnsley(int steps) {
        Tuple2d[] arr = new Tuple2d[steps];
        arr[0] = new Tuple2d(0.0, 0.0);
        for (int i = 0; i < steps - 1; i++) {
            double rand1 = Math.random();
            double rand2 = Math.random();
            if (rand1 <= 0.01) {
                arr[i + 1] = new Tuple2d(0.0, 0.16 * arr[0].getX());
            } else if (rand1 <= 0.15) {
                if (rand2 <= 0.5) {
                    arr[i + 1] = new Tuple2d(-0.15 * arr[i].getX() + 0.28
                            * arr[i].getY(), 0.26 * arr[i].getX() + 0.24
                            * arr[i].getY() + 0.44);
                } else {
                    arr[i + 1] = new Tuple2d(0.2 * arr[i].getX() - 0.26
                            * arr[i].getY(), 0.23 * arr[i].getX() + 0.22
                            * arr[i].getY() + 1.6);
                }
            } else {
                arr[i + 1] = new Tuple2d(0.85 * arr[i].getX() + 0.04
                        * arr[i].getY(), -0.04 * arr[i].getX() + 0.85
                        * arr[i].getY() + 1.6);
            }
        }
        return arr;
    }

    public static double sum(double[] arr) {
        double sum = 0.0;
        for (double v : arr) {
            sum += v;
        }
        return sum;
    }

    public static double[] logs(double[] arr) {
        double[] array = new double[arr.length];
        for (int i = 0; i < arr.length; i++) {
            array[i] = MathUtil.lg(1.0 / arr[i]);
        }
        return array;
    }

    public static double[] logs2(double[] arr) {
        double[] array = new double[arr.length];
        for (int i = 0; i < arr.length; i++) {
            array[i] = MathUtil.lg(arr[i]);
        }
        return array;
    }

    public static double[] sqrs(double[] arr) {
        double[] array = new double[arr.length];
        for (int i = 0; i < arr.length; i++) {
            array[i] = arr[i] * arr[i];
        }
        return array;
    }

    public static double[] mult(double[] arr1, double[] arr2) {
        double[] array = new double[arr1.length];
        for (int i = 0; i < arr1.length; i++) {
            array[i] = arr1[i] * arr2[i];
        }
        return array;
    }

    public static Tuple2d solve(double[][] arr1, double[][] arr2) {
        Matrix matrix1 = new Matrix(arr1);
        Matrix matrix2 = new Matrix(arr2);
        Matrix matrix3 = MatrixUtil.reverse(matrix1);
        Matrix matrix4 = matrix3.multiply2(matrix2);
        double x = 0.0;
        double y = 0.0;
        try {
            x = matrix4.getCell(0, 0);
            y = matrix4.getCell(1, 0);
        } catch (MatrixException e1) {
            e1.printStackTrace();
        }
        return new Tuple2d(x, y);
    }
}
