/******************************************************************************
 * Copyright (C) Leap Motion, Inc. 2011-2017.                                 *
 * Leap Motion proprietary and  confidential.                                 *
 *                                                                            *
 * Use subject to the terms of the Leap Motion SDK Agreement available at     *
 * https://developer.leapmotion.com/sdk_agreement, or another agreement       *
 * between Leap Motion and you, your company or other organization.           *
 ******************************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Leap.Unity.Query {

  public static class Values {

    /// <summary>
    /// Generates a new sequence that contains only a single value.
    /// </summary>
    public static QueryWrapper<T, SingleOp<T>> Single<T>(T value) {
      return new QueryWrapper<T, SingleOp<T>>(new SingleOp<T>(value));
    }

    public struct SingleOp<T> : IQueryOp<T> {
      private T _t;
      private bool _hasReturned;

      public SingleOp(T t) {
        _t = t;
        _hasReturned = false;
      }

      public bool TryGetNext(out T t) {
        if (_hasReturned) {
          t = default(T);
          return false;
        } else {
          t = _t;
          _hasReturned = true;
          return true;
        }
      }

      public void Reset() {
        _hasReturned = false;
      }
    }

    /// <summary>
    /// Generates a new sequence of a specific type that has no elements.
    /// </summary>
    public static QueryWrapper<T, EmptyOp<T>> Empty<T>() {
      return new QueryWrapper<T, EmptyOp<T>>(new EmptyOp<T>());
    }

    public struct EmptyOp<T> : IQueryOp<T> {

      public bool TryGetNext(out T t) {
        t = default(T);
        return false;
      }

      public void Reset() { }
    }

    /// <summary>
    /// Generates a new sequence that returns the integers starting at
    /// the given value and incrementing by one for each successive value.
    /// This sequence is unbounded and will never terminate.
    /// 
    /// For example:
    ///   Values.From(5)
    /// Will result in:
    ///   (5, 6, 7, 8, 9...)
    /// </summary>
    public static QueryWrapper<int, UnboundedRangeOp> From(int from) {
      return new QueryWrapper<int, UnboundedRangeOp>(new UnboundedRangeOp(from, step: 1));
    }

    /// <summary>
    /// Takes an unbounded sequence generated by the From operator and places an upper bound
    /// on it.  Once the sequence reaches or exceeds the given value, the sequence 
    /// will terminate.
    /// 
    /// For example:
    ///   Values.From(5).To(8)
    /// Would result in:
    ///   (5, 6, 7)
    /// </summary>
    public static QueryWrapper<int, RangeOp> To(this QueryWrapper<int, UnboundedRangeOp> wrapper, int to) {
      return new QueryWrapper<int, RangeOp>(new RangeOp(wrapper.op.from, to, wrapper.op.step));
    }

    /// <summary>
    /// Takes an unbounded sequence and changes its step parameter to be something other than 1.
    /// Each successive element will be incremented by the new step value instead of by 1.
    /// Passing in negative step values will cause the sequence to step backwards.  Passing in
    /// a step value of zero is legal but will just result in the starting value being repeated
    /// forever.
    /// 
    /// For example:
    ///   Values.From(5).By(2)
    /// Would result in:
    ///   (5, 7, 9, 11, 13...)
    /// </summary>
    public static QueryWrapper<int, UnboundedRangeOp> By(this QueryWrapper<int, UnboundedRangeOp> wrapper, int step) {
      return new QueryWrapper<int, UnboundedRangeOp>(new UnboundedRangeOp(wrapper.op.from, step));
    }

    /// <summary>
    /// Takes a bounded sequence and changes its step parameter to be something other than 1.
    /// Each successive element will be incremented towards the goal by the new step value instead
    /// of by one.  In this case, the sign of the step value is IGNORED.  The value will ALWAYS
    /// step towards the goal, regardless of what the sign of the step value is.
    /// 
    /// For example:
    ///   Values.From(5).To(10).By(2)
    /// Would result in:
    ///   (5, 7, 9)
    /// </summary>
    public static QueryWrapper<int, RangeOp> By(this QueryWrapper<int, RangeOp> wrapper, int step) {
      return new QueryWrapper<int, RangeOp>(new RangeOp(wrapper.op.from, wrapper.op.to, step));
    }

    public struct UnboundedRangeOp : IQueryOp<int> {
      public readonly int from, step;
      private int _curr;

      public UnboundedRangeOp(int from, int step) {
        this.from = from;
        this.step = step;

        _curr = from;
      }

      public bool TryGetNext(out int t) {
        t = _curr;
        _curr += step;
        return true;
      }

      public void Reset() {
        _curr = from;
      }
    }

    public struct RangeOp : IQueryOp<int> {
      public readonly int from, to, step;
      private int _curr;

      public RangeOp(int from, int to, int step) {
        this.from = from;
        this.to = step == 0 ? from : to;
        this.step = to > from ? Mathf.Abs(step) : -Mathf.Abs(step);

        _curr = this.from;
      }

      public bool TryGetNext(out int t) {
        t = _curr;

        if (_curr == to) {
          return false;
        }

        if ((_curr > to) == (to > from)) {
          return false;
        }

        _curr += step;
        return true;
      }

      public void Reset() {
        _curr = from;
      }
    }
  }
}
