
struct C_vec
{
  float dx,dy,dz;
};

struct C_Color
{
  float r,g,b,a;
  C_Color() : r(0.0), g(0.0), b(0.0), a(1.0) { }
  void operator*=(float val) { r*=val; g*=val; b*=val; }
};

struct C_Surface
{
  float light;
  C_Color color;
};

struct C_Sphere
{
  float pos_x,pos_y,pos_z;
  float radius;
  // surface properties
  C_surface surf;
};
struct C_Point
{
  float x,y,z;
};

float distance(C_Point p1, C_Point p2)
{
  float xx = p1.x-p2.x;
  float yy = p1.y-p2.y;
  float zz = p1.z-p2.z;
  return sqrt(xx*xx+yy*yy+zz*zz);
}

class ObjectOutput
{
public:
  virtual C_Point get_center() const=0;
  virtual float get_radius() const=0;
  virtual C_Surface get_surf(C_vec dir) const=0;
};

C_Sphere sphere_array[] = {
			   { 0.0, 0.0, 0.0, 100.0, 0.0, 255.0,255.0,255.0,255.0 },
			   { 0.0, 200.0,0.0, 100.0, 0.0, 255.0,255.0,255.0,2550.0 }
};

class SphereOutput : public ObjectOutput
{
public:
  SphereOutput(int index) : index(index) { }
  C_Surface get_surf(C_vec dir) const {
    return sphere_array[index].surf;
  }
private:
  int index;
};


class PixelProperties
{
public:
  virtual int Num() const=0;
  virtual C_Point pos(int i) const=0;
  virtual float radius(int i) const=0;
  virtual C_Surface output(int i) const=0;
  virtual float mult(int i) const=0;
};

C_Color pixel_color(PixelProperties &prop)
{
  int s = prop.Num();
  C_Color res;
  for(int i=0;i<s;i++) {
    float intensity = prop.radius(i)*prop.mult(i);
    C_Color col = prop.output(i).color;
    col*=intensity;
    res+=col;
  }
  return res;
}

class ScenePixelProperties : public PixelProperties
{
public:
  ScenePixelProperties(float x, float y, float z, float n_x, float n_y, float n_z, ObjectOutput *array, int num) : x(x),y(y),z(z),n_x(n_x),n_y(n_y),n_z(n_z), array(array), num(num) { }
  virtual int Num() const
  {
    return num;
  }
  virtual C_Point pos(int i) const
  {
    C_Point pt = array[i].get_center();
    pt.x-=x; pt.y-=y; pt.z-=z;
    float d = sqrt(pt.x*pt.x+pt.y*pt.y+pt.z*pt.z);
    pt.x/=d;
    pt.y/=d;
    pt.z/=d;
    return pt;
  }
  virtual float radius(int i) const
  {
    float radius = array[i].get_radius();
    C_Point pt = array[i].get_center();
    pt.x-=x; pt.y-=y; pt.z-=z;
    float d = sqrt(pt.x*pt.x+pt.y*pt.y+pt.z*pt.z);
    radius/=d;
    return radius;
  }
  virtual C_Surface output(int i) const
  {
    C_Point pt = array[i].get_center();
    float xx = x-pt.x;
    float yy = y-pt.y;
    float zz = z-pt.z;

    pt.x-=x; pt.y-=y; pt.z-=z;
    float d = sqrt(pt.x*pt.x+pt.y*pt.y+pt.z*pt.z);
    xx/=d;
    yy/=d;
    zz/=d;
    C_vec vec = { xx,yy,zz };
    return array[i]->get_surf(vec);
  }
  virtual float mult(int i) const
  {
    float mult = 1.0;
    C_Point pt2 = array[i]->get_center();
    float r2 = array[i]->get_radius();

    pt2.x-=x;
    pt2.y-=y;
    pt2.z-=z;

    float d2 = sqrt(pt2.x*pt2.x+pt2.y*pt2.y+pt2.z*pt2.z);

    
    int s = num;
    for(int ii=0;ii<s;ii++)
      {
	if (ii==i) continue;
	ObjectOutput *obj = array[ii];
	C_Point pt = obj->get_center();
	float r = obj->get_radius();

	pt.x-=x;
	pt.y-=y;
	pt.z-=z;
	float d = sqrt(pt.x*pt.x+pt.y*pt.y+pt.z*pt.z);

	float r_3 = r2*d/d2;
	C_Point pt3 = pt2;
	pt3.x*=d/d2;
	pt3.y*=d/d2;
	pt3.z*=d/d2;
	float dist = distance(pt3,pt);
	if (dist>r_3) continue;
	float cover = 1.0-dist/(r_3);
	cover*=r;
	cover/=r2;
	mult-=cover;
      }
    if (mult<0.0) mult=0.0;
    if (mult>1.0) mult=1.0;
    return mult;
  }
private:
  float x,y,z,n_x,n_y,n_z;
  ObjectOutput *array;
  int num;
};

