// Created with MonoDevelop
//
//    Copyright (C) 2018 Hiperscan GmbH Dresden, Germany
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Globalization;

using Hiperscan.Unix;


namespace Hiperscan.Spectroscopy
{

    public partial class DataSet
    {

        public class XConstraints
        {
            public List<double> LowerLimits { get; private set; }
            public List<double> UpperLimits { get; private set; }

            public string Constraint { get; private set; }
            public bool IsEmpty      { get; private set; }

            private XConstraints()
            {
                this.LowerLimits = new List<double>();
                this.UpperLimits = new List<double>();
                this.Constraint  = string.Empty;
                this.IsEmpty     = true;
            }

            private XConstraints(string constraint, List<double> lower_limits, List<double> upper_limits)
            {
                if (lower_limits != null && upper_limits != null && lower_limits.Count != upper_limits.Count)
                    throw new ArgumentException(Catalog.GetString("Lower and upper limits of x-axis-constraints must have the same dimension."));

                if (lower_limits == null || upper_limits == null || lower_limits.Count == 0)
                {
                    this.LowerLimits = new List<double>();
                    this.UpperLimits = new List<double>();
                    this.Constraint = string.Empty;
                    this.IsEmpty = true;
                    return;
                }

                this.IsEmpty = false;
                this.Constraint = constraint;
                this.LowerLimits = new List<double>(lower_limits);
                this.UpperLimits = new List<double>(upper_limits);

                this.LowerLimits.Sort();
                this.UpperLimits.Sort();
            }

            public DataSet Apply(DataSet ds)
            {
                if (ds == null)
                    return null;

                if (this.IsEmpty)
                    return ds;

                double eps = 0.1;

                List<int> indices = new List<int>();

                for (int ix = 0; ix < this.LowerLimits.Count; ++ix)
                {
                    indices.AddRange(ds.Find('x', '>', this.LowerLimits[ix] - eps, '<', this.UpperLimits[ix] + eps));
                }

                DataSet range = ds[indices];

                List<int> gaps = new List<int>();
                int gap_count = 0;
                for (int ix = 0; ix < range.Count && gap_count < this.UpperLimits.Count - 1; ++ix)
                {
                    if (range.X[ix] < this.UpperLimits[gap_count] + eps)
                        continue;

                    gaps.Add(ix);
                    ++gap_count;
                }

                range.CopyPropertiesFrom(ds);
                range.Gaps = gaps;

                return range;
            }

            public static XConstraints CreateFromString(string constraint)
            {
                if (string.IsNullOrEmpty(constraint))
                    return null;

                constraint = constraint.Replace(" ", string.Empty);

                string regex = string.Empty;
                if (CultureInfo.CurrentCulture.TextInfo.ListSeparator == ";" &&
                CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator == ",")
                {
                    regex = @"^[0-9,;\-]+$";
                }
                else if (CultureInfo.CurrentCulture.TextInfo.ListSeparator == "," &&
                     CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator == ".")
                {
                    regex = @"^[0-9\.,\-]+$";
                }
                else
                {
                    return null;
                }

                if (!Regex.IsMatch(constraint, regex))
                    return null;

                List<double> lower_limits = new List<double>();
                List<double> upper_limits = new List<double>();

                foreach (string range in Regex.Split(constraint, CultureInfo.CurrentCulture.TextInfo.ListSeparator))
                {
                    string[] limits = Regex.Split(range, "-");
                    if (limits.Length != 2 || string.IsNullOrEmpty(limits[0]) || string.IsNullOrEmpty(limits[1]))
                        return null;

                    double lower = double.Parse(limits[0]);
                    double upper = double.Parse(limits[1]);

                    if (lower >= upper)
                        return null;

                    foreach (double upper_limit in upper_limits)
                    {
                        if (upper_limit >= lower)
                            return null;
                    }

                    lower_limits.Add(lower);
                    upper_limits.Add(upper);
                }

                return new XConstraints(constraint, lower_limits, upper_limits);
            }

            public static XConstraints Empty
            {
                get { return new XConstraints(); }
            }

            public override string ToString()
            {
                return this.Constraint;
            }
        }
    }
}