// Geom. Objekte: Statische Member: drawAll (einfache Version mit List)
// Header für abstrakte Basisklasse "GraObj"
//
// Klaus Kusche, 2020

#ifndef _GRAOBJ_H
#define _GRAOBJ_H

#include "color.h"

// Wir verwenden zur Speicherung aller Objekte den vordefinierten list-Container
// (intern eine normale, doppelt verkettete Liste)
#include <list>

using namespace std;

class GraObj
{
  public:
    GraObj(const Color &color, int x, int y, int w, int h,
          int sx = 0, int sy = 0)
    : mRGB(color), mX(x), mY(y), mW(w), mH(h), mSX(sx), mSY(sy),
      // statt push_back oder push_front unten:
      // Merk dir zum schnelleren Rauslöschen den Iterator,
      // der auf das eigene Listen-Element zeigt
      // (push_back und push_front liefern keinen Returnwert,
      // aber insert liefert einen Iterator auf das soeben eingefügte Element)
      mMyEntry(sObjects.insert(sObjects.end(), this))
      //mMyEntry(sObjects.insert(sObjects.begin(), this))
    {
      // ersetzt durch insert oben:
      // hänge das neu erzeugte Objekt (bzw. einen Pointer darauf)
      // an die Liste aller Objekte hinten an
      // ==> Objekte werden in der Reihenfolge des Anlegens gezeichnet,
      // alte Objekte zuunterst, neue Objekte obenauf
      //sObjects.push_back(this);
      // alternativ: Vorne anhängen ==> umgekehrte Reihenfolge
      //sObjects.push_front(this);
    }

    // Auch der Copy-Konstruktor muss das Objekt in sObjects eintragen
    GraObj(const GraObj &orig)
    : mRGB(),  // Standard-Konstruktor statt Kopie der Original-Farbe
      mX(orig.mX), mY(orig.mY), mW(orig.mW), mH(orig.mH),
      mSX(orig.mSX), mSY(orig.mSY),
      // analog zu oben
      mMyEntry(sObjects.insert(sObjects.end(), this))
      //mMyEntry(sObjects.insert(sObjects.begin(), this))
    {
      //sObjects.push_back(this);
      //sObjects.push_front(this);
    }

    // ... und der Destruktor muss das Objekt aus sObjects rauslöschen
    virtual ~GraObj()
    {
      // ursprüngliche Variante:
      // Suche das Element mit dem Wert this in der Liste (langsam!!!)
      // und lösche es
      //sObjects.remove(this);
      // optionale, verbesserte Variante (schneller):
      // Lösche das Element direkt über den gemerkten Iterator darauf
      sObjects.erase(mMyEntry);
    }

    virtual GraObj *clone() const = 0;

    Color getColor() const { return mRGB; }
    int getX() const { return mX; }
    int getY() const { return mY; }
    int getW() const { return mW; }
    int getH() const { return mH; }
    int getSX() const { return mSX; }
    int getSY() const { return mSY; }

    void setColor(const Color &color)
    {
      mRGB = color;
      draw();
    }

    void setSpeed(int sx, int sy)
    {
      mSX = sx; mSY = sy;
    }
    
    void setPos(int x, int y);
    void move(int dx, int dy);
    void setSize(int w, int h);
    void scale(int percentX, int percentY);

    bool fly(bool bounce = false);

    virtual void draw() = 0;
    virtual void undraw() = 0;

    // Klassenweite Methode: Zeichne alle Objekte neu, in Z-Reihenfolge
    // (wird nicht für ein bestimmtes Objekt aufgerufen,
    // sondern für die ganze Klasse)
    static void drawAll();
  
  protected:
    Color mRGB;
    int mX, mY;
    int mW, mH;
    int mSX, mSY;

  // abgeleitete Klassen dürfen nicht am Verzeichnis aller Objekte herumfummeln
  private:
    static list<GraObj *> sObjects;  // die klassenweite Liste aller Objekte
    // optional: Iterator auf das eigene Listenelement zum schnelleren Löschen
    list<GraObj *>::iterator mMyEntry; 
};

#endif
