#include "ChronoReadingManager.h"
#include "FileManager.h"
#include <Preferences.h>

static const char* PROFILE_NS = "profile";
static const char* GUN_ID_KEY = "gunId";
static const char* PROJ_ID_KEY = "projId";

// Constructor implementation
ChronoReadingManager::ChronoReadingManager() {}
void ChronoReadingManager::initializeProfiles(){
  FileManager& files = FileManager::getInstance();
  files.loadGunProfiles(_gunProfiles);
  files.loadProjectileProfiles(_projectileProfiles);

  Preferences prefs;
  prefs.begin(PROFILE_NS, true);

  int savedGunId = prefs.getInt(GUN_ID_KEY, -1);
  int savedProjId = prefs.getInt(PROJ_ID_KEY, -1);
  prefs.end();

  GunProfile gp("", GunProfile::PROFILE_TYPE_AIR_GUN_FAC);
  ProjectileProfile pp("", ProjectileProfile::PELLET, 0.0, 0.0);

  if (savedGunId >= 0 && savedGunId < _gunProfiles.size()) {
    gp = _gunProfiles.get(savedGunId);
  } else if (_gunProfiles.size() > 0) {
    gp = _gunProfiles.get(0);
  }
  if (savedProjId >= 0 && savedProjId < _projectileProfiles.size()) {
    pp = _projectileProfiles.get(savedProjId);
  } else if (_projectileProfiles.size() > 0) {
    pp = _projectileProfiles.get(0);
  }

  _currentProfile = new ChronoProfile(gp, pp);
}
ChronoReadingManager& ChronoReadingManager::getInstance(){
  static ChronoReadingManager instance;
  return instance;
}
LinkedList<GunProfile>& ChronoReadingManager::getGunProfiles(){return _gunProfiles;}
void ChronoReadingManager::setGunProfile(int id){
  GunProfile p=_gunProfiles.get(id);
  _currentProfile->setGunProfile(p);
  Preferences prefs;
  prefs.begin(PROFILE_NS, false);
  prefs.putInt(GUN_ID_KEY, id);
  prefs.end();
}
void ChronoReadingManager::addGunProfile(GunProfile profile, bool writeToDisk){
  if (writeToDisk){
   FileManager& files = FileManager::getInstance();
   files.saveGunProfile(profile);
  }
  _gunProfiles.add(profile);
}
void ChronoReadingManager::removeGunProfile(int id){
  //remove from disk
  GunProfile profile=_gunProfiles.get(id);
  FileManager& files = FileManager::getInstance();
  files.removeGunProfile(profile);

  //now remove from local list
  for (int i=0; i<_gunProfiles.size();i++){
    GunProfile gp=_gunProfiles.get(i);
    if (gp.getName()==profile.getName() &&
        gp.getPowerLevel()==profile.getPowerLevel()){
          _gunProfiles.remove(i);
          break;
        }
  }
}
LinkedList<ProjectileProfile>& ChronoReadingManager::getProjectileProfiles(){return _projectileProfiles;}
void ChronoReadingManager::setProjectileProfile(int id){
  ProjectileProfile pp=_projectileProfiles.get(id);
  _currentProfile->setProjectileProfile(pp);
  Preferences prefs;
  prefs.begin(PROFILE_NS, false);
  prefs.putInt(PROJ_ID_KEY, id);
  prefs.end();
}
void ChronoReadingManager::addProjectileProfile(ProjectileProfile profile, bool writeToDisk){
  if (writeToDisk){
   FileManager& files = FileManager::getInstance();
   files.saveProjectileProfile(profile);
  }
  _projectileProfiles.add(profile);
}
void ChronoReadingManager::removeProjectileProfile(int id){
  //remove from disk
  ProjectileProfile profile=_projectileProfiles.get(id);
  FileManager& files = FileManager::getInstance();
  files.removeProjectileProfile(profile);

  //now remove from local list
  for (int i=0; i<_projectileProfiles.size();i++){
    ProjectileProfile p=_projectileProfiles.get(i);
    if (p.getName()==profile.getName() &&
        p.getType()==profile.getType() &&
        p.getCaliber()==profile.getCaliber() &&
        p.getWeight()==profile.getWeight()){
          _projectileProfiles.remove(i);
          break;
        }
  }
}
int ChronoReadingManager::getShotCount(){return _chronoReadings.size();}
int ChronoReadingManager::getLastFPS(){
  if (_chronoReadings.size()>0){
    return _chronoReadings.get(_chronoReadings.size()-1).getFPS();
  }
  else{
    return 0;
  }
}
int ChronoReadingManager::getLastFPE(){
  if (_chronoReadings.size()>0){
    return _chronoReadings.get(_chronoReadings.size()-1).getFPE();
  }
  else{
    return 0;
  }
}
ChronoProfile& ChronoReadingManager::getProfile(){return *_currentProfile;}
LinkedList<ChronoReading>& ChronoReadingManager::getChronographReadings(){return _chronoReadings;}
void ChronoReadingManager::addReading(int fps){
  ChronoReading reading(fps,*_currentProfile);
  _chronoReadings.add(reading);
}
int ChronoReadingManager::getLowFPS() {
  int low=0;
  for(int i=0;i<_chronoReadings.size();i++){
    if (i==0 || _chronoReadings.get(i).getFPS()<low){
      low=_chronoReadings.get(i).getFPS();
    }
  }
  return low;
}
int ChronoReadingManager::getHighFPS() {
  int high=0;
  for(int i=0;i<_chronoReadings.size();i++){
    if (i==0 || _chronoReadings.get(i).getFPS()>high){
      high=_chronoReadings.get(i).getFPS();
    }
  }
  return high;
}
int ChronoReadingManager::getAvgFPS(){
    int avg=0;
    if (_chronoReadings.size()>0){
        int total=0;
        for (int i=0;i<_chronoReadings.size();i++){
            total=total+_chronoReadings.get(i).getFPS();
        }
        avg=total/_chronoReadings.size();
    }
    return avg;
}
float ChronoReadingManager::getStandardDeviation(){
    float sd=0.0;
    float total=0.0;
    float avg=0.0;

    if (_chronoReadings.size()==0){
        return 0;
    }

    //get the average
    for (int i = 0; i < _chronoReadings.size(); i++) { // Loop through the array elements
        total=total+_chronoReadings.get(i).getFPS();
    }
    avg=total/_chronoReadings.size();

    //get the sum of squared differences
    float squaredTotal=0.0;
    for (int i = 0; i < _chronoReadings.size(); i++) { // Loop through the array elements
        float diff=_chronoReadings.get(i).getFPS()-avg;
        squaredTotal=squaredTotal+(diff*diff);
    }
    float squaredAvg=squaredTotal/_chronoReadings.size();
    sd=sqrt(squaredAvg);

    return sd;
}
int ChronoReadingManager::getSpread(){
  int low=getLowFPS();
  int high=getHighFPS();
  return high-low;
}
void ChronoReadingManager::reset(){
  _chronoReadings.clear();
}