#ifndef mam_hlavicka
#define mam_hlavicka

#include<iostream>
#include<fstream>
#include<vector>
#include<sstream>
#include<map>
#include<stack>
#include<queue>
#include<cmath>
#include "ac.h"

using namespace std;

typedef struct {
  FILE *fp;
  long low;
  long high;
  long fbits;
  int buffer;
  int bits_to_go;
  long total_bits;
} ac_encoder;

typedef struct {
  FILE *fp;
  long value;
  long low;
  long high;
  int buffer;
  int bits_to_go;
  int garbage_bits;
} ac_decoder;

typedef struct {
  int nsym;
  int *freq; //
  long *cfreq; //pravdepodobnosti pismen tj. P(a),P(c)+P(a),P(g)+P(c)+P(a) atd.
  int adapt;	//ci upravovat frequencie symbolov
} ac_model;


typedef map<char,int> kod_abeceda;//abeceda vstupnej sekvencie, viac pismen moze znamenat to iste
typedef map<int,char> dek_abeceda;//abeceda vystupnej sekvencie, kazda trieda ekviv. pismen ma max. jedneho reprez. 



class Cislo{
  double hodnota;
  bool nula;
  public:
    Cislo(int x);
    Cislo(long x);
    Cislo(double x);
    Cislo();
    Cislo(const Cislo& arg);
    Cislo(const Cislo* arg);
    void nastav(const double tato);
    double daj_hod() const;
    double normal() const;
    bool som_nula() const;
    ~Cislo();
    Cislo operator=(const Cislo&);
};
bool operator> (const Cislo c1, const Cislo c2);
bool operator< (const Cislo c1, const Cislo c2);
Cislo operator* (const Cislo c1, const Cislo c2);
Cislo operator/ (const Cislo c1, const Cislo c2);
Cislo operator+ (const Cislo c1, const Cislo c2);
Cislo sum(priority_queue<Cislo,vector<Cislo>, greater<Cislo> > a);

//typedef Cislo* Aktual;


class Aktual{
  public:
  int pocet_stavov;
  Cislo* P_stavov;
  Aktual(Cislo*,int);
  Aktual(const Aktual& arg);
  Aktual(const Aktual* arg);
  Cislo* daj() const;
  Aktual(int);
  Aktual& operator= (const Aktual&);
  Cislo operator[] (const int p){
    return Cislo(P_stavov[p]);
  }
  Aktual();
  ~Aktual();
};



//typedef pair<Cislo,/*P vypisania*/Aktual/*rozlozenie P stavov*/> policko;
class policko{
  public:
Cislo first;
Aktual second;
policko& operator= (const policko&);
policko(const policko& arg);
policko(const Cislo&, const Aktual&);
//~policko();
policko();
}; 
typedef priority_queue<Cislo,vector<Cislo>, greater<Cislo> > halda;


/*
zapis nekomprimovanych znakov bude zabezpecovat ta trieda, kt. prave cita vstup
*/

class vv_sek{//zabezpecuje vstup(bufferuje sekvenciu)/vystup sekvencie, pricom dostane/da jej cislo
  kod_abeceda* kod;
  dek_abeceda* dek;
  int dlzka_kod_sekv;
  int pocet_pismen;
  long sekv;
  int mam;
  bool zac;//ci uz zacal s vypisom komprim sekv. alebo treba pisat #
  bool koncim;//nacital som pismeno ktore nie je v kodovacej abecede a musim teda ukoncit kodovanie
  vector<char> sekvencia;//tu bude nacitana a zatial nesprac. sekvencia, ked skonci komprimaciu, tak ju jednoducho vypise
  public:
  vv_sek(int dlzka_kod_sekv,int pocet_pismen, kod_abeceda* kod,dek_abeceda* dek);
  int dlzka_sekvencie;//po kolko znakov sa koduje/dek.
  long daj();//vrati cislo nasledujucej sekvencie danej dlzky zo vstupu, v sustave kt. zakladom je pocet pismen
  void pis(long);//zapise sekvenciu s danym cislom na vystup
  bool ber(char);//dostane pismeno zo vstupu a vrati true, ak ma dost pismen
  bool koniec();
  void ukonci();
  bool zacal();
};

class vv_bool{//zabezpecuje vstu/vystup binarneho zapisu
  queue<bool> zostatok_vystup;
  queue<bool> zostatok_vstup;// v podstate mohla byt len jedna fronta, toto je dan za prehladnost
  int pocet; //pocet prvkov vo fromte
  public:
  vv_bool();
  //void pis(stack<bool>);
  //void pis(queue<bool>);
  void pis(vector<bool>);
  bool daj();
  void ukonci_vystup();//vypise zvysok vo fronte a ostatok doplni nulami
  void ukonci_vstup();//vyprazdni vstupnu frontu a prepne sa do modu len prepisovania pismenok
};

