#include "graph.h"
#include "node.h"

void Graph::create_edges(){
  Node* pr = nodes.back();
  for(Node *akt : nodes){
    make_edge(akt, pr);
    pr = akt;
  }
}

void Graph::create_vertices(){
  for(int a = 0; a < setting.n; a++){
    nodes.push_back(new Node(setting.get_p(),a));
  }
  initialize_one_cooperator();
}

Graph:: Graph(Setting st, bool cr):Graph(st){
  create_vertices();
  if(cr)
    create_edges();
}

Graph:: Graph(Setting st){
  setting = st;
  steps = 0;
}

int Graph::get_steps(){
  return steps;
}

int Graph::sum_cooperators(){
  int sum = 0;
  for(auto x : nodes){
    sum += x->cooperator;
  }
  return sum;
}

bool Graph::finished_half(){
  int num_finished = 0;
  for(auto node: nodes){
    num_finished += node->cooperator;
  }
  if(num_finished > setting.n/5){
    for(auto node: nodes){
      node->change(true);
    }
  }
  return finished();
}

bool Graph::finished(){
  bool firstType = nodes.front()->cooperator;
  bool finished = true;
  for(auto node: nodes){
    finished = finished && firstType == node->cooperator;
  }
  return finished;
}

bool Graph::fixated(){
  return finished() && nodes.back()->cooperator;
};

void Graph::make_step(){
  int to_die = rand() % nodes.size();

  if(nodes[to_die]->neighbors.size() == 1){
    nodes[to_die]->change(nodes[to_die]->neighbors[0]->cooperator);
  }else{
    double sum_payoff = 0;
    for(auto nei: nodes[to_die]->neighbors){
      sum_payoff += nei->compute_payoff(setting.get_b(), setting.get_s());
    }
    double f = sum_payoff*(float)rand()/(float)RAND_MAX;
    int i = 0;
    f -= nodes[to_die]->neighbors[i]->compute_payoff(setting.get_b(),setting.get_s());
    while(f >= 0 && i < (int)nodes[to_die]->neighbors.size()-1){
      i++;
      f -= nodes[to_die]->neighbors[i]->compute_payoff(setting.get_b(),setting.get_s());
    }
    if(nodes[to_die]->cooperator != nodes[to_die]->neighbors[i]->cooperator) steps ++;
    nodes[to_die]->change(nodes[to_die]->neighbors[i]->cooperator);
  }

  double to_mut = (float)rand()/(float)RAND_MAX;
  if(setting.get_mut_exam()&& to_mut < setting.get_mutation_rate()){
    nodes[to_die]->cooperator = !nodes[to_die]->cooperator;
  }
}

void Graph::run(){
  while(!finished_half()){
    for(int a = 0; a < 100*(int)nodes.size(); a++){
      make_step();
      //if(rand() % 100000 == 0)
      //  print_state();
    }
    
    //print_state();
  }
}

void Graph::run_logging(int steps, int log_every){
  for(int a = 0; a < steps; a++){
    make_step();
    if(a%log_every == 0){
      log();
      //if(finished()) break;
    }
  }
}

Graph::~Graph(){
  for(auto nn : nodes){
    delete nn;
  }
}

void Graph::log(){
  float proportion = (float)sum_cooperators()/(float)nodes.size();
  cout<<proportion<<endl;
}

void Graph::print_state(){
  cout<<"cooperators: "<<sum_cooperators()<<endl;
}

void Graph::make_edge(Node* fr, Node* se){
  edges.push_back(make_pair(fr,se));
  se->add_neighbor(fr);
  fr->add_neighbor(se);
}

void Graph::reset_everything(){
  for(auto nn : nodes){
    float f = (float)rand()/(float)RAND_MAX;
    nn->cooperator = (f < setting.get_p());
  }
  initialize_one_cooperator();
}


void Graph::initialize_one_cooperator(){
  if(setting.random_one){
    int to_cooperate = rand() % nodes.size();
    nodes[to_cooperate]->cooperator = true;
  }
}

void Graph::print_name(){
  cout<<"Basic Graph"<<endl;
}
