//Co treba urobit Na riadkoch 233 az 255 treba urobit funkciu, ktora najde zo zoznamu najblizsieho a preda tieto udaje a funkciu, ktora dostane parametre pozicie jedneho objektu a druheho a najde pre neho najvyhodnejsi smer

#include "spravca.h"

#define MIN_ZAMERANIE 300 //urcuje, ze na ku najmensiu vzdialenost je schopny zamerat torpedo
#define VPS 100 //Vypocty Per Second
#define KKF 3 //Kazdy kolkaty frame sa ma vykreslit
#define VPRAVO 750  //ako daleko moze ist chuck, bez toho, aby sa musela pohnut obrazovka
#define VLAVO 100
int ked_vykresli = 1;

double vzdialSpravca(double x1, double y1, double x2, double y2)
{
  return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}

Spravca::Spravca()
{
  zacNepr1=konNepr1=NULL;  //nastavi smerniky ako nulove
  zacVybuch1=konVybuch1=NULL;  //nastavi smerniky ako nulove
  zacStrela1=konStrela1=NULL;  //nastavi smerniky ako nulove
  zacStrela2=konStrela2=NULL;  //nastavi smerniky ako nulove
  zacStrela3=konStrela3=NULL;  //nastavi smerniky ako nulove
  zacStrela4=konStrela4=NULL;  //nastavi smerniky ako nulove
  screen_n = create_bitmap(1024,768);
  Chuck.SetBitmap(screen_n);
  ci_zvuk = 1;
  if((z_DeadNepr1 = load_sample("sfx/deadnepr1.wav")) == NULL);
  {
    printf("Nenasiel som sfx/deadnepr1.waw");
    ci_zvuk = 0;
  }
  if((vybuch = load_sample("sfx/Vybuch.wav")) == NULL);
  {
    printf("Nenasiel som sfx/Vybuch.waw");
    ci_zvuk = 0;
  }
}

Spravca::~Spravca() //vymaze vsetko
{
  Objekt *po1, *po2;
  po1 = zacNepr1;
  while(po1 != NULL) 
  {
    po2 = po1->GetDalsia();
    delete po1;
    po1 = po2;
  }
  
  po1 = zacVybuch1;
  while(po1 != NULL) 
  {
    po2 = po1->GetDalsia();
    delete po1;
    po1 = po2;
  }
  
  po1 = zacStrela1;
  while(po1 != NULL) 
  {
    po2 = po1->GetDalsia();
    delete po1;
    po1 = po2;
  }
  
  po1 = zacStrela2;
  while(po1 != NULL) 
  {
    po2 = po1->GetDalsia();
    delete po1;
    po1 = po2;
  }
  
  po1 = zacStrela3;
  while(po1 != NULL) 
  {
    po2 = po1->GetDalsia();
    delete po1;
    po1 = po2;
  }
  
  po1 = zacStrela4;
  while(po1 != NULL) 
  {
    po2 = po1->GetDalsia();
    delete po1;
    po1 = po2;
  }
  destroy_sample(z_DeadNepr1);
}

