#include <xvt.h>
#include <currency.h>
#include <diction.h>
#include <prefix.h>
#include <recarray.h>

#include <xvt.h>
#include <statbar.h>

///////////////////////////////////////////////////////////
// DowJones
///////////////////////////////////////////////////////////

class TDowJones : public TFile_cache
{
  struct TExchangeData : public TObject
  {
    real _chg;         // Cambio 
    int  _dec;         // Decimali per gli importi normali
    int  _dec_prices;  // Decimali per i prezzi unitari
    int  _dec_change;  // Decimali per i cambi
    bool _is_euro;     // E' l'EURO in persona
    exchange_type _et; // Il cambio e' espresso contro EURO

  public:
    const TExchangeData& operator=(const TExchangeData& d) 
    { 
      _chg = d._chg; _dec = d._dec; _dec_prices = d._dec_prices; 
      _is_euro = d._is_euro; _et = d._et;
      return d; 
    }
    TExchangeData()
      : _chg(1.0), _dec(0), _dec_prices(0), _is_euro(FALSE), _et(_exchange_base) { }
  };

  long _codditta;
  TString16 _base_val, _firm_val, _euro_val;
  real _euro_chg;

protected:
  virtual TObject* rec2obj(const TRectype& rec) const;
  
  void test_cache();
  const TExchangeData& get(const char* key);

public:
  const TString& get_base_val();
  const TString& get_firm_val();
  const TString& get_euro_val();
  void force_firm_val(const char* val);
  const char* expand_value(const char* val);
  const char* normalize_value(const char* val, const real& chg, exchange_type& et);
  
  real exchange(const real& num, 
                const char* fromval, const real& fromchg, exchange_type fromeuro,
                const char* toval, const real& tochg, exchange_type toeuro,
                int price = 0);
                
  real exchange(const real& num, const TExchange& frval, const TExchange& toval, int price = 0);    
                
  int get_dec(const char* val, bool prices = FALSE);
  const real& get_change(const char* val, exchange_type& contro_euro);
  real get_base_change(const char* val);
  real get_contro_change(const char* val);

  virtual void flush();
  
  TDowJones() : TFile_cache("%VAL"), _codditta(0) { }
  virtual ~TDowJones() { }
} DowJones;

TObject* TDowJones::rec2obj(const TRectype& rec) const
{
  TExchangeData* data = new TExchangeData;
  
  const TString4 codval = rec.get("CODTAB");
  data->_chg = rec.get_real("S4");
  if (data->_chg <= ZERO)
  {
    data->_chg = rec.get_real("R10");
    if (data->_chg <= ZERO)
    {                    
#ifdef DBG      
      if (codval.not_empty() && codval != _firm_val)
      {
        TString msg = TR("Codice valuta senza cambio");
        msg << ": " << codval;
        statbar_set_title(TASK_WIN, msg);
      }
#endif
      data->_chg = 1.0;
    }
  }
  
  if (codval.not_empty())
  {
    data->_dec = rec.get_int("I0");
    data->_dec_prices = rec.get_int("I1");
    if (data->_dec_prices < data->_dec)
      data->_dec_prices = data->_dec;
    data->_dec_change = rec.get_int("I2");
    data->_is_euro = rec.get_bool("B0");
    data->_et = rec.get_bool("B1") ? _exchange_contro : _exchange_base;
  }

  return data;
}

void TDowJones::flush()
{
  _base_val.cut(0);
  _firm_val.cut(0);
  _euro_val.cut(0);
  kill_file();
}

void TDowJones::test_cache()
{           
  // Controllo se e' cambiata la ditta
  const long newfirm = prefix().get_codditta();
  if (newfirm != _codditta)
  {
    _codditta = newfirm;
    _firm_val = prefix().firm().codice_valuta();
  }  

  if (_base_val.empty())     
  {
    fill();
    FOR_EACH_ASSOC_OBJECT(_cache, hash, key, obj) if (*key)
    {
      const TExchangeData& data = *(const TExchangeData*)obj;
      if ((_base_val.empty() || strcmp(key, "LIT") == 0) && data._chg == 1.0)
        _base_val = key;
      if (data._is_euro && _euro_val.empty())
        _euro_val = key;
    }

    if (_euro_val.empty())  // Si son dimenticati dell'EURO?
    {                           
      TExchangeData* euro = new TExchangeData;
      euro->_chg = 1936.27; euro->_dec = euro->_dec_prices = 2;
      euro->_is_euro = TRUE; euro->_et = _exchange_base;
      _euro_val = "EUR";
      _cache.add(_euro_val, euro);
    }

    if (_base_val.empty())  // Si son dimenticati delle LIRE?
    {
      TExchangeData* lira = new TExchangeData;
      lira->_chg = 1.0; lira->_dec = lira->_dec_prices = 0;
      lira->_is_euro = FALSE; lira->_et = _exchange_base;
      _base_val = "LIT";
      _cache.add(_base_val, lira);
    }            
    _firm_val = prefix().firm().codice_valuta();
    _euro_chg = get_base_change(_euro_val);
  }
}

