package shapes77.canvas;
// Příliš žluťoučký kůň úpěl ďábelské ó - PŘÍLIŠ ŽLUŤOUČKÝ KŮŇ ÚPĚL ĎÁBELSKÉ Ó
// J:/77_JAVA/shapes77/canvas/Triangle.java

import shapes77.geom.Area;
import shapes77.geom.Direction8;
import shapes77.geom.IDirectable;
import shapes77.geom.Position;
import shapes77.geom.Size;
import shapes77.util.IColorable;
import shapes77.util.NamedColor;



/*******************************************************************************
 * Instance třídy {@code Trojúhelník} představují trojúhelníky
 * určené pro práci na virtuálním plátně
 * při prvním seznámení s třídami a objekty.
 * Instance jsou definované svojí pozicí, rozměrem, barvou a směrem.
 * Pozicí instance se přitom rozumí
 * pozice levého horního rohu opsaného obdélníku
 * a rozměrem rozměr tohoto obdélníku.
 * Směr trojúhelníku je pak definován jako směr,
 * do nějž je natočen jeho hlavní vrchol.
 */
public   class Triangle
    implements ICanvasShape, IColorable, IDirectable
{
//\CC== CLASS CONSTANTS (CONSTANT CLASS/STATIC ATTRIBUTES/FIELDS) ==============

    /** Počáteční barva nakreslené instance v případě,
     *  kdy uživatel žádnou požadovanou barvu nezadá -
     *  pro elipsu {@link NamedColor#GREEN}. */
    public static final NamedColor DEFAULT_COLOR = NamedColor.GREEN;

    /** Plátno, na které se bude instance kreslit. */
    private static final Canvas CANVAS;

    /** Kreslítko, jehož pomocí je možno na plátno kreslit. */
//    private static final Canvas.Painter PAINTER;

    /** Směr, kam bude ukazovat vrchol trojúhelníku v případě,
     *  když uživatel žádný preferovaný směr nezadá.    */
    public static final Direction8 DEFAULT_DIRECTION = Direction8.NORTH;



//\CV== CLASS VARIABLES (VARIABLE CLASS/STATIC ATTRIBUTES/FIELDS) ==============

    /** Počet vytvořených instancí. */
    private static int count = 0;



//##############################################################################
//\CI== CLASS (STATIC) INITIALIZER (CLASS CONSTRUCTOR) =========================

    static {
        CANVAS  = Canvas.getInstance();
//        PAINTER = Canvas.Painter.get();
    }



//\CF== CLASS (STATIC) FACTORY METHODS =========================================
//\CG== CLASS (STATIC) GETTERS AND SETTERS =====================================
//\CM== CLASS (STATIC) REMAINING NON-PRIVATE METHODS ===========================
//\CP== CLASS (STATIC) PRIVATE AND AUXILIARY METHODS ===========================



//##############################################################################
//\IC== INSTANCE CONSTANTS (CONSTANT INSTANCE ATTRIBUTES/FIELDS) ===============

    /** ID instance = pořadí vytvoření dané instance v rámci třídy. */
    private final int ID = ++count;

    /** Vodorovné (x-ové) souřadnice vrcholů trojúhelníku. */
    private final int[] xPoints = new int[3];

    /** Svislé (y-ové) souřadnice vrcholů trojúhelníku. */
    private final int[] yPoints = new int[3];



//\IV== INSTANCE VARIABLES (VARIABLE INSTANCE ATTRIBUTES/FIELDS) ===============

    /** Výchozí název instance sestávající z názvu třídy
     *  následovaného znakem podtržení a ID instance. */
    private String name = getClass().getSimpleName() + "_" + ID;

    /** Vodorovná (x-ová) souřadnice instance. */
    private int xPos;

    /** Svislá (y-ová) souřadnice instance. */
    private int yPos;

    /** Šířka instance. */
    protected int width;

    /** Výška instance. */
    protected int height;

    /** Barva instance. */
    private NamedColor color;

    /** Směr, do nějž je natočen hlavní vrchol trojúhelníku. */
    private Direction8  dir8;



//##############################################################################
//\II== INSTANCE INITIALIZERS (CONSTRUCTORS) ===================================

    /***************************************************************************
     * Vytvoří instanci s implicitním umístěním, rozměry, barvou
     * a natočením.
     * Instance bude umístěna v levém horním rohu plátna
     * a bude mít implicitní barvu,
     * výšku rovnu kroku a šířku dvojnásobku kroku plátna
     * a bude natočena vrcholem na sever.
     */
    public Triangle()
    {
        this(0, 0, 2*Canvas.getStep(), Canvas.getStep());
    }


    /***************************************************************************
     * Vytvoří instanci se zadanou pozicí a rozměry
     * a implicitní barvou a směrem natočení.
     * Pozice instance je přitom definována jako pozice
     * levého horního rohu opsaného obdélníku,
     * a rozměr instance jako rozměr tohoto obdélníku.
     *
     * @param x       Vodorovná (x-ová) souřadnice instance,
     *                x=0 má levý okraj plátna, souřadnice roste doprava
     * @param y       Svislá (y-ová) souřadnice instance,
     *                y=0 má horní okraj plátna, souřadnice roste dolů
     * @param width   Šířka vytvářené instance, šířka &gt;= 0
     * @param height  Výška vytvářené instance, výška &gt;= 0
     */
    public Triangle(int x, int y, int width, int height)
    {
        this(x, y, width, height, DEFAULT_COLOR, DEFAULT_DIRECTION);
    }


    /***************************************************************************
     * Vytvoří instanci se zadanou pozicí, rozměry a barvou.
     * Směr natočení bude implicitní, tj. na sever.
     * Pozice instance je přitom definována jako pozice
     * levého horního rohu opsaného obdélníku,
     * a rozměr instance jako rozměr tohoto obdélníku.
     * Směr trojúhelníku je definován jako směr,
     * do nějž má být natočen jeho hlavní vrchol.
     *
     * @param x       Vodorovná (x-ová) souřadnice instance,
     *                x=0 má levý okraj plátna, souřadnice roste doprava
     * @param y       Svislá (y-ová) souřadnice instance,
     *                y=0 má horní okraj plátna, souřadnice roste dolů
     * @param width   Šířka vytvářené instance, šířka &gt;= 0
     * @param height  Výška vytvářené instance, výška &gt;= 0
     * @param color   Barva vytvářené instance
     */
    public Triangle(int x, int y, int width, int height, NamedColor color)
    {
        this(x, y, width, height, color, DEFAULT_DIRECTION);
    }


    /***************************************************************************
     * Vytvoří instanci se zadanou pozicí, rozměry a směrem natočení
     * a s implicitní barvou.
     * Pozice instance je přitom definována jako pozice
     * levého horního rohu opsaného obdélníku,
     * a rozměr instance jako rozměr tohoto obdélníku.
     * Směr trojúhelníku je definován jako směr,
     * do nějž má být natočen jeho hlavní vrchol.
     *
     * @param x         Vodorovná (x-ová) souřadnice instance,
     *                  x=0 má levý okraj plátna, souřadnice roste doprava
     * @param y         Svislá (y-ová) souřadnice instance,
     *                  y=0 má horní okraj plátna, souřadnice roste dolů
     * @param width     Šířka vytvářené instance, šířka &gt;= 0
     * @param height    Výška vytvářené instance, výška &gt;= 0
     * @param direction Směr, do nějž má být natočen hlavní vrchol
     */
    public Triangle(int x, int y, int width, int height, Direction8 direction)
    {
        this(x, y, width, height, DEFAULT_COLOR, direction);
    }


    /***************************************************************************
     * Vytvoří instanci se zadanou pozicí, rozměry, barvou,
     * i směrem natočení.
     * Pozice instance je přitom definována jako pozice
     * levého horního rohu opsaného obdélníku,
     * a rozměr instance jako rozměr tohoto obdélníku.
     * Směr trojúhelníku je definován jako směr,
     * do nějž má být natočen jeho hlavní vrchol.
     *
     * @param x         Vodorovná (x-ová) souřadnice instance,
     *                  x=0 má levý okraj plátna, souřadnice roste doprava
     * @param y         Svislá (y-ová) souřadnice instance,
     *                  y=0 má horní okraj plátna, souřadnice roste dolů
     * @param width     Šířka vytvářené instance, šířka &gt;= 0
     * @param height    Výška vytvářené instance, výška &gt;= 0
     * @param color     Barva vytvářené instance
     * @param direction Směr, do nějž má být natočen hlavní vrchol
     */
    public Triangle(int x, int y, int width, int height,
                    NamedColor color, Direction8 direction)
    {
        //Verification of arguments
        if ((width <= 0)  ||  (height <= 0)  ||  (color == null)  ||
            (direction == null)  ||  (direction == Direction8.NOWHERE))
        {
            throw new IllegalArgumentException(
                "\nnew Triangle: Parametry nemají povolené hodnoty: x=" +
                x + ", y=" + y + ", width=" + width + ", height=" + height +
                ", color=" + color + ", direction=" + direction);
        }

        //Parametry akceptovány, můžeme tvořit
        this.xPos  = x;
        this.yPos  = y;
        this.width = width;
        this.height= height;
        this.color = color;
        this.dir8  = direction;

        paintPrivate();
    }


    /***************************************************************************
     * Vytvoří instanci se zadanou pozicí, rozměry, barvou,
     * i směrem natočení.
     * Pozice instance je přitom definována jako pozice
     * levého horního rohu opsaného obdélníku,
     * a rozměr instance jako rozměr tohoto obdélníku.
     * Směr trojúhelníku je definován jako směr,
     * do nějž má být natočen jeho hlavní vrchol.
     *
     * @param position  Pozice vytvářené instance
     * @param size      Rozměr vytvářené instance
     * @param color     Barva vytvářené instance
     * @param direction Směr, do nějž má být natočen hlavní vrchol
     */
    public Triangle(Position position, Size size, NamedColor color,
                                                  Direction8 direction)
    {
        this(position.x(), position.y(), size.width(), size.height(), color, direction);
    }


    /***************************************************************************
     * Vytvoří instanci vyplňující zadanou oblast
     * a mající zadanou barvu a směr natočení.
     * Směr trojúhelníku je definován jako směr,
     * do nějž má být natočen jeho hlavní vrchol.
     *
     * @param area      Oblast, kterou má vytvářená instance zaujmout
     * @param color     Barva vytvářené instance
     * @param direction Směr, do nějž má být natočen hlavní vrchol
     */
    public Triangle(Area area, NamedColor color, Direction8 direction)
    {
        this(area.x(), area.y(), area.width(), area.height(), color, direction);
    }



//\IA== INSTANCE ABSTRACT METHODS ==============================================
//\IG== INSTANCE GETTERS AND SETTERS ===========================================

    /***************************************************************************
     * Vrátí aktuální barvu instance.
     *
     * @return Instance třídy {@link NamedColor} definující
     *         aktuálně nastavenou barvu
     */
    @Override
    public final NamedColor getColor()
    {
        return color;
    }


    /***************************************************************************
     * Nastaví novou barvu instance.
     *
     * @param color  Požadovaná nová barva
     */
    @Override
    public final void setColor(NamedColor color)
    {
        this.color = color;
        paint();
    }


    /***************************************************************************
     * Vrátí směr, do nějž je instance otočena.
     * Směr trojúhelníku je definován jako směr,
     * do nějž je natočen jeho hlavní vrchol.
     *
     * @return  Instance třídy {@code Direction8} definující
     *          aktuálně nastavený směr
     */
    @Override
    public final Direction8 getDirection()
    {
        return dir8;
    }


    /***************************************************************************
     * Otočí instanci do zadaného směru.
     * Směr trojúhelníku je definován jako směr,
     * do nějž je natočen jeho hlavní vrchol.
     *
     * @param direction Nastavovaný směr
     */
    @Override
    public final void setDirection(Direction8 direction)
    {
        assert (direction != Direction8.NOWHERE)
             : "Trojúhelník musí být natočen do některého z řádných směrů";
        if (direction != dir8) {
            NamedColor org = this.color;
            rubOut();
            dir8 = direction;
            paint();
        }
    }


    /***************************************************************************
     * Vrátí název instance.
     * Výchozí podoba názvu názvu sestává z názvu třídy
     * následovaného znakem podtržení a ID instance.
     * Název je ale možné kdykoliv změnit.
     *
     * @return  Název instance
     */
    public String getName()
    {
        return name;
    }


    /***************************************************************************
     * Nastaví nový název instance.
     *
     * @param name  Nový název instance
     */
    public void setName(String name)
    {
        this.name = name;
    }


    /***************************************************************************
     * Vrátí x-ovou (vodorovnou) souřadnici pozice instance,
     * tj. vodorovnou souřadnici levého horního rohu opsaného obdélníku.
     *
     * @return  Aktuální vodorovná (x-ová) souřadnice instance,
     *          x=0 má levý okraj plátna, souřadnice roste doprava
     */
    @Override
    public int getX()
    {
        return xPos;
    }


    /***************************************************************************
     * Vrátí y-ovou (svislou) souřadnici pozice instance,
     * tj. svislou souřadnici levého horního rohu opsaného obdélníku.
     *
     * @return  Aktuální svislá (y-ová) souřadnice instance,
     *          y=0 má horní okraj plátna, souřadnice roste dolů
     */
    @Override
    public int getY()
    {
        return yPos;
    }


    /***************************************************************************
     * Přemístí instanci na zadanou pozici.
     * Pozice instance je přitom definována jako pozice
     * levého horního rohu opsaného obdélníku.
     *
     * @param x  Nově nastavovaná vodorovná (x-ová) souřadnice instance,
     *           x=0 má levý okraj plátna, souřadnice roste doprava
     * @param y  Nově nastavovaná svislá (y-ová) souřadnice instance,
     *           y=0 má horní okraj plátna, souřadnice roste dolů
     */
    @Override
    public void setPosition(int x, int y)
    {
        rubOut();
        xPos = x;
        yPos = y;
        paint();
    }


    /***************************************************************************
     * Vrátí šířku instance v bodech.
     * Šířka instance jsou přitom definována jako šířka
     * opsaného obdélníku.
     *
     * @return  Aktuální šířka instance v bodech
     */
    @Override
    public int getWidth()
    {
        return width;
    }


    /***************************************************************************
     * Vrátí výšku instance v bodech.
     * Výška instance jsou přitom definována jako výška
     * opsaného obdélníku.
     *
     * @return  Aktuální výška instance v bodech
     */
    @Override
    public int getHeight()
    {
        return height;
    }


    /***************************************************************************
     * Nastaví nové rozměry instance.
     * Rozměry instance jsou přitom definovány jako rozměry
     * opsaného obdélníku.
     * Nastavované rozměry musí být nezáporné,
     * místo nulového rozměru se nastaví rozměr rovný jedné.
     *
     * @param width    Nově nastavovaná šířka; šířka &gt;= 0
     * @param height   Nově nastavovaná výška; výška &gt;= 0
     */
    @Override
    public void setSize(int width, int height)
    {
        if ((width < 0) || (height < 0)) {
            throw new IllegalArgumentException(
                            "\nRozměry nesmějí být záporné: width="
                          + width + ", height=" + height);
        }
        rubOut();
        this.width  = width;
        this.height = height;
        paint();
    }



//\IM== INSTANCE REMAINING NON-PRIVATE METHODS =================================

    /***************************************************************************
     * Vrátí kopii dané instance,
     * tj. stejně velkou, umístěnou a natočenou instanci stejné barvy.
     *
     * @return Požadovaná kopie
     */
    @Override
    public Triangle copy()
    {
        if (this instanceof ICanvasShape) {
            return new Triangle(xPos, yPos, width, height, color, dir8);
        }
        else {
            //Pomocná třída zabezpečující správnou funkci i v případě,
            //kdy třída nebude implementovat rozhraní ICanvasShape
            class TT extends Triangle implements ICanvasShape {
                TT(int x, int y, int s, int v, NamedColor b, Direction8 d) {
                    super(x, y, s, v, b, d);
                }
                @Override public TT copy() { return (TT)(super.copy()); }
            }
            return new TT(xPos, yPos, width, height, color, dir8);
        }
    }


    /***************************************************************************
     * Zobrazí svoji instanci, tj.vykreslí její obraz na plátno.
     */
    @Override
    public void paint()
    {
        paintPrivate();
    }


    /***************************************************************************
     * Smaže obraz své instance z plátna (nakreslí ji barvou pozadí plátna).
     */
    @Override
    public final void rubOut()
    {
        int[][] points = getVertexes();
        CANVAS.erase(new java.awt.Polygon(points[0], points[1], 3));
    }


    /***************************************************************************
     * Vrátí textový podpis instance, tj. její řetězcovou reprezentaci.
     * Používá se především při ladění.
     *
     * @return Název instance následovaný jejími souřadnicemi,
     *         rozměry a barvou
     */
    @Override
    public String toString()
    {
        return name + "[x=" + xPos + ", y=" + yPos
             + ", width=" + width + ", height=" + height
             + ", color=" + color + ", direction=" + dir8 + "]";
    }



//\IP== INSTANCE PRIVATE AND AUXILIARY METHODS =================================

    /***************************************************************************
     * Vykreslí obraz své instance na plátno.
     * Je definována proto, aby umožnila korektní provedení operace
     * v konstruktoru, přestože metoda {@link #paint()} je virtuální.
     */
    private void paintPrivate()
    {
        int[][] points = getVertexes();
        CANVAS.fill(new java.awt.Polygon(points[0], points[1], 3), color);
    }


    /***************************************************************************
     * Vrátí matici se souřadnicemi jednotlivých vrcholů trojúhelníku.
     *
     * @return Matice se souřadnicemi
     */
    private int[][] getVertexes()
    {
        int[] xpoints = null;
        int[] ypoints = null;

        switch(dir8)
        {
            case EAST:
                xpoints = new int[]{ xPos,  xPos + (width),    xPos };
                ypoints = new int[]{ yPos,  yPos + (height/2),  yPos + height };
                break;

            case NORTH_EAST:
                xpoints = new int[]{ xPos,  xPos + width,  xPos + width };
                ypoints = new int[]{ yPos,  yPos,          yPos + height };
                break;

            case NORTH:
                xpoints = new int[]{ xPos,          xPos + (width/2), xPos + width };
                ypoints = new int[]{ yPos + height, yPos,             yPos + height };
                break;

            case NORTH_WEST:
                xpoints = new int[]{ xPos,          xPos,  xPos + width };
                ypoints = new int[]{ yPos + height,  yPos,  yPos         };
                break;

            case WEST:
                xpoints = new int[]{ xPos,              xPos + width, xPos + width };
                ypoints = new int[]{ yPos + (height/2), yPos,         yPos + height };
                break;

            case SOUTH_WEST:
                xpoints = new int[]{ xPos,  xPos,           xPos + width };
                ypoints = new int[]{ yPos,  yPos + height,  yPos + height };
                break;

            case SOUTH:
                xpoints = new int[]{ xPos,  xPos + (width/2),  xPos + width };
                ypoints = new int[]{ yPos,  yPos + height,     yPos,        };
                break;

            case SOUTH_EAST:
                xpoints = new int[]{ xPos,           xPos +width,    xPos + width };
                ypoints = new int[]{ yPos + height,  yPos + height,  yPos         };
                break;

            default:
                throw new IllegalStateException(
                    "Instance points to a no existing direction");
        }
        return new int[][] { xpoints, ypoints };
    }



//##############################################################################
//\NT== NESTED DATA TYPES ======================================================
}
