#include <iostream>
#include <vector>
#include <string>

#define CSTR
#define DSTR

using   int_pair = std::pair< int, int >;
//----------------------------------------------------------------------------------------

struct                          POINT3D
{
    float   x, y, z;

    CSTR                        POINT3D() : x( 0 ), y( 0 ), z( 0 ) {}
    CSTR                        POINT3D( float x, float y, float z )
    :   x( x ), y( y ), z( z ) {}

    auto&                       operator=( const POINT3D& rhs )
    {
        x = rhs.x, y = rhs.y, z = rhs.z;
        return  *this;
    }

    friend  std::ostream&       operator<<( std::ostream& os, const POINT3D& p )
    {
        os << " x: " << p.x << std::endl;
        os << " y: " << p.y << std::endl;
        os << " z: " << p.z << std::endl;
        return  os;
    }
};
//----------------------------------------------------------------------------------------

enum    class                   DEVICE_TYPE
{
    sensor,
    motor,
};

class                           DEVICE_MANAGER;

///---------------------------------------------------------------------------------------

class                           DEVICE
{
friend  class                   DEVICE_MANAGER;
private:
    virtual void                refresh() = 0;
    const   DEVICE_TYPE         device_type;

protected:
    std::string                 name;
    virtual void                show() const = 0;

public:
    CSTR                        DEVICE(
        const std::string& name,
        const DEVICE_TYPE device_type )
    :   name( name )
    ,   device_type( device_type )
    {}
};
///---------------------------------------------------------------------------------------

class                           DEVICE_MANAGER
{
private:
    std::vector< DEVICE* >      devices;

public:
    CSTR                        DEVICE_MANAGER()
    {}
    DSTR                       ~DEVICE_MANAGER()
    {
        while( !devices.empty() )
        {
            delete  devices.back();
            devices.pop_back();
        }
    }

    void                        add( const DEVICE* device )
    {
        devices.push_back( (DEVICE*)device );
    }

    void                        refresh() const
    {
        for( auto* device : devices )
            device->refresh();
    }

    void                        show() const
    {
        for( const auto* device : devices )
            device->show();
    }

    template< typename T >
    void                        set( const T& value ) const;
};
//----------------------------------------------------------------------------------------

template< class T >
class                           SENSOR : public DEVICE
{
protected:
    T                           value;

    virtual void                show() const
    {
        std::cout << name << std::endl;
        std::cout << value << std::endl;
    }

public:
    CSTR                        SENSOR( DEVICE_MANAGER* owner, const std::string& name )
    :   DEVICE( name, DEVICE_TYPE::sensor )
    {
        owner->add( ( DEVICE* )this );
    }

    const   T                   get() const
    {
        return  value;
    }

    void                        set( const T& rvalue )
    {
        value = rvalue;
    }
};
///---------------------------------------------------------------------------------------

template< typename T >
inline  void                    DEVICE_MANAGER::set( const T& value ) const
{
    for( auto* device : devices )
    {
        if( device->device_type == DEVICE_TYPE::sensor )
            ( (SENSOR< T >*)device )->set( value );
    }
}
///---------------------------------------------------------------------------------------

class                           GYRO : public SENSOR< POINT3D >
{
protected:
    virtual void                refresh()
    {
        value.x = (float)rand() / rand();
        value.y = (float)rand() / rand();
        value.z = (float)rand() / rand();
    }

public:
    CSTR                        GYRO( DEVICE_MANAGER* owner, const int counter )
    :   SENSOR< POINT3D >( owner,
                           std::string( "Gyro_" ) + std::to_string( counter ) ) {}
};
///---------------------------------------------------------------------------------------

class                           ACCE : public SENSOR< POINT3D >
{
protected:
    virtual void                refresh()
    {
        value.x = (float)rand();
        value.y = (float)rand();
        value.z = (float)rand();
    }

public:
    CSTR                        ACCE( DEVICE_MANAGER* owner, const int counter )
    :   SENSOR< POINT3D >( owner,
                           std::string( "Accelero_" ) + std::to_string( counter ) ) {}
};

//----------------------------------------------------------------------------------------

inline  std::string             get_version_string( const int_pair& version )
{
    return  std::to_string( version.first ) + "." + std::to_string( version.second );
}

inline  std::string             get_dimension_string( const int_pair& dimension )
{
    return  std::to_string( dimension.first ) + "x" + std::to_string( dimension.second );
}

class                           SMART_PHONE
{
protected:
    DEVICE_MANAGER*             sensor_manager;

    virtual std::string         get_model_name() const = 0;
    virtual std::string         get_core_type() const = 0;
    virtual int                 get_core_speed_mhz() const = 0;
    virtual int                 get_core_count() const = 0;
    virtual int                 get_ram_size_gb() const = 0;
    virtual int                 get_flash_size_gb() const = 0;
    virtual std::string         get_ext_memory_type() const { return  "None"; }
    virtual int                 get_ext_memory_max_gb() const = 0;
    virtual std::string         get_os_type() const = 0;
    virtual int_pair            get_os_version() const = 0;
    virtual std::string         get_display_type() const = 0;
    virtual int_pair            get_display_matrix() const = 0;
    virtual int                 get_pixel_density_ppi() const = 0;
    virtual std::string         get_proof_type() const { return  "None"; }
    virtual int                 get_battery_cap_mah() const = 0;
    virtual std::string         get_removable_battery() const { return  "No"; }

public:
    CSTR                        SMART_PHONE()
    {
        sensor_manager = new DEVICE_MANAGER();
    }
    DSTR                       ~SMART_PHONE()
    {
        delete  sensor_manager;
    }