void Spravca::Pridaj(int co,  double px, double py, double speed, double maxspeed, double uhol, int health, int max_health, double otacanie, int vybuch)
{
  //co == 0 bude nasa nadupana ponorka
  //co == 1 - 49 bude typ nepriatela 1 - 49
  //co == 50 - 199 budu zbrane
  //co == 200 - budu ine objekty
  // px je x pozicia, ktoru mam mat objekt (moze byt aj mimo screen)
  // podobne ako px, len y os
  // toto je jeho AKTUALNA rychlost (niektorym objektom(napriklad torpedam a gulometu) sa rychlost pocas cyklu zvysuje az na maxspeed
  // maxspeed je maximalna mozna nadobudelna rychlost
  //uhol pod ktorym sa pohybuje (0 je dole 90 je vpravo 180 je hore 270 je vlavo)
  //health je aktualny zivot, pri co == 200 je health a maxhealth sila vybuchu
  //maxhealth je maximalny mozny pocet zivotov
  //otacanie je ze o okolko sa moze otocit za jeden vypocet
  //vybuch je sila vybuchu, ked ten dany objekt zomrie (proste vybuchne (torpedo ma 50, navnada 1, gulomet 30, nepriatel tusim 100))
  Objekt* po;
  Objekt* zac;
  Objekt* kon;
  double vzdial;
  switch(co)  //nastavi zaciatocne veci pre funkciu
  {
    case 1: //prida nepriatela typu 1
      po = zac = kon = new Nepr1();
      zac = zacNepr1;
      kon = konNepr1;
      printf("Objekt 1");
      break;
    case 50:
      po = new Strela1();
      zac = new Strela1();
      kon = new Strela1();
      zac = zacStrela1;
      kon = konStrela1;
      printf("Objekt 50");
      break;
    case 51:
      po = zac = kon = new Strela2();
      zac = zacStrela2;
      kon = konStrela2;
      printf("Objekt 51");
      break;
    case 52:
      po = zac = kon = new Strela3();
      zac = zacStrela3;
      kon = konStrela3;
      printf("Objekt 52");
      break;
    case 53:
      po = zac = kon = new Strela4();
      zac = zacStrela4;
      kon = konStrela4;
      printf("Objekt 53");
      break;
    case 200:
      po = zac = kon = new Vybuch1();
      zac = zacVybuch1;
      kon = konVybuch1;
      printf("Objekt 200");
      break;
  }  
  if(co == 1 || co == 50 || co == 51 || co == 52 || co == 53 || co == 200) //aby nepridaval do neexistujuceho zoznamu
  {
    po ->SetBitmap(screen_n);  //nastavi bitmapu vykreslovania
    if(zac == NULL) //ked este nie je
    {
      po->SetDalsia(NULL);
      po->SetPredosla(NULL);
      zac = po;
      kon = po;
      printf("  PRVY\n");
    }
    else  //ked uz nejaky Objekt tohto typu je
    {
      kon->SetDalsia(po);
      po->SetPredosla(kon);
      kon = po;
      printf("  INY\n");
    }
    kon->SetPx(px);
    kon->SetPy(py);
    kon->SetSpeed(speed);
    kon->SetMax_speed(maxspeed);
    kon->SetUhol(uhol);
    kon->SetHealth(health);
    kon->SetMax_health(max_health);
    kon->SetOtacanie(otacanie);
    kon->SetVybuch(vybuch);
    printf(" uspesne pridany \n");
    
  switch(co)  //Nastavi zacObjekt a konObjekt (bez toho je to NULL, co nie je dobre, lebo potom nic nefunguje)
    {
    case 1: 
      SetZacNepr1(zac);
      SetKonNepr1(kon);
      break;
    case 50:
      SetZacStrela1(zac);
      SetKonStrela1(kon);
      break;
    case 51:
      SetZacStrela2(zac);
      SetKonStrela2(kon);
      break;
    case 52:
      SetZacStrela3(zac);
      SetKonStrela3(kon);
      break;
    case 53:
      SetZacStrela4(zac);
      SetKonStrela4(kon);
      break;
    case 200:
      SetZacVybuch1(zac);
      SetKonVybuch1(kon);
      if(ci_zvuk == 1)
        play_sample(GetVybuch(),128,128,1000,FALSE);
      //ubere to Max_health - vzdial na druhu (cim blizsie, tym viac) vsetkym
      for(zac = GetZacNepr1();zac!=NULL;zac = zac->GetDalsia())
        if((vzdial = vzdialSpravca(kon->GetPx(),kon->GetPy(),zac->GetPx(),zac->GetPy())) < kon->GetMax_health())
        {
          vzdial = kon->GetMax_health() - vzdial;
          zac->SetHealth((int) (zac->GetHealth() - vzdial*vzdial));
          if(zac->GetHealth() < 0)
            zac->SetHealth(0);
        }
      for(zac = GetZacStrela1();zac!=NULL;zac = zac->GetDalsia())
        if((vzdial = vzdialSpravca(kon->GetPx(),kon->GetPy(),zac->GetPx(),zac->GetPy())) < kon->GetMax_health())
        {
          vzdial = kon->GetMax_health() - vzdial;
          zac->SetHealth((int) (zac->GetHealth() - vzdial*vzdial));
          if(zac->GetHealth() < 0)
            zac->SetHealth(0);
        } 
      for(zac = GetZacStrela2();zac!=NULL;zac = zac->GetDalsia())
        if((vzdial = vzdialSpravca(kon->GetPx(),kon->GetPy(),zac->GetPx(),zac->GetPy())) < kon->GetMax_health())
        {
          vzdial = kon->GetMax_health() - vzdial;
          zac->SetHealth((int) (zac->GetHealth() - vzdial*vzdial));
          if(zac->GetHealth() < 0)
            zac->SetHealth(0);
        } 
      for(zac = GetZacStrela3();zac!=NULL;zac = zac->GetDalsia())
        if((vzdial = vzdialSpravca(kon->GetPx(),kon->GetPy(),zac->GetPx(),zac->GetPy())) < kon->GetMax_health())
        {
          vzdial = kon->GetMax_health() - vzdial;
          zac->SetHealth((int) (zac->GetHealth() - vzdial*vzdial));
          if(zac->GetHealth() < 0)
            zac->SetHealth(0);
        } 
      for(zac = GetZacStrela4();zac!=NULL;zac = zac->GetDalsia())
        if((vzdial = vzdialSpravca(kon->GetPx(),kon->GetPy(),zac->GetPx(),zac->GetPy())) < kon->GetMax_health())
        {
          vzdial = kon->GetMax_health() - vzdial;
          zac->SetHealth((int) (zac->GetHealth() - vzdial*vzdial));
          if(zac->GetHealth() < 0)
            zac->SetHealth(0);
        }  
        double vzdial2, vzdial3; //predok a zadok
        //kedze ma tri akcne body tak musi skontrolovat vsetky tri, ked je dosah vybuchu dostatocny, tak sa pokracuje
        if((vzdial = vzdialSpravca(kon->GetPx(),kon->GetPy(),Chuck.GetPx() - 50,Chuck.GetPy())) < kon->GetMax_health() || (vzdial2 = vzdialSpravca(kon->GetPx(),kon->GetPy(),(Chuck.GetPx()-50 + Chuck.GetVx())/2,(Chuck.GetPy()+ Chuck.GetVy())/2)) < kon->GetMax_health() || (vzdial3 = vzdialSpravca(kon->GetPx(),kon->GetPy(),(3*Chuck.GetPx()-150 - Chuck.GetVx())/2,(Chuck.GetPy()*3 - Chuck.GetVy())/2)) < kon->GetMax_health())
        {  
          //vybere najmensiu vzdialenost a potom vypocita ujmu na zdravi
          if(vzdial <= vzdial2 && vzdial <= vzdial3)
            vzdial = kon->GetMax_health() - vzdial;
          if(vzdial2 < vzdial && vzdial2 <= vzdial3)
            vzdial = kon->GetMax_health() - vzdial2;
          if(vzdial3 < vzdial2 && vzdial3 < vzdial)
            vzdial = kon->GetMax_health() - vzdial3;
          Chuck.SetHealth((int) (Chuck.GetHealth() - vzdial*vzdial));
          if(Chuck.GetHealth() < 0)
            Chuck.SetHealth(0);
        } 
      break;
    }  
  }
  else
    printf("Zadal si objekt, ktory neexistuje vo funkcii pridaj\n");
}

