//(P)erhaps (C)lown (N)onsense (I)s (C)onstructive
//(O)nly (T)oward (G)etting (S)ettlers (U)nderwear
//
// A well-formed Java class has 10 anatomy elements:
//   P = public     C = class      N = class Name
//   I = Ivars      C = Constructors (overloaded)
//   O = Overridden toString
//   T = (part of OT pair — toString method)
//   G = Getters    S = Setters    U = Utility methods
//
// This class USES OrderedPair (composition) for the vertex
// and USES ComplexOrderedPair (via inheritance chain) for complex roots.
public class Quadratic {

    // ── IVARS ─────────────────────────────────────────────────── I
    private double a;              // leading coefficient (must ≠ 0)
    private double b;              // linear coefficient
    private double c;              // constant term
    private double discriminant;   // b²-4ac: computed, never set directly
    private OrderedPair vertex;    // vertex point — another class as an ivar!

    // ── CONSTRUCTORS ──────────────────────────────────────────── C
    // default: f(x) = x²
    public Quadratic(){
        System.out.println("...Quadratic default constructor...");
        a = 1.0;
        b = 0.0;
        c = 0.0;
        computeDiscriminant();
        computeVertex();
    }//end default constructor

    // full constructor
    public Quadratic(double a, double b, double c){
        System.out.println("...Quadratic full constructor...");
        if (a == 0) throw new IllegalArgumentException("a cannot be zero in a quadratic");
        this.a = a;
        this.b = b;
        this.c = c;
        computeDiscriminant();
        computeVertex();
    }//end full constructor

    // copy constructor
    public Quadratic(Quadratic orig){
        System.out.println("...Quadratic copy constructor...");
        this.a = orig.a;
        this.b = orig.b;
        this.c = orig.c;
        computeDiscriminant();
        computeVertex();
    }//end copy constructor

    // ── TOSTRING ──────────────────────────────────────────────── O/T
    @Override
    public String toString(){
        return "f(x) = " + a + "x\u00B2 + " + b + "x + " + c
             + " | vertex: " + vertex
             + " | disc: " + discriminant;
    }//end toString

    // ── GETTERS ───────────────────────────────────────────────── G
    public double     getA()            { return a;            }
    public double     getB()            { return b;            }
    public double     getC()            { return c;            }
    public double     getDiscriminant() { return discriminant; }
    public OrderedPair getVertex()      { return vertex;       }

    // ── SETTERS (each validates, then triggers side-effect recompute) ── S
    public void setA(double a){
        System.out.println("...setA...");
        if (a == 0) throw new IllegalArgumentException("a cannot be zero");
        this.a = a;
        computeDiscriminant();   // ← side effect — disc depends on a
        computeVertex();         // ← side effect — vertex depends on a
    }//end setA

    public void setB(double b){
        System.out.println("...setB...");
        this.b = b;
        computeDiscriminant();
        computeVertex();
    }//end setB

    public void setC(double c){
        System.out.println("...setC...");
        this.c = c;
        computeDiscriminant();
        computeVertex();
    }//end setC

    // ── UTILITY METHODS ───────────────────────────────────────── U
    // evaluate f(x) = ax² + bx + c at a given x
    public double f(double x){
        return a * x * x + b * x + c;
    }//end f

    // discriminant: b²-4ac
    private void computeDiscriminant(){
        System.out.println("...computeDiscriminant...");
        discriminant = b * b - 4 * a * c;
    }//end computeDiscriminant

    // vertex: h = -b/(2a),  k = f(h)
    private void computeVertex(){
        System.out.println("...computeVertex...");
        double h = -b / (2 * a);
        double k = f(h);
        vertex = new OrderedPair(h, k);
        vertex.setLabel("V");
    }//end computeVertex

    // roots — uses ComplexOrderedPair when discriminant < 0
    public String getRootsDescription(){
        if (discriminant > 0){
            double r1 = (-b + Math.sqrt(discriminant)) / (2 * a);
            double r2 = (-b - Math.sqrt(discriminant)) / (2 * a);
            return "Two real roots: x = " + r1 + "  and  x = " + r2;
        } else if (discriminant == 0){
            double r = -b / (2 * a);
            return "One repeated root: x = " + r;
        } else {
            // disc < 0: ComplexOrderedPair enters the picture!
            double realPart = -b / (2 * a);
            double imagPart = Math.sqrt(-discriminant) / (2 * a);
            ComplexOrderedPair root1 = new ComplexOrderedPair(realPart,  imagPart);
            ComplexOrderedPair root2 = new ComplexOrderedPair(realPart, -imagPart);
            return "Complex roots: " + root1 + "  and  " + root2;
        }
    }//end getRootsDescription

}//end class Quadratic
