#define MSGS_OFF

#include <iostream.h>
#include <math.h>

#include <graphics.h>
#include <stdlib.h>
#include <conio.h>
#include <dos.h>

int maxx;
int maxy;

class Celestial
{
protected:
   double X, Y;
   int Size;

   virtual void Show(int) = 0;
   virtual void Recalc() = 0;

public:
   Celestial(double px, double py, int psize);
   double getX() const { return X; }
   double getY() const { return Y; }
   int getSize() const { return Size; }

   void Update();
};

Celestial :: Celestial(double px, double py, int psize) :
             X(px), Y(py), Size(psize)
{
#ifdef MSGS
   cout << "Celestial created (" << X << " " << Y
        << " " << Size << " )" << endl;
#endif
}

void Celestial :: Update()
{
   Show(0);
   Recalc();
   Show(1);
}


double dist(Celestial &a, Celestial &b)
{
   double dx = a.getX() - b.getX();
   double dy = a.getY() - b.getY();
   return sqrt(dx * dx + dy * dy);
}

class Star : public Celestial
{
public:
   Star(int psize) : Celestial(maxx / 2, maxy / 2, psize)
   {
   #ifdef MSGS
      cout << "star created" << endl;
   #endif
   }

   virtual void Show(int);
   virtual void Recalc() {}
};

void Star :: Show(int bShow)
{
   setcolor( bShow ? YELLOW : getbkcolor() );
   circle((int)X, (int)Y, Size);
}

Star *sun;

class Planet : public Celestial
{
private:
   int R, Angle;
   const char *name;

protected:
   virtual void Recalc() {}

public:
   Planet(double px, double py, int psize,
          int pr, int pangle, const char *pname);

   virtual void Show(int);
};

Planet :: Planet(double px, double py, int psize,
                 int pr, int pangle, const char *pname) :
                 Celestial(px, py, psize),
                 R(pr), Angle(pangle), name(pname)
{
   #ifdef MSGS
      cout << "planet " << name << " created ("
           << R << " " << Angle << " )" << endl;
   #endif
}

void Planet :: Show(int bShow)
{
   setcolor( bShow ? LIGHTGREEN : getbkcolor() );
   circle((int)X, (int)Y, Size);
}


class Particle : public Celestial
{
private:
   double Vx, Vy;

public:
   Particle(double px, double py, double pvx, double pvy);
   ~Particle();

   virtual void Show(int);
   virtual void Recalc();
};

Particle :: Particle(double px, double py, double pvx, double pvy) :
            Celestial(px, py, 1),
            Vx(pvx), Vy(pvy)
{
   #ifdef MSGS
      cout << "particle created : Vx = " << Vx << " Vy = " << Vy << endl;
   #endif
}

Particle :: ~Particle()
{
   Show(0);
}

void Particle :: Show(int bShow)
{
   putpixel( (int)X, (int)Y, bShow ? LIGHTGRAY : getbkcolor() );
}

void Particle :: Recalc()
{
   Vx += ((getX() - sun->getX()) / dist(*sun, *this));
   Vy += ((getY() - sun->getY()) / dist(*sun, *this));
   X += Vx;
   Y += Vy;
}


const int parrSize = 1500;
const int stepParticles = 15;
const int maxPartSpeed = 3;


class Comet : public Celestial
{
private:
   double Ex, Ey;

   int Vx, Vy;
   Particle *trace[parrSize];
   int maxParticles;

private:
   int Cvt(int curr);

protected:
   virtual void Recalc();

public:
   Comet(double px, double py, int psize, int pvx, int pvy);
   virtual void Show(int);

};

Planet *earth;
Comet *c_01;

int Comet :: Cvt(int curr)
{
   int sign;
   if(!curr) sign = 0;
   else sign = curr / abs(curr);

   return sign * random(maxPartSpeed - abs(curr));
}


