using System;
using System.Data;
using System.Configuration;
using System.Collections.Generic;
using System.Collections;


/// <summary>
/// Summary description for RischioRelativo
/// </summary>
public class RischioRelativo_PesoPercentuale
{
    public RischioRelativo_PesoPercentuale()
    {
        //
        // TODO: Add constructor logic here
        //
    }



    public RischioRelativo_PesoPercentuale(string area, string nomeprogetto, decimal rischio, string rischiostring, decimal peso)
    {
        _area = area;
        _rischio = rischio;
        _rischiostring = rischiostring;
        _peso = peso;
        _nomeprogetto = nomeprogetto;

    }


    private string _area;
    private string _nomeprogetto;
    private decimal _rischio;
    private string _rischiostring;

    private decimal _peso;
    //private TipoRitorno _tipodiritorno;
        
    private DataTable _datasource;


       

    /// <summary>
    /// Datatable contenente i controvalori di tutte le aree bisogno / progetti per cui vanno calcolati Peso e Rischio.
    /// </summary>
    public DataTable DataSource
    {
        get { return _datasource; }
        set { _datasource = value; }
    }

    public string Area
    {
        get { return _area; }
        set { _area = value; }
    }

    public string NomeProgetto
    {
        get { return _nomeprogetto; }
        set { _nomeprogetto = value; }
    }

    public decimal Rischio
    {
        get { return _rischio; }
        set { _rischio = value; }
    }

    /// <summary>
    /// Rappresentazione in stringa del Rischio relativo nel caso in cuiquesto non sia calcolabile (es VaR = n.d.)
    /// </summary>
    public string RischioString
    {
        get { return _rischiostring; }
        set { _rischiostring = value; }
    }

    public decimal Peso
    {
        get { return _peso; }
        set { _peso = value; }
    }

   

    /// <summary>
    /// Rirorna un DataTable contenente con i valori del Peso Percentuale.
    /// Il nome delle colonne :
    /// AreaBisogno
    /// NomeProgetto
    /// PesoPercentuale
    /// </summary>
    /// <returns></returns>
    public DataTable calcolaPeso()
    {
        DataTable dtResult = new DataTable("dtResult");
        dtResult.Columns.Add("AreaBisogno");
        dtResult.Columns.Add("NomeProgetto");
        dtResult.Columns.Add("PesoPercentuale",typeof(decimal));


        decimal dividendo = 0;
        decimal divisore = 0;
        decimal ctvArea = 0;
        decimal pesopercentuale = 0;

        List<RischioRelativo_PesoPercentuale> _itemsPesoPercentuale = new List<RischioRelativo_PesoPercentuale>();

        DataView dwAreeBisogno = new DataView(_datasource);
        DataTable dtAreeBisogno = dwAreeBisogno.ToTable(true, "need_area");

        if (dtAreeBisogno.Rows.Count == 0)
            return null;

        foreach (DataRow drGlobale in dtAreeBisogno.Rows)
        {
            if (drGlobale["need_area"].ToString().ToLower() != "na") //escludo le risorse non allocate
            {

                //Recupero il dividendo (controvalore dell'area o del singolo progetto)
                DataRow[] rowFilter;
                rowFilter = _datasource.Select("need_area ='" + drGlobale["need_area"] + "'");

                foreach (DataRow row in rowFilter)
                {

                    ctvArea = Convert.ToDecimal(row["CONTROVALOREATTUALE"]);
                    dividendo = Math.Round(ctvArea, 2);

                    // Recupero il divisore
                    divisore = recuperaDivisorePeso(_datasource);

                    if (divisore == 0M)
                        pesopercentuale = 0M;
                    else
                        pesopercentuale = Math.Round((dividendo / divisore) * 100, 2);

                    _itemsPesoPercentuale.Add(new RischioRelativo_PesoPercentuale(row["NEED_AREA"].ToString(), row["nome_progetto"].ToString(), 0,string.Empty, pesopercentuale));
                    divisore = 0;

                }
            }
        }

        _itemsPesoPercentuale = ArrotondaLista(_itemsPesoPercentuale, TipoRitorno.PESO);

        foreach (RischioRelativo_PesoPercentuale obj in _itemsPesoPercentuale)
        {

            DataRow rToAdd = dtResult.NewRow();
            rToAdd["AreaBisogno"] = obj.Area;
            rToAdd["NomeProgetto"] = obj.NomeProgetto;
            rToAdd["PesoPercentuale"] = obj.Peso;
            dtResult.Rows.Add(rToAdd);
        }

        return dtResult;

    }


