#include <iostream>
#include <vector>
#include <utility>
#include <functional>
#include <string>
#include <sstream>
#include <algorithm>
#include <set>

namespace sportstracker {
    namespace algorithms {

        #include <functional>

        namespace detail {

            template< typename T >
            size_t divizer( std::vector< T >& array, size_t start, size_t end,  std::function< bool( T, T )> comp )
            {
                auto marker = start;
                for( auto i = start; i <= end; i++ ) {
                    if( comp( array[ i ], array[ end ] ) ) {
                        std::swap( array[ marker ] ,array[ i ] );
                        marker++;
                    }
                }
                return marker - 1;
            }

            template< typename T >
            void quick_sort( std::vector< T >& array, size_t start, size_t end, std::function< bool( T, T )> comp )
            {
                if( start >= end )
                    return;

                auto pivot = divizer< T >( array, start, end, comp );

                quick_sort( array, start    , pivot - 1, comp );
                quick_sort( array, pivot + 1, end      , comp );
            }
        }

        /*
        * Sorting vector in increasing order by default.
        ***/
        template< typename T >
        void quick_sort( std::vector< T >& array )
        {
            if( array.size() < 2 )
                return;
            detail::quick_sort( array, 0, array.size() - 1, []( T a, T b ) -> bool { return ( a <= b )? true : false; } );
        }

        /*
        * Sorting vector using comparator
        ***/
        template< typename T >
        void quick_sort( std::vector< T >& array, std::function< bool( T, T )> comp )
        {
            if( array.size() < 2 )
                return;
            detail::quick_sort( array, 0, array.size() - 1, comp );
        }
    }

    namespace utils {

        struct Nocopy {
            Nocopy() {}
            Nocopy( const Nocopy& ) = delete;
            Nocopy& operator=( const Nocopy& ) = delete;
        };

        std::string year_postfix( int yaer )
        {
            auto t1 = yaer % 10;
            auto t2 = yaer % 100;

            if( t1 == 1 && t2 != 11 ) {
                return "год";
            }

            if( t1 >= 2 && t1 <= 4 && ( t2 < 10 || t2 >= 20 ) ) {
                return "года";
            }
            else {
                return "лет";
            }
        }

    }

    namespace football {

        class IPosition {
        public:
            IPosition() {}
            IPosition( const IPosition& ) {}

            virtual ~IPosition() {}

            virtual int         id  () { return 0; }
            virtual std::string name() { return ""; }
        };

        class PosGoalkeeper : public IPosition {
        public:
            int         id  () override final { return 1; }
            std::string name() override final { return "вратарь"; }
        };

        class PosDefender : public IPosition {
        public:
            int         id  () override final { return 2; }
            std::string name() override final { return "защитник"; }
        };

        class PosMidfielder: public IPosition {
        public:
            int         id  () override final { return 3; }
            std::string name() override final { return "полузащитник"; }
        };

        class PosDefensiveMidfielder : public IPosition {
        public:
            int         id  () override final { return 4; }
            std::string name() override final { return "опорный полузащитник"; }
        };

        class PosAttackingMidfielder : public IPosition {
        public:
            int         id  () override final { return 5; }
            std::string name() override final { return "атакующий полузащитник"; }
        };

        class PosForward : public IPosition {
        public:
            int         id  () override final { return 6; }
            std::string name() override final { return "нападающий"; }
        };

        class IStatus {
        public:
            IStatus() {}
            IStatus( const IStatus& ) {}

            virtual ~IStatus() {}

            virtual int         id  () { return 0; }
            virtual std::string name() { return ""; }
        };

        class StatNewbie : public IStatus {
        public:
            int         id  () override final { return 1; }
            std::string name() override final { return "новичок"; }
        };

        class StatMiddle : public IStatus {
        public:
            int         id  () override final { return 2; }
            std::string name() override final { return "середняк"; }
        };

        class StatStar : public IStatus {
        public:
            int         id  () override final { return 3; }
            std::string name() override final { return "звезда"; }
        };

        class StatSuperstar : public IStatus {
        public:
            int         id  () override final { return 4; }
            std::string name() override final { return "суперзвезда"; }
        };

        class StatOldfag : public IStatus {
        public:
            int         id  () override final { return 5; }
            std::string name() override final { return "старожил"; }
        };

        class Player {
        public:

             Player()
             {
                age_      = 0;
                position_ = new PosGoalkeeper();
                status_   = new StatNewbie();
             }

             Player( const Player& value )
             {
                firstName_  = value.firstName_;
                secondName_ = value.secondName_;
                age_        = value.age_;
                status_     = value.status_;
                position_   = value.position_;
             }


             Player(
                const std::string& firstName,
                const std::string& secondName,
                int age,
                IStatus* status,
                IPosition* position )
             : firstName_( firstName ),
               secondName_( secondName ),
               age_( age )
             {
                status_   = status;
                position_ = position;
             }

            ~Player()
            {
            }

            std::string toString  ()
            {
                return firstName_  + " " +
                       secondName_ + " " +
                       std::to_string( age_ ) + " " +
                       utils::year_postfix( age_ ) + " " +
                       position_->name() + " " +
                       status_->name();
            }

            std::string firstName ()
            {
                return firstName_;
            }

            std::string secondName()
            {
                return secondName_;
            }

            IPosition* position()
            {
                return position_;
            }

            unsigned int age()
            {
                return age_;
            }

