using System;
using System.Collections.Generic;
using System.Text;
using Yamalib.Util;

namespace Yamalib.Math
{
    /// <summary>
    /// Ｂスプラインクラス
    /// </summary>
    public class BSpline
    {
        /**
         * 	B-スプライン曲線を算出します
         */
        public static List<Position> CalcBSpline(List<Position> sample, int divCnt)
        {
            float t, r;
            List<Position> pts = new List<Position>();
            List<int> nv = new List<int>();
            double div = (double)divCnt;

            // 単純基底関数生成
            for (int i = 0; i < sample.Count + 3; ++i)
            {
                nv.Add(i);
            }

            for (int i = 0; i < sample.Count * div; ++i)
            {
                Position pt = new Position();
                t = i / (float)div;

                //            if (i == this.tmax * 10) {
                //               //t>=tmax では基底関数は0になる
                //  			t -= 0.001;
                //          }

                for (int j = 0; j < sample.Count; ++j)
                {
                    r = BaseN(j, 3, t, nv);
                    pt.X += (sample[j].X) * r;
                    pt.Y += (sample[j].Y) * r;
                }

                pts.Add(pt);
            }

            return CleanUp(pts, divCnt);
        }

        private static List<Position> CleanUp(List<Position> pts, int div)
        {
            if (pts.Count <= (div * 2) || 0 > pts.Count - (div * 2)) return null;
            if ((div * 2) > pts.Count - (div * 2)) return null;

            List<Position> result = new List<Position>();
            for (int i = (int)(div * 2); i < pts.Count - (div * 2); ++i)
            {
                result.Add(pts[i]);
            }
            return result;
        }

        private static float BaseN(int i, int k, float t, List<int> nv)
        {
            float w1 = 0.0f;
            float w2 = 0.0f;

            if (k == 1)
            {
                if (t >= nv[i] && t < nv[i + 1])
                {
                    return 1.0f;
                }
                return 0.0f;
            }

            //		Log.print(" i + k = %d, i + 1 = %d", i + k, i + 1);

            if ((nv[i + k] - nv[i + 1]) != 0)
            {
                w1 = ((nv[i + k] - t) / (nv[i + k] - nv[i + 1]))
                        * BaseN(i + 1, k - 1, t, nv);
            }

            if ((nv[i + k - 1] - nv[i]) != 0)
            {
                w2 = ((t - nv[i]) / (nv[i + k - 1] - nv[i])) * BaseN(i, k - 1, t, nv);
            }

            return (w1 + w2);
        }

        /// 2点間の距離を返す
        private static double Dist(double x1, double y1, double x2, double y2)
        {
            return System.Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
        }

        private const int LC_TMAX_1 = 8;
    }
}
