#include "Spieler.h"

#include "Team.h"
#include "Laser.h"
#include "Karte.h"
#include "SSKlient.h"
#include <iostream>

// Inhalt der Spieler Klasse aus Spieler.h
// Konstruktor
Spieler::Spieler( SpielerStr *zStr )
    : Model2DObject()
{
    activeSkills = new RCArray< ActiveSkill >();
    accountId = 0;
    sNum = zStr->id;
    farbe = 0;
    team = 0;
    klient = 0;
    level = 1;
    for( int i = 0; i < T_MAX; i++ )
        tastatur[ i ] = 0;
    amLeben = 1;
    schussAbk = 0;
    energieAbk = 0;
    repAbk = 0;
    reinkAbk = 0;
    maxReinkAbk = 5;
    startPos = zStr->pos;
    setPosition( startPos );
    setDrehung( (float)zStr->rot );
    beschleunigung = zStr->beschleunigung;
    energie = zStr->maxEnergie;
    stability = zStr->maxStability;
    reparatur = zStr->reparatur;
    laserIntensity = zStr->laserIntensity;
    laserEffizienz = zStr->laserEffizienz;
    akkuLeistung = zStr->akkuLeistung;
    maxEnergie = zStr->maxEnergie;
    maxStability = zStr->maxStability;
    laserTempo = zStr->laserTempo;
    netzwerk = zStr->netzwerk;
    wendigkeit = zStr->wendigkeit;
    antriebEffizienz = zStr->antriebEffizienz;
    energieSchild = zStr->energieSchild;
    energieSchildEffizienz = zStr->energieSchildEffizienz;
    skillPunkte = zStr->skillPunkte;
    schadenBekommen = 0;
    schadenGemacht = 0;
    treibstoffVerbraucht = 0;
    shots = 0;
    treffer = 0;
    punkte = 0;
    kills = 0;
    tode = 0;
    zeitAmLeben = 0;
    zeitTod = 0;
    ep = 0;
    nextSkillEp = 80;
    needWiederbelebung = 0;
}

// Destruktor
Spieler::~Spieler()
{
    if( team )
        team->release();
    if( klient )
        klient->release();
    activeSkills->release();
}

double Spieler::calculateBeschleunigung()
{
    double factor = 1;
    double bonus = 0;
    for( auto s = activeSkills->getIterator(); s; s++ )
    {
        factor *= s->getBeschleunigungFactor();
        bonus += s->getBeschleunigungBonus();
    }
    return ( beschleunigung + team->beschleunigung ) * factor + bonus;
}

double Spieler::calculateReparatur()
{
    double factor = 1;
    double bonus = 0;
    for( auto s = activeSkills->getIterator(); s; s++ )
    {
        factor *= s->getReparaturFactor();
        bonus += s->getReparaturBonus();
    }
    return ( reparatur + team->reparatur ) * factor + bonus;
}

double Spieler::calculateLaserIntensity()
{
    double factor = 1;
    double bonus = 0;
    for( auto s = activeSkills->getIterator(); s; s++ )
    {
        factor *= s->getLaserIntensityFactor();
        bonus += s->getLaserIntensityBonus();
    }
    return ( laserIntensity + team->laserIntensity ) * factor + bonus;
}

double Spieler::calculateLaserEffizienz()
{
    double factor = 1;
    double bonus = 0;
    for( auto s = activeSkills->getIterator(); s; s++ )
    {
        factor *= s->getLaserEffizienzFactor();
        bonus += s->getLaserEffizienzBonus();
    }
    return ( laserEffizienz + team->laserEffizienz ) * factor + bonus;
}

double Spieler::calculateAkkuLeistung()
{
    double factor = 1;
    double bonus = 0;
    for( auto s = activeSkills->getIterator(); s; s++ )
    {
        factor *= s->getAkkuLeistungFactor();
        bonus += s->getAkkuLeistungBonus();
    }
    return ( akkuLeistung + team->akkuLeistung ) * factor + bonus;
}

double Spieler::calculateMaxEnergie()
{
    double factor = 1;
    double bonus = 0;
    for( auto s = activeSkills->getIterator(); s; s++ )
    {
        factor *= s->getMaxEnergieFactor();
        bonus += s->getMaxEnergieBonus();
    }
    return ( maxEnergie + team->maxEnergie ) * factor + bonus;
}

double Spieler::calculateMaxStability()
{
    double factor = 1;
    double bonus = 0;
    for( auto s = activeSkills->getIterator(); s; s++ )
    {
        factor *= s->getMaxStabilityFactor();
        bonus += s->getMaxStabilityBonus();
    }
    return ( maxStability + team->maxStability ) * factor + bonus;
}