void Spravca::Uber(int co, Objekt *ktory) //prvy parameter je ze aky typ 
//(vid funkcia Pridaj) druhy je ze ktory konkretny clen zoznamu
{
  Objekt *po1;
  Objekt *po2;
  Objekt *zac, *kon;
  zac=kon=po1=po2=NULL; //ked je NULL, tak my to dost pomoze pri zistovani, ze ci je jediny v strede na zaciatku alebo na konci
  if(ktory != NULL) //aby nebol segmentation fault, ked zada NULLovy smernik
    switch(co)  //vlastne init, ktory vytvori nove objekty a potom nastavi to co treba
    {
     case 1:  
       zac = kon = new Nepr1();
       zac = GetZacNepr1();
       kon = GetKonNepr1();
       Chuck.SetMoney(Chuck.GetMoney() + (int) ((double)ktory->GetMax_health()*ktory->GetMax_speed()*ktory->GetOtacanie()/2));
       break; 
     case 50:
       zac = kon = new Strela1();
       zac = GetZacStrela1();
       kon = GetKonStrela1();
       break; 
     case 51:
       zac = kon = new Strela2();
       zac = GetZacStrela2();
       kon = GetKonStrela2();
       break; 
     case 52:
       zac = kon = new Strela3();
       zac = GetZacStrela3();
       kon = GetKonStrela3();
       break; 
     case 53:
       zac = kon = new Strela4();
       zac = GetZacStrela4();
       kon = GetKonStrela4();
       break; 
     case 200:
       zac = kon = new Vybuch1();
       zac = GetZacVybuch1();
       kon = GetKonVybuch1();
       break; 
    }
    
  else
    printf("Nepokusaj sa vymazat Objekt typu %d ktory nie je\n",co);
  
  if(co != 200)  //ked vybuchne vybuch tak nevznikne novy vybuch
    Pridaj(200,ktory->GetPx(),ktory->GetPy(),0,0,0,ktory->GetVybuch(),ktory->GetVybuch(),0,0);
  
  if(ktory->GetPredosla() != NULL) //pokusi sa dostat predoslu ktoreho, aby po vymzani dokazal zaplatat vzniknutu dieru
  {
    po1 = ktory->GetPredosla();
    printf("po1");
  }
  if(ktory->GetDalsia() != NULL)   //podone ako predosli if
  {
    po2 = ktory->GetDalsia();
    printf("po2");
  } 
  if(po1 ==  NULL && po2 == NULL)  //ked je jediny
  {
    zac = kon = NULL;
    delete ktory;
    printf("00");
  }
  if(po1 == NULL && po2 != NULL) //ked je prvy
  {
    zac = zac->GetDalsia();
    zac->SetPredosla(NULL);
    delete ktory;
    printf("01");
  }
  if(po2 == NULL && po1 !=NULL) //ked je posledny
  {
    kon = kon->GetPredosla();
    kon ->SetDalsia(NULL);
    delete ktory;
    printf("10");
  }
  if(po1 != NULL && po2 != NULL) //podmienka je tu preto, aby sa to neprekrivalo a nezvnikol SF
  {
    po1->SetDalsia(ktory->GetDalsia());  //ked nie je ani jedno z predoslych
    po2->SetPredosla(ktory->GetPredosla());
    delete ktory;
    printf("11");
  }
  switch(co)  //nakoniec nastavi zacObjekt a konObjekt Spravcu (BigBossa)
  {
    case 1: 
      SetZacNepr1(zac);
      SetKonNepr1(kon);
      if(ci_zvuk == 1)
        play_sample(z_DeadNepr1,128,128,1000,FALSE);
      break;
    case 50:
      SetZacStrela1(zac);
      SetKonStrela1(kon);
      break;
    case 51:
      SetZacStrela2(zac);
      SetKonStrela2(kon);
      break;
    case 52:
      SetZacStrela3(zac);
      SetKonStrela3(kon);
      break;
    case 53:
      SetZacStrela4(zac);
      SetKonStrela4(kon);
      break;
    case 200:
      SetZacVybuch1(zac);
      SetKonVybuch1(kon);
      break;
  }
  printf(" Uber %d\n",co);
}

