//------------------------------------------------------------------------------
// <copyright file=”*.cs” company=”EhLib Team”>
//     Copyright (c) 2017 Dmitry V. Bolshakov   
// </copyright>
//------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.Reflection;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;

namespace EhLib
{

  [TypeConverter(typeof(ExpandableObjectConverter))]
  public class AggregationCalculator
  {
    private System.Type dataType;
    private bool includeHidenRows = true;

    [DefaultValue(true)]
    public bool IncludeHidenRows
    {
      get { return includeHidenRows; }
      set { includeHidenRows = value; }
    }

    [Browsable(false)]
    public Type DataType
    {
      get { return dataType; }
    }

    [Browsable(false)]
    public virtual string DefaultFormatString
    {
      get { return null; }
    }

    public virtual void InitCalcData(System.Type dataType)
    {
      this.dataType = dataType;
    }

    public virtual void StepCalcData(object stepValue)
    {

    }

    public virtual object FinalizeCalcData()
    {
      return null;
    }

  }

  public class SumCalculator: AggregationCalculator
  {
    protected object CurValue { get; set; }
    protected MethodInfo SumMethod { get; set; }

    public override void InitCalcData(System.Type dataType)
    {
      base.InitCalcData(dataType);
      CurValue = null;
      if (dataType == null)
      {
        SumMethod = null;
      }
      else if (TypeIsInteger(dataType))
      {
        SumMethod = typeof(SumCalculator).GetMethod("op_Addition_Int64", BindingFlags.Static | BindingFlags.Public);
      }
      else if (TypeIsFloat(dataType))
      {
        SumMethod = typeof(SumCalculator).GetMethod("op_Addition_Double", BindingFlags.Static | BindingFlags.Public);
      }
      else
      {
        SumMethod = dataType.GetMethod("op_Addition", BindingFlags.Static | BindingFlags.Public);
      }
    }

    public override void StepCalcData(object stepValue)
    {
      if ((stepValue != null) && EhLibManager.DefaultEhLibManager.IsAggregatableNumberType(stepValue.GetType()))
      {
        if (CurValue == null)
          //try
          //{
          CurValue = Convert.ChangeType(stepValue, this.DataType);
        //}
        //catch
        //{
        //}
        else
        {
          //try
          //{
          stepValue = Convert.ChangeType(stepValue, this.DataType);
          CurValue = SumMethod.Invoke(null, new object[] { CurValue, stepValue });
          //}
          //catch
          //{
          //}
        }
      }
    }

    public override object FinalizeCalcData()
    {
      return CurValue;
    }

    public virtual bool TypeIsInteger(System.Type dataType)
    {
      if ((dataType == typeof(sbyte)) ||
          (dataType == typeof(byte)) ||
          (dataType == typeof(char)) ||
          (dataType == typeof(short)) ||
          (dataType == typeof(ushort)) ||
          (dataType == typeof(int)) ||
          (dataType == typeof(uint)) ||
          (dataType == typeof(long)) ||
          (dataType == typeof(ulong))
         )
        return true;
      else
        return false;
    }

    public virtual bool TypeIsFloat(System.Type dataType)
    {
      if ((dataType == typeof(float)) ||
          (dataType == typeof(double))
         )
        return true;
      else
        return false;
    }

//#pragma warning disable IDE1006 // Naming Styles
    public static long op_Addition_Int64(long v1, long v2)
    {
      return v1 + v2;
    }

    public static double op_Addition_Double(double v1, double v2)
    {
      return v1 + v2;
    }
//#pragma warning restore IDE1006 // Naming Styles

  }

  public class AverageCalculator : SumCalculator
  {
    private long countValue;
    private bool includeNullValues = false;

    [DefaultValue(false)]
    public bool IncludeNullValues
    {
      get { return includeNullValues; }
      set { includeNullValues = value; }
    }

    [Browsable(false)]
    public override string DefaultFormatString
    {
      get { return "#.0000"; }
    }

    public override void InitCalcData(System.Type dataType)
    {
      base.InitCalcData(dataType);
      countValue = 0;
    }

    public override void StepCalcData(object stepValue)
    {
      base.StepCalcData(stepValue);
      if (stepValue != null)
        countValue = countValue + 1;
    }

    public override object FinalizeCalcData()
    {
      if ((countValue == 0) || (this.CurValue == null))
        return null;
      else
      {
        if (TypeIsInteger(DataType))
          return (long)base.FinalizeCalcData() / this.countValue;
        else if (TypeIsFloat(DataType))
          return (double)base.FinalizeCalcData() / this.countValue;
        else
        {                                            
          MethodInfo divMethod = DataType.GetMethod("op_Division", BindingFlags.Static | BindingFlags.Public);
          return divMethod.Invoke(null, new object[] { base.FinalizeCalcData(), Convert.ChangeType(countValue, DataType) });
        }
      }
    }
  }

  public class CountCalculator : AggregationCalculator
  {
    private long curValue;

    private bool includeNullValues = false;

    [DefaultValue(false)]
    public bool IncludeNullValues
    {
      get { return includeNullValues; }
      set { includeNullValues = value; }
    }

    public override void InitCalcData(System.Type dataType)
    {
      base.InitCalcData(dataType);
      curValue = 0;
    }

    public override void StepCalcData(object stepValue)
    {
      if (IncludeNullValues)
        curValue = curValue + 1;
      else if (stepValue != null && stepValue != DBNull.Value)
        curValue = curValue + 1;
    }

    public override object FinalizeCalcData()
    {
      return curValue;
    }
  }

  public class MaxCalculator : AggregationCalculator
  {
    protected object CurValue { get; set; }

    public override void InitCalcData(System.Type dataType)
    {
      base.InitCalcData(dataType);
      CurValue = null;
    }

    public override void StepCalcData(object stepValue)
    {
      if ((CurValue == null) && (!DBNull.Value.Equals(stepValue)))
        CurValue = stepValue;
      else if ((stepValue != null) && (!DBNull.Value.Equals(stepValue)) )
      {
        var vl = (IComparable)CurValue;
        if (vl != null && vl.CompareTo(stepValue) < 0)
          CurValue = stepValue;
      }
    }

    public override object FinalizeCalcData()
    {
      return CurValue;
    }

  }

  public class MinCalculator : MaxCalculator
  {
    public override void StepCalcData(object stepValue)
    {
      if ((CurValue == null)  && (!DBNull.Value.Equals(stepValue)))
        CurValue = stepValue;
      else if ((stepValue != null) && (!DBNull.Value.Equals(stepValue)))
      {
        var vl = CurValue as IComparable;
        if (vl != null && vl.CompareTo(stepValue) > 0)
          CurValue = stepValue;
      }
    }
  }

  public class StaticTextCalculator : AggregationCalculator
  {
    public object StaticText { get; set; }

    [DefaultValue(null)]
    public string Text { get; set; }

    public override object FinalizeCalcData()
    {
      return StaticText;
    }
  }

}