const char* TDowJones::expand_value(const char* val)
{
  if (val && (*val > ' ' || *val < '\0'))  
  {
    if (*val == '_')
    {
      if (xvt_str_compare_ignoring_case(val, "_FIRM") == 0)
        val = get_firm_val(); else
      if (xvt_str_compare_ignoring_case(val, "_EURO") == 0)
        val = get_euro_val(); else
      if (xvt_str_compare_ignoring_case(val, "_BASE") == 0)
        val = get_base_val();
    }
    else
    {
    // Leave code as is
    }
  }
  else
    val = get_firm_val();
    
   return val;
}

const TDowJones::TExchangeData& TDowJones::get(const char* val)
{
  test_cache();
  return (const TExchangeData&)query(expand_value(val));
}  

const TString& TDowJones::get_base_val()
{
  test_cache();
  return _base_val;
}

const TString& TDowJones::get_firm_val()
{
  test_cache();
  return _firm_val;
}

const TString& TDowJones::get_euro_val()
{
  test_cache();
  return _euro_val;
}

void TDowJones::force_firm_val(const char* val)
{
	test_cache();
	_firm_val = val;
}

real TDowJones::exchange(const real& num,     // Importo da convertire
                         const char* frval,   // Dalla valuta
                         const real& frchg,   // Dal cambio 
                         exchange_type freur, // Dal cambio in euro
                         const char* toval,   // Alla valuta
                         const real& tochg,   // Al cambio 
                         exchange_type toeur, // Al cambio in euro
                         int price)           // e' un prezzo ?
{
  real n = num;
  if (!n.is_zero())
  {
    const TExchangeData& datafr = get(frval);
    const TExchangeData& datato = get(toval);
    
    real fr = frchg;
    if (fr <= ZERO) { fr = datafr._chg; freur = datafr._et; }
    
    real to = tochg;                    
    if (to <= ZERO) { to = datato._chg; toeur = datato._et; }

    const int mode = (freur == _exchange_contro ? 1 : 0) + (toeur  == _exchange_contro ? 2 : 0);
    switch (mode)
    {                                      
    case 1: 
      n = (n * _euro_chg) / (fr * to); // Modo misto 1
      break;  
    case 2: 
      n = n * (fr * to) / _euro_chg; // Modo misto 2
      break;   
    case 3:
      n = n * to / fr;               // Nuovo modo
      break;                       
    default: 
      n = n * fr / to;               // Vecchio modo
      break;  
    }
    if (price == 0 || price == 1) // Arrotonda solo in caso normale
      n.round(price ? datato._dec_prices : datato._dec); 
  }
  return n;
}

real TDowJones::exchange(const real& num,        // Importo da convertire
                         const TExchange& frval, // Dalla valuta
                         const TExchange& toval, // Alla valuta
                         int price)             // e' un prezzo ?
{                                         
  exchange_type fret, toet;
  const real& frch = frval.get_change(fret);
  const real& toch = toval.get_change(toet);
  return exchange(num, frval.get_value(), frch, fret, toval.get_value(), toch, toet, price);
}                         

int TDowJones::get_dec(const char* val, bool price)
{
  const TExchangeData& data = get(val);
  return price ? data._dec_prices : data._dec;
}

const real& TDowJones::get_change(const char* val, exchange_type& et)
{
  const TExchangeData& data = get(val);
  et = data._et;
  return data._chg;
}

real TDowJones::get_base_change(const char* val)
{
  exchange_type et; 
  real c = get_change(val, et);
  if (et == _exchange_contro)
  {
    c = _euro_chg / c;
    c.round(6);
  }  
  return c;  
}

