#include <iostream>
#include <string>
#include <sstream>

using namespace std;

class Ware {
  public:
    Ware(string name, float quantity, float price);
    virtual void setQuantity(float quantity);
    virtual void setPrice(float price);
    virtual float getQuantity() const;
    virtual float getPrice() const;
    float getValue() const;
    virtual string details() const = 0;
  protected:
    string name;
    int quantity;   // grams
    int price;      // cents
};

class WareByPiece: public Ware {
  public:  
    WareByPiece(string name, float quantity, float price);
    void setQuantity(float quantity);
    string details() const;
};

class WareByWeight: public Ware {
  public:    
    WareByWeight(string name, float quantity, float price);
    string details() const;
};

class Bread: public WareByPiece {
  public:    
    Bread(float quantity, float price) :
            WareByPiece("Bread", quantity, price) {};
};

class Olive: public WareByPiece {
  public:    
    Olive(float quantity, float price) :
            WareByPiece("Olive", quantity, price) {};
};

class Apples: public WareByWeight {
  public:    
    Apples(float quantity, float price) :
            WareByWeight("Apples", quantity, price) {};
};

class Oranges: public WareByWeight {
  public:    
    Oranges(float quantity, float price) :
            WareByWeight("Oranges", quantity, price) {};
};


Ware::Ware(string name, float quantity, float price) :
        name(name) {
    this->setQuantity(quantity);    // PROBLEM! virtual method in constructor
    this->setPrice(price);          // PROBLEM! virtual method in constructor
};


void Ware::setQuantity(float quantity) {
    if (quantity < 0) {
        cerr << "ERROR: Ware: quantity cannot be negative" << endl;
    } else
        this->quantity = quantity * 1000;
}

void Ware::setPrice(float price) {
    if (price < 0) {
        cerr << "ERROR: Ware: price cannot be negative" << endl;
    } else
        this->price = price * 100;
}

float Ware::getQuantity() const {
    return (float)this->quantity / 1000;
}

float Ware::getPrice() const {
    return (float)this->price / 100;
}

float Ware::getValue() const {
    return (float)((int)((float)(this->price * this->quantity + 500) / 1000)) / 100;
}

WareByPiece::WareByPiece(string name, float quantity, float price) :
        Ware(name, quantity, price) {};


void WareByPiece::setQuantity(float quantity) {
    if ((int)(quantity) != quantity) {
        cerr << "ERROR: WareByPiece: quantity must be integer" << endl;
    } else
        Ware::setQuantity(quantity);
};

string WareByPiece::details() const {
    stringstream s;
    
    s << this->name << ": ";
    s << this->getQuantity() << " at ";
    s << this->getPrice() << "PLN for ";
    s << this->getValue() << "PLN";
    
    return s.str();
}


WareByWeight::WareByWeight(string name, float quantity, float price) :
        Ware(name, quantity, price) {};

string WareByWeight::details() const {
    stringstream s;
    
    s << this->name << ": ";
    s << this->getQuantity() << "kg at ";
    s << this->getPrice() << "PLN for ";
    s << this->getValue() << "PLN";
    
    return s.str();
}


int main() {

    Ware w;


    Ware *basket[4];
    float total = 0;
    
    basket[0] = new Bread(2.5, 4.80);
    basket[1] = new Olive(3, 12.50);
    basket[2] = new Apples(1.51, 3.25);    
    basket[3] = new Oranges(1.8, 6.40);

    for (int i = 0; i < 4; i++) {
        cout << basket[i]->details() << endl;
        total += basket[i]->getValue();
    }
    
    cout << "Total: " << total << "PLN" << endl;
    
    return 0;
}
