package math.polynomials;

import java.util.Objects;

public class Jdm implements Cloneable, Comparable<Jdm>, IFunct {
    private double coeff;
    private String letter;
    private int exp;

    public Jdm(double coeff, String letter, int exp) {
        this.coeff = coeff;
        this.letter = Objects.requireNonNullElse(letter, "x");
        this.exp = exp;
    }

    //----------------- overrides -------------------------------------------
    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (other == null) {
            return false;
        }
        if (getClass() != other.getClass()) {
            return false;
        }
        if (!(other instanceof Jdm)) {
            return false;
        }
        Jdm a = (Jdm) other;
        return (coeff == a.coeff) && (exp == a.exp)
                && (letter.equals(a.letter));
    }

    @Override
    public int hashCode() {
        return 17 * Double.valueOf(coeff).hashCode() + 19
                * (Integer.valueOf(exp).hashCode() * 23 * (letter.hashCode()));
    }

    @Override
    public String toString() {
        String result = null;
        if (coeff == 0) {
            result = "";
            return result;
        }
        switch (exp) {
            case 0:
                result = "" + coeff;
                break;
            case 1:
                if (coeff == 1 || coeff == -1) {
                    result = coeff == 1 ? "" + letter : "" + "-" + letter;
                } else {
                    result = "" + coeff + letter;
                }
                break;
            default:
                if (coeff == 1 || coeff == -1) {
                    result = coeff == 1 ? "" + letter + "^" + exp : "" + "-"
                            + letter + "^" + exp;
                } else {
                    result = "" + coeff + letter + "^" + exp;
                }
                break;
        }
        return result;
    }

    @SuppressWarnings("MethodDoesntCallSuperMethod")
    @Override
    public Jdm clone() {
        return new Jdm(coeff, letter, exp);
    }

    @Override
    public int compareTo(Jdm o) {
        if (!this.getLetter().equals(o.getLetter())) {
            return this.getLetter().compareTo(o.getLetter());
        }
        int a = this.getExp();
        int b = o.getExp();
        if (a > b) {
            return -1;
        }
        if (a < b) {
            return 1;
        }
        if (a == b) {
            if (this.getCoeff() < o.getCoeff()) {
                return -1;
            }
            if (this.getCoeff() > o.getCoeff()) {
                return 1;
            }
        }
        return 0;
    }

    //----------------- setters and getters ---------------------------------
    public double getCoeff() {
        return coeff;
    }

    public void setCoeff(double coeff) {
        this.coeff = coeff;
    }

    public String getLetter() {
        return letter;
    }

    public void setLetter(String letter) {
        this.letter = letter;
    }

    public int getExp() {
        return exp;
    }

    public void setExp(int exp) {
        this.exp = exp;
    }

    //----------------- instance methods ------------------------------------
    @Override
    public double getValue(double... x) {
        return coeff * Math.pow(x[0], exp);
    }

    public Jdm derivative() {
        return new Jdm(coeff * exp, letter, exp - 1);
    }

    /**
     * @param jdm
     * @return true jesli wielomiany sa podobne tzn róznia sie jedynie
     * wspolczynnikiem liczbowym coeff.
     */
    @SuppressWarnings("RedundantIfStatement")
    public boolean isSimilarTo(Jdm jdm) {
        if ((this.exp == jdm.exp) && (this.letter.equals(jdm.letter))) {
            return true;
        }
        return false;
    }

    //sprawdz czy jednomian jest do pozostawienia
    @SuppressWarnings("RedundantIfStatement")
    public boolean toRemain() {
        if (coeff == 0) {
            return false;
        }
        return true;
    }

    public int degree() {
        return exp;
    }

    public void add(Jdm jdm) {
        if (this.isSimilarTo(jdm)) {
            this.coeff += jdm.getCoeff();
        }
    }

    public void sub(Jdm jdm) {
        if (this.isSimilarTo(jdm)) {
            this.coeff -= jdm.getCoeff();
        }
    }

    public void mult(double coeff) {
        this.coeff *= coeff;
    }

    public void mult(Jdm jdm) throws PolyException {
        if (this.getLetter().equals(jdm.getLetter())) {
            this.coeff *= jdm.getCoeff();
            this.exp += jdm.getExp();
        } else {
            throw new PolyException("Jednomiany mają inną zmienną");
        }
    }

    public void div(double coeff) {
        this.coeff = this.coeff / coeff;
    }

    public void div(Jdm jdm) throws PolyException {
        if (PolyUtil.canDivide(this, jdm)) {
            this.coeff = this.coeff / jdm.getCoeff();
            this.exp = this.exp - jdm.getExp();
        } else {
            throw new PolyException("Nie można przeprowadzić dzielenia");
        }
    }
}