real TDowJones::get_contro_change(const char* val)
{
  exchange_type et; 
  real c = get_change(val, et);
  if (et == _exchange_base)
  {
    c = _euro_chg / c;
    c.round(6);
  }  
  return c;  
}

const char* TDowJones::normalize_value(const char* val, const real& exch, exchange_type& et)
{
  val = expand_value(val);
  if (et == _exchange_undefined)
  {                             
    if (exch > ZERO)
    {
      real base_diff = get_base_change(val) - exch; 
      base_diff = abs(base_diff);
      
      real contro_diff = get_contro_change(val) - exch; 
      contro_diff = abs(contro_diff);
      
      et = base_diff < contro_diff  ? _exchange_base : _exchange_contro;
    }
    else
      get_change(val, et);
  }
  return val;
}

///////////////////////////////////////////////////////////
// TExchange
///////////////////////////////////////////////////////////

void TExchange::copy(const TExchange& exc)
{
  strcpy(_val, exc._val);
  _exchange = exc._exchange;
  _et = exc._et;
} 

const real& TExchange::get_change(exchange_type& et) const
{          
  if (_exchange.is_zero())
    return DowJones.get_change(_val, et);
  et = _et;
  return _exchange;
}

real TExchange::get_base_change() const
{
  exchange_type et;
  real c = get_change(et);
  if (et != _exchange_base)
  {
     c = DowJones.get_base_change("_EURO") / c;
     c.round(6);
  }   
  return c;
}

real TExchange::get_contro_change() const
{
  exchange_type et;
  real c = get_change(et);
  if (et != _exchange_contro)
  {
     c = DowJones.get_base_change("_EURO") / c;
     c.round(6);
  }   
  return c;
}

int TExchange::compare(const TSortable& obj) const
{                                     
  const TExchange& exc = (const TExchange&)obj;
  int cmp = strcmp(_val, exc._val);
  if (cmp == 0)
  {   
    if (_et == exc._et)
    {                   
      exchange_type dummy;
      const real diff = get_change(dummy) - exc.get_change(dummy);
      cmp = diff.sign();
    }
    else
    {
      real chg1 = get_contro_change();
      real chg2 = exc.get_contro_change();
      const real diff = chg1 - chg2;
      cmp = diff.sign();
    }
  }
  return cmp;
}

bool TExchange::same_value_as(const TExchange& exc) const
{
  return strcmp(_val, exc._val) == 0;
}

bool TExchange::is_firm_value() const
{
  return DowJones.get_firm_val() == _val;
}

bool TExchange::is_euro_value() const
{
  return DowJones.get_euro_val() == _val;
}

int TExchange::decimals(bool price) const
{
  return DowJones.get_dec(_val, price);
}                      

void TExchange::set(const char* val, const real& chg, exchange_type et)
{
  val = DowJones.normalize_value(val, chg, et);
  strncpy(_val, val, 4); 
  _val[3] = '\0';
  _exchange = chg;
  _et = et;
}

void TExchange::set(const TRectype& rec)
{
  const TString4 codval = rec.get("CODVAL");
  const real chg = rec.get_real("CAMBIO");
  exchange_type et = _exchange_undefined;
  if (rec.exist("CONTROEURO"))
    et = rec.get_bool("CONTROEURO") ? _exchange_contro : _exchange_base;
  set(codval, chg, et);
}

TExchange::TExchange(const char* val, const real& chg, exchange_type et)
{
  set(val, chg, et);
}

TExchange::TExchange(const TRectype& rec)
{
  set(rec);
}

///////////////////////////////////////////////////////////
// TCurrency
///////////////////////////////////////////////////////////

const TString& TCurrency::get_base_val()
{
  return DowJones.get_base_val();
}

const TString& TCurrency::get_firm_val()
{
  return DowJones.get_firm_val();
}

const TString& TCurrency::get_euro_val()
{
  return DowJones.get_euro_val();
}

int TCurrency::get_base_dec(bool price)
{
  return DowJones.get_dec(NULL, price);
}

int TCurrency::get_firm_dec(bool price)
{
  return DowJones.get_dec(get_firm_val(), price);
}

int TCurrency::get_euro_dec(bool price)
{
  return DowJones.get_dec(get_euro_val(), price);
}

const real& TCurrency::get_firm_change(exchange_type& ce)
{
  return DowJones.get_change("_FIRM", ce);
}

const real& TCurrency::get_euro_change()
{                     
  exchange_type dummy;               
  return DowJones.get_change("_EURO", dummy);
}
  