void Spravca::Najblizsi(Objekt *co,Objekt *zac,double &min_x,double &min_y,double &vzdial)
{
  for(;zac != NULL;zac = zac->GetDalsia()) //prejde vsetkych nepriatelov a najde toho najblizsieho
  {
    if(vzdialSpravca(co->GetPx(), co->GetPy(), zac->GetPx(), zac->GetPy()) < vzdial)
    {
      vzdial = vzdialSpravca(co->GetPx(), co->GetPy(), zac->GetPx(), zac->GetPy());
      min_x=zac->GetPx();
      min_y=zac->GetPy();
    }  
  }
}

int Spravca::Vyber_smer(double px, double py, double ciel_x, double ciel_y, double uhol, double otacanie)
{
  double a,b,c; //zobere si tri moznosti (otocit sa dolava, doprava, alebo zostat v tomto kurzore)
  a = vzdialSpravca(ciel_x,ciel_y,px + 0.1*sin((uhol+otacanie)*pi180),py + 0.1*cos((uhol+otacanie)*pi180));
  b = vzdialSpravca(ciel_x,ciel_y,px + 0.1*sin(uhol*pi180),py + 0.1*cos(uhol*pi180));
  c = vzdialSpravca(ciel_x,ciel_y,px + 0.1*sin((uhol-otacanie)*pi180),py + 0.1*cos((uhol-otacanie)*pi180));
  if(a<=b && a<=c)
    return 1;
  if(c<a && c<b)
    return -1;
  return 0;  
}