    /// <summary>
    /// Ritorna un DataTable contenente con i controvalori totali di ciascusa area/progetto.
    /// I controvalori espressi qui sono già comprensivi di patrimonio non rappresentabile.
    /// </summary>
    /// <returns></returns>
    public DataTable calcolaControvaloriAttuali()
    {

        DataTable dtResult = new DataTable("dtResult");
        dtResult.Columns.Add("AreaBisogno");
        dtResult.Columns.Add("NomeProgetto");
        dtResult.Columns.Add("ControvaloreTotale", typeof(decimal));
        dtResult.Columns.Add("Ordinamento_progetto", typeof(int));

        foreach (DataRow dr in _datasource.Rows)
        {
            DataRow rToAdd = dtResult.NewRow();
            rToAdd["AreaBisogno"] = dr["need_area"].ToString();
            rToAdd["NomeProgetto"] = dr["nome_progetto"].ToString();
            rToAdd["ControvaloreTotale"] = dr["ControvaloreAttuale"]== DBNull.Value ? 0 :Convert.ToDecimal(dr["ControvaloreAttuale"]);
            rToAdd["Ordinamento_progetto"] = dr["ORDINAMENTO_PROGETTO"] == DBNull.Value ? 0 : Convert.ToInt32(dr["ORDINAMENTO_PROGETTO"]);
            dtResult.Rows.Add(rToAdd);       
        
        }

        return dtResult;
    
    }