void TCurrency::force_firm_val(const char* val)
{
  DowJones.force_firm_val(val);
}

void TCurrency::force_cache_update()
{
  DowJones.flush();
}
  
void TCurrency::force_value(const char* newval, const real& newchange, exchange_type newet)
{       
  _chg.set(newval, newchange, newet);
}

void TCurrency::change_value(const TExchange& exc)
{
  if (!_num.is_zero() && _chg != exc)
    _num = DowJones.exchange(_num, _chg, exc, is_price());
  _chg = exc;
}

void TCurrency::change_value(const char* val, const real& newchange, exchange_type to_euro)
{
  const TExchange exc(val, newchange, to_euro);
  change_value(exc);
}

int TCurrency::decimals() const
{
  return DowJones.get_dec(get_value(), is_price());
}                      

int TCurrency::compare(const TSortable& s) const
{
  const TCurrency& cur = (const TCurrency&)s;
  if (same_value_as(cur))
  {
    return _num == cur._num ? 0 : (_num > cur._num ? +1 : -1);
  }
  TCurrency curr(cur);
  curr.change_value(get_value());
  return _num == curr._num ? 0 : (_num > curr._num ? +1 : -1);
}

void TCurrency::copy(const TCurrency& cur)
{
  _chg = cur._chg;
  _num = cur._num;
  _price = cur._price;
}

const char* TCurrency::string(bool dotted) const
{
  if (dotted)
  {
    TString16 picture;
    picture.format(".%d", decimals());
    return _num.string(picture);
  }
  return _num.stringa(0, decimals());
}

TCurrency& TCurrency::operator+=(const TCurrency& cur)
{
  CHECK(is_price() == cur.is_price(), "Somma di pere e mele!");
  if (!cur._num.is_zero())
 {
    if (same_value_as(cur))
    {
      _num += cur._num;
    }
    else
    {
      real n = DowJones.exchange(cur._num, cur._chg, _chg, is_price());
      _num += n;
    }
  }  
  return *this;
}

TCurrency TCurrency::operator+(const TCurrency& num) const
{
  TCurrency cur(*this);
  cur += num;
  return cur;
}

TCurrency& TCurrency::operator-=(const TCurrency& cur)
{
  CHECK(is_price() == cur.is_price(), "Sottrazione di pere e mele!");
  if (same_value_as(cur))
    _num -= cur._num;
  else
  {
    real n = DowJones.exchange(cur._num, cur._chg, _chg, is_price());
    _num -= n;
  }
  return *this;
}

TCurrency TCurrency::operator-(const TCurrency& num) const
{
  TCurrency cur(*this);
  cur -= num;
  return cur;
}

TCurrency& TCurrency::operator*=(const real& num) 
{ 
  _num *= num; 
  _num.round(decimals());
  return *this; 
}

TCurrency TCurrency::operator*(const real& num) const
{
  TCurrency cur(*this);
  cur *= num;
  return cur;
}

TCurrency TCurrency::abs() const
{
  if (_num.sign() < 0)
  {
    TCurrency k(-_num, _chg, is_price());
    return k;
  }
  return *this;  
}

bool TCurrency::is_base_value() const
{
  return get_base_val() == get_value();
}

bool TCurrency::is_firm_value() const
{
  return _chg.is_firm_value();
}

TCurrency::TCurrency(const real& num, const char* val, const real& exchg, exchange_type et, bool price)
         : _num(num), _price(price)
{
  force_value(val, exchg, et);
  _num.round(decimals());
}

TCurrency::TCurrency(const real& num, const TExchange& exc, bool price)
         : _chg(exc), _num(num), _price(price)
{ 
  _num.round(decimals());
}

bool same_values(const char * valuea, const char * valueb)
{     
  if (valuea == NULL || *valuea == '\0')
    valuea = TCurrency::get_firm_val();
  if (valueb == NULL || *valueb == '\0')
    valueb = TCurrency::get_firm_val();
    
  return xvt_str_compare_ignoring_case(valuea, valueb) == 0;  
}


real change_currency(const real& num, 
                     const char* fromval, const real& fromchg, exchange_type fromeuro,
                     const char* toval, const real& tochg, exchange_type toeuro,
                     int price)
{
  return DowJones.exchange(num, fromval, fromchg, fromeuro, toval, tochg, toeuro, price);
}