#ifndef GALAXY_H
#define GALAXY_H

#include <array>
#include <cstdint>
#include <iostream>
#include <ostream>
#include <regex>
#include <set>
#include <stdexcept>
#include <string>
#include <vector>

#include "buysell.h"
#include "json.hpp"
using json = nlohmann::json;

// #define MAX_WARPS 6

typedef uint16_t sector_type;

struct sector_warps {
  sector_type sector;  // Yes, for debug
  // std::set<sector_type> warps;  // possibly
  std::set<sector_type> warps;
  // sector_type warps[MAX_WARPS];
  // ports
  // planets
  // ctor that zeros everything out?
  sector_warps();
  sector_warps(sector_type, std::set<sector_type>);
  void add(sector_type sector);
  // add() that adds warp to end of warps?
  friend std::ostream& operator<<(std::ostream& os, const sector_warps& warps);
  // bool operator==(const sector_warps& rhs) const;
  // void sort(void);
};

/**
 * Find possible trades with these two port types
 *
 * 0 = NONE
 * 1 = GOOD OE trade pair
 * 2 = OK trade pair
 * 3 = FAIR (one buys, one sells)
 *
 * This will probably be expanded in the future to return:
 *    What port(s) to trade with.  (pos, return top 2)
 *
 * @param port1
 * @param port2
 * @return int
 */
// int trade_type(port_type port1, port_type port2);

enum trade_types {
  NONE,
  BEST,    // OE PAIR
  OK,      // PAIR
  FAIR_E,  // BS E
  FAIR_O,  // BS O
  FAIR_F,  // BS F
};

struct trade_type_result {
  trade_types type;
  buysell trades;
};
// trade_type_result trade_type_info(port_type port1, port_type port2);

struct port_pair_type {
  int type;
  buysell trades;
  sector_type s1, s2;
};

struct port {
  sector_type sector;
  uint8_t type;
  uint16_t amount[3];
  uint8_t percent[3];
  bool unknown(void);
  // port();
  friend std::ostream& operator<<(std::ostream& os, const port& p);
};

struct port parse_portcim(const std::string line);

// store density scan information in galaxy

struct density {
  sector_type sector;
  uint16_t density;
  uint16_t warps;
  uint16_t navhaz;
  bool anomaly;
  bool known;
  friend bool operator==(const struct density lhs, const struct density rhs);
};

class density_scan {
 public:
  sector_type sector;
  std::array<density, 6> d;
  density_scan();

  // resets the scan with a new sector
  void reset(sector_type sector);
  void add_scan(density d);
  density find(sector_type sector);

  // private:
  int pos;
};

class planet {
 public:
  sector_type sector;
  int number;
  int level;
  std::string name;
  char c;  // class
};

// SPECIAL = 0
// STARDOCK = 9

class Galaxy {
 public:
  Galaxy();
  ~Galaxy();

  void reset(void);
  json config;
  json meta;

  int burnt_percent;
  density_scan dscan;

  // warps;
  // ports;
  std::map<sector_type, port> ports;
  std::map<sector_type, sector_warps> warps;
  std::map<int, planet> planets;

  void add_warp(sector_warps);
  void add_port(sector_type sector, int port_class);
  void add_port(port);

  void save(void);
  void load(void);

  sector_type find_nearest_unexplored(sector_type sector);

  std::vector<port_pair_type> find_best_trades(void);
  std::vector<port_pair_type> find_trades(sector_type sector,
                                          bool highest = true);
  trade_type_result trade_type_info(sector_type port1, sector_type port2,
                                    int burnt_percent = 20);
  void sort_port_pair_type(std::vector<port_pair_type>& pptv);
  port_pair_type find_closest(int sector);
  port_pair_type find_closest_trade(int sector, int lowest_trade_type,
                                    int burnt_percent = 20);
  sector_type find_nearest_selling(int sector, int product, int selling=250);
  std::vector<sector_type> find_safe(void);
  char game;
  std::string username;
};

#endif