double Spieler::calculateLaserTempo()
{
    double factor = 1;
    double bonus = 0;
    for( auto s = activeSkills->getIterator(); s; s++ )
    {
        factor *= s->getLaserTempoFactor();
        bonus += s->getLaserTempoBonus();
    }
    return ( laserTempo + team->laserTempo ) * factor + bonus;
}

double Spieler::calculateWendigkeit()
{
    double factor = 1;
    double bonus = 0;
    for( auto s = activeSkills->getIterator(); s; s++ )
    {
        factor *= s->getWendigkeitFactor();
        bonus += s->getWendigkeitBonus();
    }
    return ( wendigkeit + team->wendigkeit ) * factor + bonus;
}

double Spieler::calculateAntriebEffizienz()
{
    double factor = 1;
    double bonus = 0;
    for( auto s = activeSkills->getIterator(); s; s++ )
    {
        factor *= s->getAntriebEffizienzFactor();
        bonus += s->getAntriebEffizienzBonus();
    }
    return ( antriebEffizienz + team->antriebEffizienz ) * factor + bonus;
}

double Spieler::calculateEnergieSchild()
{
    double factor = 1;
    double bonus = 0;
    for( auto s = activeSkills->getIterator(); s; s++ )
    {
        factor *= s->getEnergieSchildFactor();
        bonus += s->getEnergieSchildBonus();
    }
    return ( energieSchild + team->energieSchild ) * factor + bonus;
}

double Spieler::calculateEnergieSchildEffizienz()
{
    double factor = 1;
    double bonus = 0;
    for( auto s = activeSkills->getIterator(); s; s++ )
    {
        factor *= s->getEnergieSchildEffizienzFactor();
        bonus += s->getEnergieSchildEffizienzBonus();
    }
    return ( energieSchildEffizienz + team->energieSchildEffizienz ) * factor + bonus;
}

// nicht constant
void Spieler::addSkill( ActiveSkill *skill )
{
    activeSkills->add( skill );
}

void Spieler::useSkill( int id )
{
    if( activeSkills->z( id ) )
        activeSkills->z( id )->activate();
}

void Spieler::offline()
{
    klient->offline();
}

void Spieler::online( SSKlientV *zKlient )
{
    klient->online( zKlient );
}

void Spieler::setModelData( Model2DData *data )
{
    for( auto i = data->polygons->getIterator(); i; i++ )
    {
        if( i._.name->istGleich( "engine_l" ) )
        {
            stL = *i._.schwerpunkt;
            Vertex l, r;
            for( int j = 0; j < 4; j++ )
            {
                if( i._.tKordinaten->get( j ).y == 1.f )
                {
                    if( i._.tKordinaten->get( j ).x == 0.f )
                        l = i._.vertex->get( j );
                    if( i._.tKordinaten->get( j ).x == 1.f )
                        r = i._.vertex->get( j );
                }
            }
            kL = ( ( l + ( r - l ) * 0.5 ) - stL );
        }
        if( i._.name->istGleich( "engine_r" ) )
        {
            stR = *i._.schwerpunkt;
            Vertex l, r;
            for( int j = 0; j < 4; j++ )
            {
                if( i._.tKordinaten->get( j ).y == 1.f )
                {
                    if( i._.tKordinaten->get( j ).x == 0.f )
                        l = i._.vertex->get( j );
                    if( i._.tKordinaten->get( j ).x == 1.f )
                        r = i._.vertex->get( j );
                }
            }
            kR = ( ( l + ( r - l ) * 0.5 ) - stR );
        }
        if( i._.name->istGleich( "engine_m" ) )
        {
            stM = *i._.schwerpunkt;
            Vertex l, r;
            for( int j = 0; j < 4; j++ )
            {
                if( i._.tKordinaten->get( j ).y == 1.f )
                {
                    if( i._.tKordinaten->get( j ).x == 0.f )
                        l = i._.vertex->get( j );
                    if( i._.tKordinaten->get( j ).x == 1.f )
                        r = i._.vertex->get( j );
                }
            }
            kM = ( ( l + ( r - l ) * 0.5 ) - stM );
        }
    }
    kL.normalize();
    kR.normalize();
    kM.normalize();
    setModel( data );
}

void Spieler::setAccountId( int id )
{
    accountId = id;
}

void Spieler::setTeam( Team *team )
{
    if( this->team )
        this->team->release();
    this->team = team;
}