            void setFirstName ( const std::string& value )
            {
                firstName_ = value;
            }

            void setSecondName( const std::string& value )
            {
                secondName_ = value;
            }

            void setAge       ( unsigned int        value )
            {
                age_ = value;
            }

        private:

            std::string  firstName_;
            std::string  secondName_;
            unsigned int age_;
            IPosition*   position_;
            IStatus*     status_;

        };

        class SportsTeam {
        public:

            virtual void addPlayer     ( const Player& player ) = 0;
            virtual void removePlayerAt( size_t pos )           = 0;
            virtual void output        ()                       = 0;

        };

        class FootballTeam : public SportsTeam, public utils::Nocopy {
        public:
             FootballTeam() {}
            ~FootballTeam() {}

            virtual void addPlayer( const Player& player ) override
            {
                players_.push_back( player );
            }

            virtual void removePlayerAt( size_t pos ) override
            {
                // maybe it's worth to throw exception here?..
                players_.erase( players_.begin() + pos );
            }

            virtual void output()                     override
            {
                for( auto& itr : players_ )
                    std::cout << itr.toString() << std::endl;
            }

            void sort()
            {
                auto comp = []( Player a, Player b ) -> bool {
                    bool result = true;
                    if( a.secondName().compare( b.secondName() ) == 0 ) {
                        result = ( a.secondName() > b.secondName() ) && ( a.firstName() > b.firstName() );
                    }
                    else {
                        result = ( a.secondName() > b.secondName() );
                    }

                    result &= ( a.age() > b.age() );
                    result &= ( a.position()->id() > b.position()->id() );

                    return result;
                };

                std::sort( players_.begin(), players_.end(), comp );

            }

        private:

            std::vector< Player > players_;

        };

        class Game {
        public:
            static Game& getInstance()
            {
                std::cout << "I'm a singletone, lol!" << std::endl;
                static Game instance;
                return instance;
            }

        private:

            FootballTeam firstTeam_;
            FootballTeam secondTeam_;

            Game() {}

            Game( Game const& ) = delete;
            void operator=( Game const& ) = delete;
        };


    }
}

using namespace sportstracker;
using namespace sportstracker::football;
using namespace std;

int main(int argc, char const *argv[])
{
    // vector< int > array = { 1, 5, 3, 8 };

    // algorithms::quick_sort< int >( array, []( int a, int b ) -> bool { return ( a <= b )? true : false; } );

    // for( auto& itr : array )
    // 	cout << itr << endl;


    auto germany = new FootballTeam();

    germany->addPlayer( Player( "Мануэль" , "Нойер"       , 28, new StatStar()     , new PosGoalkeeper() ) );
    germany->addPlayer( Player( "Рон"     , "Цилер"       , 25, new StatNewbie()   , new PosGoalkeeper() ) );
    germany->addPlayer( Player( "Филипп"  , "Лам"         , 32, new StatStar()     , new PosDefender() ) );
    germany->addPlayer( Player( "Тонн"    , "Кросс"       , 23, new StatSuperstar(), new PosGoalkeeper() ) );
    germany->addPlayer( Player( "Месут"   , "Озил"        , 26, new StatSuperstar(), new PosDefensiveMidfielder() ) );
    germany->addPlayer( Player( "Бастиан" , "Швайнштайгер", 30, new StatSuperstar(), new PosDefensiveMidfielder() ) );
    germany->addPlayer( Player( "Мирослав", "Клозе"       , 36, new StatOldfag()   , new PosAttackingMidfielder() ) );
    germany->addPlayer( Player( "Марио"   , "Гётце"       , 22, new StatOldfag()   , new PosGoalkeeper() ) );
    germany->addPlayer( Player( "Лукаш"   , "Подольски"   , 29, new StatOldfag()   , new PosDefensiveMidfielder() ) );


    germany->sort();
    germany->output();

    set< IPosition* > positions;

    positions.insert( new PosGoalkeeper() );
    positions.insert( new PosDefender() );
    positions.insert( new PosMidfielder() );
    positions.insert( new PosDefensiveMidfielder() );
    positions.insert( new PosAttackingMidfielder() );
    positions.insert( new PosForward() );

    cout << '\n';

    for( auto& itr : positions )
        cout << itr->name() << " " << itr->id() << endl;

    set< IStatus* > statuses;

    statuses.insert( new StatNewbie() );
    statuses.insert( new StatMiddle() );
    statuses.insert( new StatStar() );
    statuses.insert( new StatSuperstar() );
    statuses.insert( new StatOldfag() );

    cout << '\n';

    for( auto& itr : statuses )
        cout << itr->name() << " " << itr->id() << endl;

    vector< IPosition* > posVector( positions.begin(), positions.end() );

    cout << '\n';

    algorithms::quick_sort< IPosition* >( posVector, []( IPosition* a, IPosition* b ) -> bool { return ( a->id() > b->id() )? true : false; } );

    for( auto& itr : posVector )
        cout << itr->name() << " " << itr->id() << endl;


    vector< IStatus* > statVector( statuses.begin(), statuses.end() );

    cout << '\n';

//    algorithms::quick_sort< IStatus* >( statVector, []( IStatus* a, IStatus* b ) -> bool { return ( a->id() > b->id() )? true : false; } );

    for( auto& itr : statVector )
        cout << itr->name() << " " << itr->id() << endl;




    cout << '\n';

    Game::getInstance();


    return 0;
}