    /// <summary>
    /// Ritorna un DataTable contenente con i valori del Rischio Relativo.
    /// Il nome delle colonne :
    /// AreaBisogno
    /// NomeProgetto
    /// RischioRelativo
    /// 
    /// 
    /// Di seguito la formula utilizzata:
    /// 
    /// var area/progetto * ctv area/progetto
    /// -------------------------------------
    /// sommatoria ( var area/progetto * ctv area/progetto )
    /// 
    /// </summary>
    /// <returns></returns>
    public DataTable calcolaRischio()
    {
        DataTable dtResult = new DataTable("dtResult");
        dtResult.Columns.Add("AreaBisogno");
        dtResult.Columns.Add("NomeProgetto");
        dtResult.Columns.Add("RischioRelativo",typeof(decimal));
        dtResult.Columns.Add("RischioRelativoString");


        decimal varpArea = 0;
        decimal dividendo = 0;
        decimal divisore = 0;
        decimal ctvArea = 0;
        decimal rischio = 0;
        string rischiostring = string.Empty;

        List<RischioRelativo_PesoPercentuale> _itemsRischioRelativo = new List<RischioRelativo_PesoPercentuale>();
        List<Rischio> _itemsRischioRelativoAppoggio = new List<Rischio>();

        if (_datasource.Rows.Count == 0)
            return null;

        foreach (DataRow dr in _datasource.Rows)
        {
            rischio = 0;
            varpArea = dr["var_needarea"]==DBNull.Value? 0 : Convert.ToDecimal(dr["var_needarea"]);
            ctvArea = Convert.ToDecimal(dr["controvaloreattuale"]);
            rischiostring = string.Empty;

            dividendo = varpArea * ctvArea;
            if (dividendo != 0)
            {
                divisore = recuperaDivisoreRischio(_datasource);
                rischio = Math.Round(dividendo * 100 / divisore, 2);
            }

            if (dr["var_needareastring"].ToString().ToLower() == "n.c.")
                rischiostring = "n.c.";

            _itemsRischioRelativo.Add(new RischioRelativo_PesoPercentuale(dr["NEED_AREA"].ToString(), dr["nome_progetto"].ToString(), rischio, rischiostring, 0));
            
        }
      
        _itemsRischioRelativo = ArrotondaLista(_itemsRischioRelativo, TipoRitorno.RISCHIO);

        foreach (RischioRelativo_PesoPercentuale obj in _itemsRischioRelativo)
        {

            DataRow rToAdd = dtResult.NewRow();
            rToAdd["AreaBisogno"] = obj.Area;
            rToAdd["NomeProgetto"] = obj.NomeProgetto;
            rToAdd["RischioRelativo"] = obj.Rischio;
            rToAdd["RischioRelativoString"] = obj.RischioString;
            dtResult.Rows.Add(rToAdd);
        }

        return dtResult;

    }
      

    
    /// <summary>
    /// Ritorna la sommatoria di tutte i controvalori di area/progetto * il VaR di area/progetto di tutte le area/progetto.
    /// </summary>
    /// <param name="dt"></param>
    /// <returns></returns>
    private decimal recuperaDivisoreRischio(DataTable dt)
    {
        decimal returnValue = 0M;
        decimal ctvA = 0;
        decimal varA = 0;
        DataRow[] drFilter;

        object sumExt = dt.Compute("Sum(CONTROVALOREATTUALE)", "NEED_AREA='Ext'");
        drFilter = dt.Select("NEED_AREA='Ext'");
        if (sumExt.ToString() == string.Empty) 
            sumExt = 0M;
        if (Convert.ToDecimal(sumExt) > 0)
        {
            ctvA = Convert.ToDecimal(sumExt);
            varA = Convert.ToDecimal(drFilter[0]["var_needarea"]);
            returnValue = (returnValue + Math.Round(ctvA * varA, 2));
        }

        object sumLiq = dt.Compute("Sum(CONTROVALOREATTUALE)", "NEED_AREA='Liq'");
        drFilter = dt.Select("NEED_AREA='Liq'");
        if (sumLiq.ToString() == string.Empty) 
            sumLiq = 0M;
        if (Convert.ToDecimal(sumLiq) > 0)
        {
            ctvA = Convert.ToDecimal(sumLiq);
            varA = Convert.ToDecimal(drFilter[0]["var_needarea"]);
            returnValue = (returnValue + Math.Round(ctvA * varA, 2));
        }

        //object sumInv = dt.Compute("Sum(CONTROVALOREATTUALE)", "NEED_AREA='Inv'");
        drFilter = dt.Select("NEED_AREA='Inv'");
        //if (sumInv.ToString() == string.Empty) sumInv = 0M;       
        foreach (DataRow drInvestimento in drFilter)
        {
             varA = Convert.ToDecimal(drInvestimento["var_needarea"]);
             ctvA = Convert.ToDecimal(drInvestimento["controvaloreattuale"]);
             returnValue = (returnValue + Math.Round(ctvA * varA, 2));
        }      
       


        object sumPre = dt.Compute("Sum(CONTROVALOREATTUALE)", "NEED_AREA='Pre'");
        drFilter = dt.Select("NEED_AREA='Pre'");
        if (sumPre.ToString() == string.Empty) 
            sumPre = 0M;
        if (Convert.ToDecimal(sumPre) > 0)
        {
            ctvA = Convert.ToDecimal(sumPre);
            varA = Convert.ToDecimal(drFilter[0]["var_needarea"]);
            returnValue = (returnValue + Math.Round(ctvA * varA, 2));
        }

        object sumRis = dt.Compute("Sum(CONTROVALOREATTUALE)", "NEED_AREA='Ris'");
        drFilter = dt.Select("NEED_AREA='Ris'");
        if (sumRis.ToString() == string.Empty) 
            sumRis = 0M;
        if (Convert.ToDecimal(sumRis) > 0)
        {
            ctvA = Convert.ToDecimal(sumRis);
            varA = Convert.ToDecimal(drFilter[0]["var_needarea"]);
            returnValue = (returnValue + Math.Round(ctvA * varA, 2));
        }
        return returnValue;
    }