void Spieler::setSpielerFarbe( int farbe )
{
    this->farbe = farbe;
}

void Spieler::setKlient( Klient *klient )
{
    if( this->klient )
        this->klient->release();
    this->klient = klient;
}

bool Spieler::setTastataturStatus( TastaturStatus ts, bool aktiv )
{
    if( tastatur[ ts ] == aktiv || !amLeben )
        return 0;
    tastatur[ ts ] = aktiv;
    return 1;
}

bool Spieler::tick( const WeltInfo& info, double zeit )
{
    wInfo = info;
    if( !team )
        return 0;
    reinkAbk -= zeit;
    if( reinkAbk < 0 )
        reinkAbk = 0;
    if( !reinkAbk && !amLeben )
    { // Wiederbelebung
        needWiederbelebung = 1;
        return 0;
    }
    if( amLeben )
    {
        for( auto s = activeSkills->getIterator(); s; s++ )
            s->tick( zeit );
        schussAbk -= zeit;
        energieAbk -= zeit;
        if( schussAbk < 0 )
            schussAbk = 0;
        if( energieAbk < 0 )
            energieAbk = 0;
        repAbk -= zeit;
        if( repAbk < 0 )
            repAbk = 0;
        if( tastatur[ T_GAS ] )
        {
            double treibstoff = 0.5 * ( calculateBeschleunigung() / 15 )
                * zeit * ( 100 / calculateAntriebEffizienz() );
            treibstoffVerbraucht += treibstoff;
            float factor = 1;
            if( treibstoff > energie )
                factor = (float)( energie / treibstoff );
            if( factor > 0 )
                impuls( getWorldPos( stM ), getWorldDir( kM ) * (float)zeit * (float)calculateBeschleunigung() * factor );
            energie -= treibstoff * factor;
        }
        if( tastatur[ T_ROT_R ] )
        {
            double treibstoff = 0.25 * ( calculateWendigkeit() / 15 )
                * zeit * ( 100 / calculateAntriebEffizienz() );
            treibstoffVerbraucht += treibstoff;
            float factor = 1;
            if( treibstoff > energie )
                factor = (float)( energie / treibstoff );
            if( factor > 0 )
                impuls( getWorldPos( stL ), getWorldDir( kL ) * (float)zeit * (float)calculateWendigkeit() * factor );
            energie -= treibstoff * factor;
        }
        if( tastatur[ T_ROT_L ] )
        {
            double treibstoff = 0.25 * ( calculateWendigkeit() / 15 )
                * zeit * ( 100 / calculateAntriebEffizienz() );
            treibstoffVerbraucht += treibstoff;
            float factor = 1;
            if( treibstoff > energie )
                factor = (float)( energie / treibstoff );
            if( factor > 0 )
                impuls( getWorldPos( stR ), getWorldDir( kR ) * (float)zeit * (float)calculateWendigkeit() * factor );
            energie -= treibstoff * factor;
        }
        Model2DObject::tick( info, zeit );
        if( !energieAbk )
        {
            energie += calculateAkkuLeistung() * zeit;
            if( energie > calculateMaxEnergie() )
                energie = calculateMaxEnergie();
        }
        if( !repAbk )
        {
            stability += calculateReparatur() * zeit;
            if( stability > calculateMaxStability() )
                stability = calculateMaxStability();
        }
        zeitAmLeben += zeit;
    }
    else
        zeitTod += zeit;
    return 0;
}