Comet :: Comet(double px, double py, int psize, int pvx, int pvy) :
         Celestial(px, py, psize),
         Vx(pvx), Vy(pvy), maxParticles(100)
{
   Ex = (sun->getX() - sun->getSize()) / maxPartSpeed;
   Ey = (sun->getY() - sun->getSize()) / maxPartSpeed;


   for(int i = 0; i < maxParticles; i++)
   {
      double dx = (px - sun->getX());
      double dy = (py - sun->getY());
      #ifdef MSGS
         cout << "i = " << i << " Dx = " << dx << " Dy = " << dy << endl;
      #endif
      trace[i] = new Particle(px - 1 + random(2), py - 1 + random(2),
                              Vx + Cvt((int)(dx / Ex)) - 1 + random(2),
                              Vy + Cvt((int)(dy / Ey)) - 1 + random(2));
   }

   #ifdef MSGS
      cout << "comet created (" << Vx << " " << Vy << " )" << endl;
      cout << "dist to sun = " << dist(*sun, *this) << endl;
   #endif
}

void Comet :: Recalc()
{
   X += Vx;
   Y += Vy;
   #ifdef MSGS
      cout << "comet : dist to sun = " << dist(*sun, *this) << endl;
   #endif

   int count = 0;
   for(int i = 0; i < maxParticles; i++)
   {
      trace[i]->Recalc();
      double d = dist(*trace[i], *this);

      #ifdef MSGS
         cout << "i = " << i << " Dist = " << d << endl;
      #endif

      if(d > 40 * Size)
      {
         delete trace[i];
         count += 1;

         trace[i] = new Particle((int)(getX()) - 1 + random(2), (int)(getY()) - 1 + random(2),
                                 Vx + Cvt((int)((getX() - sun->getX()) / Ex)) - 1 + random(2),
                                 Vy + Cvt((int)((getY() - sun->getY()) / Ey)) - 1 + random(2));
      }
   }

   #ifdef MSGS
      cout << count << " new particles created" << endl;
   #endif
   for(i = 0; i <= stepParticles - count; i++)
   {
      trace[maxParticles + i] = new Particle((int)(getX()) - 1 + random(2), (int)(getY()) - 1 + random(2),
                                    Vx + Cvt((int)((getX() - sun->getX()) / Ex)) - 1 + random(2),
                                    Vy + Cvt((int)((getY() - sun->getY()) / Ey)) - 1 + random(2));
   }
   if(stepParticles - count > 0)
      maxParticles += (stepParticles - count);

   #ifdef MSGS
      cout << maxParticles << " currently. Max = " << parrSize << endl;
   #endif
}

void Comet :: Show(int bShow)
{
   setcolor( bShow ? WHITE : getbkcolor() );
   circle((int)X, (int)Y, Size);

   for(int i = 0; i < maxParticles; i++)
      trace[i]->Show(bShow);
}


class Observer
{
public:
   Observer();
   ~Observer();

   void Run();
};

Observer :: Observer()
{
   int gdriver = DETECT, gmode, errorcode;
   initgraph(&gdriver, &gmode, "");

   errorcode = graphresult();
   if(errorcode != grOk)
   {
      cerr << "Graphics error: " << grapherrormsg(errorcode) << endl;
      exit(-1);
   }
   maxx = getmaxx(); maxy = getmaxy();
   randomize();
}

Observer :: ~Observer()
{
   closegraph();
}

void Observer :: Run()
{
   while(1)
   {
      sun->Update();
      earth->Update();
      c_01->Update();

      if(kbhit())
      {
         if(getch() == 27) break;
      }

      delay(150);
   }
}

int main()
{
   Observer obj;

   sun = new Star(10);
   earth = new Planet(maxx / 2., maxy / 2. + 3 * 20, 3,
                      maxy / 2. + 3 * 20, 0, "earth");
   c_01 = new Comet(maxx / 2. - 50, maxy, 2, 0, -5);

   obj.Run();
   return 0;
}