class vrchol{
  vrchol* syn0;
  vrchol* syn1;
  vrchol* p_otec;
  long sek;
  Cislo* p_vaha;
  bool p_som;
  bool koniec; //bude kodovat koniec sekvencie
  public:
    vrchol(char t);
    vrchol(vrchol* s0,vrchol* s1);
    vrchol(Cislo c,long s,Aktual A1);
    vrchol(Cislo c,long s);
    vrchol();
    vrchol(const vrchol& arg);
    ~vrchol();
    bool som()const;
    bool koncim()const;
    Cislo vaha()const;
    void otec(vrchol* v);
    void povedz();
    long sekvencia()const;
    vrchol* l_syn()const;
    vrchol* p_syn()const;
    vrchol* motec()const;
    void zmaz();
    vrchol& operator=(const vrchol& arg);
};


bool operator< (vrchol v1,vrchol v2);
bool operator> (vrchol v1,vrchol v2);

class ukaz{
  public:
  vrchol* moj;
  ukaz(vrchol* v);
  ukaz(const ukaz& arg);
  ukaz();
  void zmaz();
  ukaz& operator=(const ukaz& arg);
};

bool operator< (ukaz u1, ukaz u2);

bool operator> (ukaz u1, ukaz u2);


class Model{
  Cislo** P_prechodov;// [stav z][stav do]
  Cislo** P_emisii;// [stav][sekvencia]
  Aktual* A;// P stavov pred kodovanim
  Aktual* B;// momentalne P stavov
  int* rad;//pole s radmi stavov
  long dlzka;//dlzka kodovanej sekvencie
  long pocet_stavov;
  long pocet_pismen;
  bool poc_rozdelenie;
  long predchadzajuca_sq; //cislo predchadzajucej sekvencie default 0;
  long* delitel;//ked bude treba dostat zo sekvencie poslednych x pismen vydeli sa cislo reprez. sekv. polickom x-1
  //zvysok podeleni bude poslednych x pismen
  int max_rad;//najvacsi rad stavu
  //map<long,map<long,map<long,pair<Cislo,Aktual> > > > predpocitane;// [predch. sekvencia][stav][vypis. sekvencia]<P,aktual> 
  policko*** Predpocitane; //[predch.sekv][stav][vypis_sekvencia] pair<Cislo,Aktual>
  long pocet_predpoc;
  public:
  Model(ifstream* g);
  void predpocitaj(int dlzka);
  priority_queue<ukaz,vector<ukaz>, greater<ukaz> > vrat(long sekvencia);
  void reset();
  priority_queue<ukaz,vector<ukaz>, greater<ukaz> > nova();
  void novy_A(long sekvencia);
  int pocet();
  void vypis_sa();
  void napln();
  void update_model(ac_model* acm,int sym);
  void ac_model_init(ac_model *acm);
   ~Model();
  policko P_sekvencie(long sekvencia,const Cislo* P_stavov);
  policko* predlz(long predchadzajuca_sekvencia,long pocet_stary,policko* stare);
};






/*class kod{
  
  trieda osetrujuca vstup a vystup programu
  potrebujem:
    1, parsovat bool na znaky a spat
    2, vediet priradit znaku cislo podla kluca a naopak
  
  queue<bool> na_kod;
  queue<bool> z_kod;
  queue<char> na_vystup;
  queue<char> na_vstup;
  void ber(bool b);//
  void ber(stack<bool> b);
  void ber(queue<bool> b);
  void ber(char b);
  bool daj_b();// vrati vrch vstupnej fronty
  
};*/


  
/*TODO FUNKCIA PRE VYBUSENIE KODU ZO SMERNIKU NA VRCHOL*/
vector<bool> koduj(vrchol* vrch);
/*FUNKCIE PRE STAVBU STROMCEKOV*/
vrchol* urob_stromcek_zhora(priority_queue<ukaz,vector<ukaz>, greater<ukaz> > vrcholy);
vrchol* urob_stromcek_zdola(priority_queue<ukaz,vector<ukaz>, greater<ukaz> > vrcholy,long sekvencia);
vrchol* urob_stromcek_koniec(priority_queue<ukaz,vector<ukaz>, greater<ukaz> > vrcholy);

void ac_encoder_init (ac_encoder *, const char *);
void ac_encoder_done (ac_encoder *);
void ac_decoder_init (ac_decoder *, const char *);
void ac_decoder_done (ac_decoder *);
void ac_model_init (ac_model *, int, int *, int, Model*);
void ac_model_done (ac_model *);
long ac_encoder_bits (ac_encoder *);
void ac_encode_symbol (ac_encoder *ace, ac_model *acm, int sym,Model* M);
int ac_decode_symbol (ac_decoder *acd, ac_model *acm, Model* M);
  

#endif