void Spravca::Krok() //urobi to jeden krok
{
  Chuck.Ovladanie();
  Chuck.Maxspeed();
  Chuck.Pohni(); //aby sa mohla pohnut obrazovka hned
  if(Chuck.GetPx() - 1 > VPRAVO || Chuck.GetPx() + 1< VLAVO)
    Chuck.SetPx(Chuck.GetPx() - Chuck.GetSx());

  Objekt *po = zacNepr1;
  for(;po != NULL;po = po->GetDalsia())
  {
    po->SetUhol(po->GetUhol()+(Vyber_smer(po->GetPx(),po->GetPy(),Chuck.GetPx(),Chuck.GetPy(),po->GetUhol(),po->GetOtacanie()))*po->GetOtacanie());
    if(Chuck.GetPx() > VPRAVO || Chuck.GetPx() < VLAVO)
      po->SetPx(po->GetPx() - Chuck.GetSx());
    po->Pohni();
    if(ked_vykresli == KKF)
      po->Nakresli();
  }
  
  po = zacVybuch1;
  for(;po != NULL;po = po->GetDalsia())
  { 
    po->SetHealth(po->GetHealth()-1);
    if(Chuck.GetPx() > VPRAVO || Chuck.GetPx() < VLAVO)
      po->SetPx(po->GetPx() - Chuck.GetSx());
    po->Pohni();
    if(ked_vykresli == KKF)
      po->Nakresli();
  }
  
  //min x a y udavaju suradnice najblizsieho nepriatela, lebo je nanic si pametat celu triesu
  double min_x = 1000, min_y = 1000, min_vzdialSpravca = MIN_ZAMERANIE; //min vzdialSpravca udava, ze na aku najmensiu vzdialSpravcaenost je schopny niekoho zameriat
  Objekt *ps = GetZacStrela1(); 
  for(;ps != NULL;ps = ps->GetDalsia(), min_vzdialSpravca = MIN_ZAMERANIE,min_x=1000,min_y=1000)  //prebehne vsetky TORPEDA
  {
    if(Chuck.GetPx() > VPRAVO || Chuck.GetPx() < VLAVO)
      ps->SetPx(ps->GetPx() - Chuck.GetSx());
    ps->Pohni();
    if(ked_vykresli == KKF)  //vykresli len ked je to potrebne
      ps->Nakresli();  

    Najblizsi(ps,zacNepr1,min_x,min_y,min_vzdialSpravca);

    if(min_x < 1000 && min_y < 1000)
    {
      ps->SetUhol(ps->GetUhol()+(Vyber_smer(ps->GetPx(),ps->GetPy(),min_x,min_y,ps->GetUhol(),ps->GetOtacanie()))*ps->GetOtacanie());
    }
  }
  Objekt *ps2 = GetZacStrela2();
  for(;ps2 != NULL;ps2 = ps2->GetDalsia())
  {
    if(Chuck.GetPx() > VPRAVO || Chuck.GetPx() < VLAVO)
      ps2->SetPx(ps2->GetPx() - Chuck.GetSx());
    ps2->Pohni();
    if(ked_vykresli == KKF)
      ps2->Nakresli();
  }
  
  
  ps2 = GetZacStrela4();
  for(;ps2 != NULL;ps2 = ps2->GetDalsia())
  {
    if(Chuck.GetPx() > VPRAVO || Chuck.GetPx() < VLAVO)
      ps2->SetPx(ps2->GetPx() - Chuck.GetSx());
    ps2->Pohni();
    if(ps2->GetHealth() <=0 || ps2->GetPrebijanie() >= ps2->GetReload())
    {
      Uber(53,ps2);
      ps2 = NULL;
    }
    if(ps2 == NULL)
      break;
    for(ps = GetZacNepr1();ps!=NULL;ps = ps->GetDalsia())
    {
      if(vzdialSpravca(ps->GetPx(),ps->GetPy(),ps2->GetPx(),ps2->GetPy()) < 6)
      {
        Uber(53,ps2);
        ps2 = NULL;
      }
      if(ps2 == NULL)
        break;
    }
    if(ps2 == NULL)
      break;
    if(ked_vykresli == KKF)
      ps2->Nakresli();
  }
  
  
  min_vzdialSpravca = MIN_ZAMERANIE;
  min_x = 1000;
  min_y = 1000;
  ps2 = GetZacStrela3();  //nepriatelske torpeda
  for(;ps2 != NULL;ps2 = ps2->GetDalsia(), min_vzdialSpravca = MIN_ZAMERANIE, min_x = 1000, min_y = 1000)
  {
    if(Chuck.GetPx() > VPRAVO || Chuck.GetPx() < VLAVO)
      ps2->SetPx(ps2->GetPx() - Chuck.GetSx());
    Najblizsi(ps2,GetZacStrela2(),min_x,min_y,min_vzdialSpravca);
    if((vzdialSpravca(Chuck.GetPx(),Chuck.GetPy(),ps2->GetPx(),ps2->GetPy()))<min_vzdialSpravca)
    {
      min_x = Chuck.GetPx();
      min_y = Chuck.GetPy();
      min_vzdialSpravca = vzdialSpravca(Chuck.GetPx(),Chuck.GetPy(),ps2->GetPx(),ps2->GetPy());
    }
    if(min_vzdialSpravca<300)
    {
      ps2->SetUhol(ps2->GetUhol()+(Vyber_smer(ps2->GetPx(),ps2->GetPy(),min_x,min_y,ps2->GetUhol(),ps2->GetOtacanie()))*ps2->GetOtacanie());
    }
  
    ps2->Pohni();
    if(ked_vykresli == KKF)
      ps2->Nakresli();
  }
  

  
  //urobi vsetko s Chucom
  
  Chuck.Nakresli();
  
  if(ked_vykresli == KKF) //vykresli to kazdy KKF-ty frame
  {
    stretch_blit(screen_n,screen,0,0,1024,(int) ((double)768*9/10),0,0,SCREEN_W,(int) ((double)SCREEN_H*9/10));
    ked_vykresli = 0;
  }
  
  ked_vykresli++;
}