bool Spieler::setSkill( int art )
{
    if( !skillPunkte )
        return 0;
    skillPunkte--;
    switch( art )
    {
    case 0: // Max Stabilität
        maxStability += 10;
        stability += 10;
        team->maxStability = team->getMaxStabilityBonus();
        break;
    case 1: // Max Energie
        maxEnergie += 10;
        team->maxEnergie = team->getMaxEnergieBonus();
        break;
    case 2: // Reparatur
        reparatur += 0.15;
        team->reparatur = team->getReperaturBonus();
        break;
    case 3: // Laser Intensität
        laserIntensity += 4;
        team->laserIntensity = team->getLaserIntensityBonus();
        break;
    case 4: // Laser Effizienz
        laserEffizienz += 1.5;
        team->laserEffizienz = team->getLaserEffizienzBonus();
        break;
    case 5: // Laser Tempo
        laserTempo += 15;
        team->laserTempo = team->getLaserTempoBonus();
        break;
    case 6: // Beschleunigung
        beschleunigung += 10;
        team->beschleunigung = team->getBeschleunigungBonus();
        break;
    case 7: // Wendigkeit
        wendigkeit += 2.5;
        team->wendigkeit = team->getWendigkeitBonus();
        break;
    case 8: // Netzwerk
        netzwerk += 1;
        team->maxStability = team->getMaxStabilityBonus();
        team->maxEnergie = team->getMaxEnergieBonus();
        team->reparatur = team->getReperaturBonus();
        team->laserIntensity = team->getLaserIntensityBonus();
        team->laserEffizienz = team->getLaserEffizienzBonus();
        team->laserTempo = team->getLaserTempoBonus();
        team->beschleunigung = team->getBeschleunigungBonus();
        team->wendigkeit = team->getWendigkeitBonus();
        team->akkuLeistung = team->getAkkuLeistungBonus();
        team->antriebEffizienz = team->getAntriebEffizienzBonus();
        team->energieSchild = team->getEnergieSchildBonus();
        team->energieSchildEffizienz = team->getEnergieSchildEffizienzBonus();
        break;
    case 9: // Akkuleistung
        akkuLeistung += 0.12;
        team->akkuLeistung = team->getAkkuLeistungBonus();
        break;
    case 10: // Akkuleistung
        antriebEffizienz += 10;
        team->antriebEffizienz = team->getAntriebEffizienzBonus();
        break;
    case 11: // Energie Schild
        energieSchild += 0.075;
        team->energieSchild = team->getEnergieSchildBonus();
        break;
    case 12: // Energie Schild Effizienz
        energieSchildEffizienz += 10;
        team->energieSchildEffizienz = team->getEnergieSchildEffizienzBonus();
        break;
    }
    return 1;
}

Laser *Spieler::getLaser( int sId )
{
    if( !amLeben || !tastatur[ T_FEUER ] || schussAbk || !team )
        return 0;
    double kosten = ( calculateLaserIntensity() / 2 +
        calculateLaserTempo() / 9 ) *
                      ( 1 / ( calculateLaserEffizienz() / 25 ) );
    if( kosten < 1 )
        kosten = 1;
    if( kosten > energie )
        return 0;
    energie -= kosten;
    schussAbk = 1;
    energieAbk = 1.5;
    shots++;
    Vertex sSpeed = ( Vertex( lowPrecisionCos( rotation ), lowPrecisionSin( rotation ) ) * (float)calculateLaserTempo() );
    sSpeed += speed;
    return new Laser( sId, getPosition(), sSpeed, sNum, calculateLaserIntensity() );
}

bool Spieler::nimmSchaden( double &intensity, int sLevel )
{
    double originalIntens = intensity;
    double schieldVal = intensity - intensity / calculateEnergieSchild();
    double schieldEVal = schieldVal / ( calculateEnergieSchildEffizienz() / 100 );
    if( schieldEVal > energie )
    {
        schieldEVal = energie;
        schieldVal = schieldEVal * ( calculateEnergieSchildEffizienz() / 100 );
    }
    intensity -= schieldVal;
    energie -= schieldEVal;
    stability -= intensity;
    schadenBekommen += intensity;
    energieAbk = 1.5;
    repAbk = 2;
    if( stability <= 0 )
    {
        amLeben = 0;
        setCollision( 0 );
        tode++;
        stability = 0;
        reinkAbk += maxReinkAbk;
        maxReinkAbk++;
        punkte--;
    }
    else
    {
        float levelFactor = (float)sLevel / (float)level;
        addEp( ( (float)originalIntens / 20 ) * levelFactor );
    }
    return stability == 0;
}

void Spieler::machSchaden( double intensity, int sLevel, bool erf )
{
    schadenGemacht += intensity;
    addTreffer( (float)intensity, sLevel, erf );
}

void Spieler::addKill( int sLevel, bool erf )
{
    float levelFactor = (float)sLevel / (float)level;
    if( erf )
        addEp( 80 * levelFactor );
    kills++;
    punkte++;
    maxReinkAbk--;
    if( maxReinkAbk < 5 )
        maxReinkAbk = 5;
}

void Spieler::addTreffer( float intensity, int sLevel, bool erf )
{
    treffer++;
    float levelFactor = (float)sLevel / (float)level;
    if( erf )
        addEp( ( intensity / 10 ) * levelFactor );
}

void Spieler::wiederbeleben()
{
    for( auto s = activeSkills->getIterator(); s; s++ )
        s->reset();
    needWiederbelebung = 0;
    setPosition( startPos );
    reinkAbk = maxReinkAbk * 2;
    energie = calculateMaxEnergie();
    stability = calculateMaxStability();
    schussAbk = 0;
    energieAbk = 0;
    repAbk = 0;
    setSpeed( 0, 0 );
    setDrehung( 0 );
    setDrehungSpeed( 0 );
    for( int i = 0; i < T_MAX; i++ )
        tastatur[ i ] = 0;
    amLeben = 1;
    setCollision( 1 );
}

