# OrderedPair.py — Cross Training: Classes & OOP
# Import in ordered_pair_main.py with: from OrderedPair import OrderedPair
#
# OOP vocabulary this file demonstrates:
#   class           — blueprint keyword (same as Java, JavaScript)
#   __init__        — Python's constructor; called for EVERY new object
#   self            — Python's 'this'; must be explicit in every method signature
#   _x, _y          — underscore prefix = private by convention (not enforced by language)
#   @classmethod    — factory method; Python's substitute for constructor overloading
#   cls             — first param of a classmethod; receives the class itself
#   __str__         — Python's toString(); called automatically by print() and str()
#   math.sqrt()     — from the math module; equivalent to Java's Math.sqrt()
# ─────────────────────────────────────────────────────────────────────
#
# KEY DIFFERENCE FROM JAVA: Python has exactly ONE __init__ per class.
# Java has multiple overloaded constructors. Python substitutes
# @classmethod factory methods for the "copy constructor" pattern.
# Every instance method must explicitly list 'self' as its first parameter.

import math

class OrderedPair:

    def __init__(self, x=0.0, y=0.0):
        """Default constructor (x=0.0, y=0.0) and two-parameter constructor combined."""
        print("...constructor...")
        self._x       = x
        self._y       = y
        self._abs_val = self._compute_abs_val()
        self._label   = "O" if (x == 0.0 and y == 0.0) else "P"

    @classmethod
    def from_ordered_pair(cls, orig):
        """Python's substitute for Java's copy constructor.
        @classmethod receives the class as 'cls'; delegates to __init__ for base setup.
        """
        print("...copy constructor (classmethod)...")
        instance        = cls(orig._x, orig._y)
        instance._label = orig._label + "_transpose"
        return instance

    # Override — called automatically by print(obj) and str(obj)
    def __str__(self):
        return self._label + "(" + str(self._x) + ", " + str(self._y) + ")"

    # Getters (accessors)
    def get_abs_val(self): return self._abs_val
    def get_label(self):   return self._label

    # Setter (mutator)
    def set_label(self, lbl):
        print("...set_label...")
        self._label = lbl

    # Private utility method — underscore prefix by convention
    def _compute_abs_val(self):
        print("...compute_abs_val...")
        return math.sqrt(self._x ** 2 + self._y ** 2)

    def transpose(self):
        print("...transpose...")
        self._x, self._y = self._y, self._x   # Pythonic swap; no temp variable needed

#end class OrderedPair