    /// <summary>
    /// Ritorna la sommatoria di tutte i controvalori di area/progetto di tutte le area/progetto.
    /// </summary>
    /// <param name="dt"></param>
    /// <returns></returns>
    private decimal recuperaDivisorePeso(DataTable dt)
    {
        decimal returnValue = 0M;
        decimal ctvA = 0;
       

        object sumExt = dt.Compute("Sum(CONTROVALOREATTUALE)", "NEED_AREA='Ext'");       
        if (sumExt.ToString() == string.Empty)
            sumExt = 0M;            
        ctvA = Convert.ToDecimal(sumExt);        
        returnValue = (returnValue + ctvA);
        

        object sumLiq = dt.Compute("Sum(CONTROVALOREATTUALE)", "NEED_AREA='Liq'");       
        if (sumLiq.ToString() == string.Empty)
            sumLiq = 0M;            
        ctvA = Convert.ToDecimal(sumLiq);        
        returnValue = (returnValue + ctvA);
       

        object sumInv = dt.Compute("Sum(CONTROVALOREATTUALE)", "NEED_AREA='Inv'");        
        if (sumInv.ToString() == string.Empty) 
            sumInv = 0M;
        ctvA = Convert.ToDecimal(sumInv);        
        returnValue = (returnValue + ctvA);
        


        object sumPre = dt.Compute("Sum(CONTROVALOREATTUALE)", "NEED_AREA='Pre'");        
        if (sumPre.ToString() == string.Empty)
            sumPre = 0M;
        ctvA = Convert.ToDecimal(sumPre);
        returnValue = (returnValue +ctvA);
       

        object sumRis = dt.Compute("Sum(CONTROVALOREATTUALE)", "NEED_AREA='Ris'");        
        if (sumRis.ToString() == string.Empty)
            sumRis = 0M;
        ctvA = Convert.ToDecimal(sumRis); 
        returnValue = (returnValue + ctvA);
       
        return returnValue;
    }
   


    /// <summary>
    /// Arrotonda, aggiungendo all'oggetto piu alto la differenza per arrivare al 100%, una lista di oggetti.
    /// </summary>
    /// <param name="listadaarrotondare"></param>
    /// <returns></returns>
    private List<RischioRelativo_PesoPercentuale> ArrotondaLista(List<RischioRelativo_PesoPercentuale> listadaarrotondare, TipoRitorno tiporitorno)
    {

        //-----------------------------------------------------------
        // verifico se la somma di tutti i rischi relativi è 100.
        decimal totale = 0M;
        foreach (RischioRelativo_PesoPercentuale r in listadaarrotondare)
        {
            if (tiporitorno == TipoRitorno.RISCHIO)
                totale += Math.Round(r.Rischio, 2);
            else
                totale += Math.Round(r.Peso, 2);
        }

        // se la somma non è 100 aggiungo o elimino la differenza all'area che ha il rischio piu alto.
        if (totale != 100M)
        {
            RischioRelativo_PesoPercentuale robjMax; // è l'area/progetto con valore (rischio o peso) piu grande 
            
            MyItemCompare _compare = new MyItemCompare();
            _compare.TipologiaRitorno = tiporitorno;
            listadaarrotondare.Sort(_compare);

            decimal diff = 0;
            decimal rischio_pesoTotaleAreaInvestimento = 0; // somma del rischio o del peso (a seconda della richiesta) di tutti i progetti di investimento.

                       

            foreach (RischioRelativo_PesoPercentuale obj in listadaarrotondare)
            {
                if (obj.Area.ToLower() == "inv")
                {
                    if (tiporitorno == TipoRitorno.RISCHIO)
                        rischio_pesoTotaleAreaInvestimento += obj.Rischio;
                    else if (tiporitorno == TipoRitorno.PESO)
                        rischio_pesoTotaleAreaInvestimento += obj.Peso;
                }
            }

            robjMax = listadaarrotondare.ToArray()[listadaarrotondare.Count - 1];

            if (tiporitorno == TipoRitorno.RISCHIO)
            {
                if (rischio_pesoTotaleAreaInvestimento > robjMax.Rischio)
                {
                    List<RischioRelativo_PesoPercentuale> listadaarrotondare_appoggio = listadaarrotondare;

                    // devo prendere il progetto di investimento piu alto.
                    listadaarrotondare_appoggio = listadaarrotondare.FindAll(delegate(RischioRelativo_PesoPercentuale r) { return (r.Area.ToLower() == "inv"); });
                    listadaarrotondare_appoggio.Sort(_compare);

                    robjMax = listadaarrotondare_appoggio.ToArray()[listadaarrotondare_appoggio.Count - 1];
                }
            }
            else if (tiporitorno == TipoRitorno.PESO)
            {                
                if (rischio_pesoTotaleAreaInvestimento > robjMax.Peso)
                {
                    List<RischioRelativo_PesoPercentuale> listadaarrotondare_appoggio = listadaarrotondare;

                    // devo prendere il progetto di investimento piu alto.
                    listadaarrotondare_appoggio = listadaarrotondare.FindAll(delegate(RischioRelativo_PesoPercentuale r) { return (r.Area.ToLower() == "inv"); });
                    listadaarrotondare_appoggio.Sort(_compare);

                    robjMax = listadaarrotondare_appoggio.ToArray()[listadaarrotondare_appoggio.Count - 1];
                }
            }
            

            // ricavo la differenza
            if (totale == 0)
                diff = 0;
            else
                diff = 100 - totale;



            foreach (RischioRelativo_PesoPercentuale objToUpdate in listadaarrotondare)
            {
                if (objToUpdate.Area == robjMax.Area && objToUpdate.NomeProgetto == robjMax.NomeProgetto)
                {
                    if (tiporitorno == TipoRitorno.PESO)
                        objToUpdate.Peso += diff;
                    else
                        objToUpdate.Rischio += diff;
                }
            }

            return listadaarrotondare;
        }
        else //== 100
        {
            return listadaarrotondare;
        }
    }
}