void Spieler::addEp( float ep )
{
    this->ep += ep;
    while( this->ep >= nextSkillEp )
    {
        level++;
        skillPunkte++;
        this->ep -= nextSkillEp;
        nextSkillEp *= 1.025f;
    }
}

// constant
int Spieler::getAccountId() const
{
    return accountId;
}

bool Spieler::istOnline() const
{
    return klient ? klient->istOnline() : 0;
}

Klient *Spieler::zKlient() const
{
    return klient;
}

bool Spieler::istTreffer( Vec2< float > pos ) const
{
    if( !amLeben )
        return 0;
    return istPunktInnen( pos ) || istPunktInnen( pos + wInfo.size ) || istPunktInnen( pos + Punkt( wInfo.size.x, 0 ) ) || istPunktInnen( pos + Punkt( 0, wInfo.size.y ) ) ||
        istPunktInnen( pos + -wInfo.size ) || istPunktInnen( pos + Punkt( -wInfo.size.x, 0 ) ) || istPunktInnen( pos + Punkt( 0, -wInfo.size.y ) ) ||
        istPunktInnen( pos + Punkt( -wInfo.size.x, wInfo.size.y ) ) || istPunktInnen( pos + Punkt( wInfo.size.x, -wInfo.size.y ) );
}

// Raumschiff Eigenschaften
double Spieler::getTeamMaxEnergieBonus() const
{
    return maxEnergie / 100 * netzwerk;
}

double Spieler::getTeamMaxStabilityBonus() const
{
    return maxStability / 100 * netzwerk;
}

double Spieler::getTeamReparaturBonus() const
{
    return reparatur / 100 * netzwerk;
}

double Spieler::getTeamLaserIntensityBonus() const
{
    return laserIntensity / 100 * netzwerk;
}

double Spieler::getTeamLaserEffizienzBonus() const
{
    return laserEffizienz / 100 * netzwerk;
}

double Spieler::getTeamAkkuLeistungBonus() const
{
    return akkuLeistung / 100 * netzwerk;
}

double Spieler::getTeamLaserTempoBonus() const
{
    return laserTempo / 100 * netzwerk;
}

double Spieler::getTeamBeschleunigungBonus() const
{
    return beschleunigung / 100 * netzwerk;
}

double Spieler::getTeamWendigkeitBonus() const
{
    return wendigkeit / 100 * netzwerk;
}

double Spieler::getTeamAntriebEffizienzBonus() const
{
    return antriebEffizienz / 100 * netzwerk;
}

double Spieler::getTeamEnergieSchildBonus() const
{
    return energieSchild / 100 * netzwerk;
}

double Spieler::getTeamEnergieSchildEffizienzBonus() const
{
    return energieSchildEffizienz / 100 * netzwerk;
}

bool Spieler::istAmLeben() const
{
    return amLeben;
}

// Statistik Werte
int Spieler::getSpielerNummer() const
{
    return sNum;
}

Team *Spieler::zTeam() const
{
    return team;
}

int Spieler::getSpielerFarbe() const
{
    return farbe;
}

int Spieler::getSchadenBekommen() const
{
    return (int)schadenBekommen;
}

int Spieler::getSchadenGemacht() const
{
    return (int)schadenGemacht;
}

int Spieler::getTreibstoffVerbraucht() const
{
    return (int)treibstoffVerbraucht;
}

int Spieler::getShots() const
{
    return shots;
}

int Spieler::getTreffer() const
{
    return treffer;
}

int Spieler::getPunkte() const
{
    return punkte;
}

int Spieler::getKills() const
{
    return kills;
}

int Spieler::getTode() const
{
    return tode;
}

int Spieler::getZeitAmLeben() const
{
    return (int)zeitAmLeben;
}

int Spieler::getZeitTod() const
{
    return (int)zeitTod;
}

bool Spieler::doNeedWiederbelebung() const
{
    return needWiederbelebung;
}

float Spieler::getEp() const
{
    return ep;
}

int Spieler::getSkillP() const
{
    return skillPunkte;
}

int Spieler::getLevel() const
{
    return level;
}

// Reference Counting
Spieler *Spieler::getThis()
{
    ref++;
    return this;
}

Spieler *Spieler::release()
{
    ref--;
    if( !ref )
        delete this;
    return 0;
}