    void                        show_specification() const
    {
        using   namespace       std;

        cout << "Model              : " << get_model_name() << endl;
        cout << "Processor          : " << get_core_speed_mhz() / 1000.f << "GHz "
                                        << get_core_type() << endl;
        cout << "Core               : " << get_core_count() << endl;
        cout << "RAM                : " << get_ram_size_gb() << "GB" << endl;

        cout << "Int. storage       : " << get_flash_size_gb() << "GB" << endl;
        cout << "Ext. storage       : " << get_ext_memory_max_gb() << "GB" << endl;

        cout << "OS                 : " << get_os_type() << " "
                                        << get_version_string( get_os_version() ) << endl;
        cout << "Display            : " << get_display_type() << " ("
                                        << get_dimension_string( get_display_matrix() )
                                        << ")" << endl;
        cout << "Pixel density      : " << get_pixel_density_ppi() << " PPI" << endl;
        cout << "Water/Dust proof   : " << get_proof_type() << endl;
        cout << "Battery capacity   : " << get_battery_cap_mah() << "mAh" << endl;
        cout << "Removable battery  : " << get_removable_battery() << endl;

        cout << endl;
    }

    void                        show_sensor()
    {
        sensor_manager->show();
    }

    void                        refresh()
    {
        sensor_manager->refresh();
    }

    template< typename T >
    void                        set_sensor( const T& value )
    {
        sensor_manager->set( value );
    }
};

//---------------------------------------------------------------------------------------

class                           GALAXY_S5 : public SMART_PHONE
{
private:
    static  int                 counter;
    const   int                 battery_cap;

protected:
    virtual std::string         get_model_name() const
    {
        return  "Samsung Galaxy S5";
    }
    virtual std::string         get_core_type() const
    {
        return  "Snapdragon 801";
    }
    virtual int                 get_core_speed_mhz() const
    {
        return  2500;
    }
    virtual int                 get_core_count() const
    {
        return  4;
    }
    virtual int                 get_ram_size_gb() const
    {
        return  2;
    }
    virtual int                 get_flash_size_gb() const
    {
        return  16;
    }
    virtual std::string         get_ext_memory_type() const
    {
        return  "microSD";
    }
    virtual int                 get_ext_memory_max_gb() const
    {
        return  128;
    }
    virtual std::string         get_os_type() const
    {
        return  "Android";
    }
    virtual int_pair            get_os_version() const
    {
        return  std::make_pair( 4, 4 );
    }
    virtual std::string         get_display_type() const
    {
        return  "5.1\" Super AMOLED Full HD";
    }
    virtual int_pair            get_display_matrix() const
    {
        return  std::make_pair( 1920, 1080 );
    }
    virtual int                 get_pixel_density_ppi() const
    {
        return  432;
    }
    virtual std::string         get_proof_type() const
    {
        return  "IP67";
    }
    virtual int                 get_battery_cap_mah() const
    {
        return  battery_cap;
    }
    virtual std::string         get_removable_battery() const
    {
        return  "Yes";
    }

public:
    CSTR                        GALAXY_S5( const int battery_cap = 2800 )
    :   battery_cap( battery_cap )
    {
        new GYRO( sensor_manager, 0 );
        new ACCE( sensor_manager, 0 );
    }
};

int                             GALAXY_S5::counter = 0;

//---------------------------------------------------------------------------------------

class                           HTC_M8 : public SMART_PHONE
{
private:
    static  int                 counter;
    const int                   flash_size;

protected:
    virtual std::string         get_model_name() const
    {
        return  "HTC One (M8)";
    }
    virtual std::string         get_core_type() const
    {
        return  "Snapdragon 801";
    }
    virtual int                 get_core_speed_mhz() const
    {
        return  2500;
    }
    virtual int                 get_core_count() const
    {
        return  4;
    }
    virtual int                 get_ram_size_gb() const
    {
        return  2;
    }
    virtual int                 get_flash_size_gb() const
    {
        return  flash_size;
    }
    virtual std::string         get_ext_memory_type() const
    {
        return  "microSD (128GB)";
    }
    virtual int                 get_ext_memory_max_gb() const
    {
        return  128;
    }
    virtual std::string         get_os_type() const
    {
        return  "Android";
    }
    virtual int_pair            get_os_version() const
    {
        return  std::make_pair( 4, 4 );
    }
    virtual std::string         get_display_type() const
    {
        return  "5.0\" Super Clear LCD 3 Full HD";
    }
    virtual int_pair            get_display_matrix() const
    {
        return  std::make_pair( 1920, 1080 );
    }
    virtual int                 get_pixel_density_ppi() const
    {
        return  440;
    }
    virtual int                 get_battery_cap_mah() const
    {
        return  2600;
    }

public:
    CSTR                        HTC_M8( const int flash_size )
    :   flash_size( flash_size )
    {
        new GYRO( sensor_manager, 0 );
        new ACCE( sensor_manager, 0 );
    }
};

int                             HTC_M8::counter = 0;

//---------------------------------------------------------------------------------------

int main()
{
    SMART_PHONE*    phones[] { new GALAXY_S5, new HTC_M8( 32 ) };

    for( const auto* phone : phones )
        phone->show_specification();

    for( auto* phone : phones )
    {
        phone->refresh();
        phone->show_sensor();
        phone->set_sensor( POINT3D( 0, 0, 0 ) );
        phone->show_sensor();
    }

    return  0;
}
//---------------------------------------------------------------------------------------