public class Rischio
{

    private decimal _fattoreribasamento = 0;
    private decimal _ribasamentototale = 0;
    private string _area = string.Empty;
    private string _nomeprogetto = string.Empty;



    public Rischio(string Area, string NomeProgetto, decimal FattoreRibasamento)
    {
        _area = Area;
        _nomeprogetto = NomeProgetto;
        _fattoreribasamento = FattoreRibasamento;

    }

    public string Area
    {
        get { return _area; }
        set { _area = value; }
    }

    public string NomeProgetto
    {
        get { return _nomeprogetto; }
        set { _nomeprogetto = value; }
    }

    public decimal RibasamentoTotale
    {
        get { return _ribasamentototale; }
        set { _ribasamentototale = value; }
    }


    public decimal FattoreRibasamento
    {
        get { return _fattoreribasamento; }
        set { _fattoreribasamento = value; }
    }



}

public enum TipoRitorno
{
    RISCHIO,
    PESO
}

public class MyItemCompare : IComparer<RischioRelativo_PesoPercentuale>
{

    private TipoRitorno _tipologiaritorno;

    public TipoRitorno TipologiaRitorno
    {
        get { return _tipologiaritorno; }
        set { _tipologiaritorno = value; }
    }




    /// <summary>
    /// Compara due struttire di tipo MyItem
    /// </summary>
    /// <param name="x">MyItem</param>
    /// <param name="y">MyItem</param>
    /// <returns>0 = sono uguali, -1 y è maggiore, 1 x è maggiore </returns>
    public int Compare(RischioRelativo_PesoPercentuale x, RischioRelativo_PesoPercentuale y)
    {
        if (x == null)
        {
            if (y == null)
            {
                // Se x è null e y è null, sono uguali
                return 0;
            }
            else
            {
                // Se x è null e y non è null, y è maggiore   
                return -1;
            }
        }
        else
        {
            // Se x non è null... 
            if (y == null)
            // ...e y è null, x è magiore.
            {
                return 1;
            }
            else
            {
                // ...e y non è null, confronto la categoria
                int retval;

                if (_tipologiaritorno == TipoRitorno.RISCHIO)
                    retval = (x.Rischio).CompareTo(y.Rischio);
                else
                    retval = (x.Peso).CompareTo(y.Peso);

                if (retval != 0)
                {
                    // Se la categoria non è uguale ritorno il valore di retval
                    return retval;
                }
                else
                {
                    // Se la categoria è la stessa, allora devo ordinare per la descrizione
                    return CheckNull(x.Area).CompareTo(CheckNull(y.Area));
                }
            }

        }
    }

    private string CheckNull(string val)
    {
        if (val == null)
            return "";

        return val;
    }
}