using System.Collections.Generic;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;

namespace HeuristicLab.Problems.ProgramSynthesis {
  /// <summary>
  /// Concatinates two vectors on the type stack.
  /// </summary>
  [StorableClass]
  public abstract class VectorConcatExpression<T> : StatelessExpression {
    protected VectorConcatExpression() { }
    [StorableConstructor]
    protected VectorConcatExpression(bool deserializing) : base(deserializing) { }

    protected bool IsNoop(IInternalPushInterpreter interpreter, IPushStack<IReadOnlyList<T>> vectorStack) {
      return vectorStack.Count < 2 ||
             vectorStack.Top.Count + vectorStack[1].Count > interpreter.Configuration.MaxVectorLength;
    }

    protected void Eval(IInternalPushInterpreter interpter, IPushStack<IReadOnlyList<T>> vectorStack) {
      var first = vectorStack.Pop();
      var second = vectorStack.Top;
      var result = new List<T>(second.Count + first.Count);
      result.AddRange(second);
      result.AddRange(first);

      vectorStack.Top = result;
    }
  }

  [StorableClass]
  [PushExpression(
    StackTypes.IntegerVector,
    "INTEGER[].CONCAT",
    "Concatenates the top two vectors on the INTEGER[] stack.")]
  public class IntegerVectorConcatExpression : VectorConcatExpression<long> {
    public IntegerVectorConcatExpression() { }
    [StorableConstructor]
    protected IntegerVectorConcatExpression(bool deserializing) : base(deserializing) { }

    public override bool IsNoop(IInternalPushInterpreter interpreter) {
      return IsNoop(interpreter, interpreter.IntegerVectorStack);
    }

    public override void Eval(IInternalPushInterpreter interpreter) {
      Eval(interpreter, interpreter.IntegerVectorStack);
    }
  }

  [StorableClass]
  [PushExpression(
    StackTypes.FloatVector,
    "FLOAT[].CONCAT",
    "Concatenates the top two vectors on the FLOAT[] stack.")]
  public class FloatVectorConcatExpression : VectorConcatExpression<double> {
    public FloatVectorConcatExpression() { }
    [StorableConstructor]
    protected FloatVectorConcatExpression(bool deserializing) : base(deserializing) { }

    public override bool IsNoop(IInternalPushInterpreter interpreter) {
      return IsNoop(interpreter, interpreter.FloatVectorStack);
    }

    public override void Eval(IInternalPushInterpreter interpreter) {
      Eval(interpreter, interpreter.FloatVectorStack);
    }
  }

  [StorableClass]
  [PushExpression(
    StackTypes.BooleanVector,
    "BOOLEAN[].CONCAT",
    "Concatenates the top two vectors on the BOOLEAN[] stack.")]
  public class BooleanVectorConcatExpression : VectorConcatExpression<bool> {
    public BooleanVectorConcatExpression() { }
    [StorableConstructor]
    protected BooleanVectorConcatExpression(bool deserializing) : base(deserializing) { }

    public override bool IsNoop(IInternalPushInterpreter interpreter) {
      return IsNoop(interpreter, interpreter.BooleanVectorStack);
    }

    public override void Eval(IInternalPushInterpreter interpreter) {
      Eval(interpreter, interpreter.BooleanVectorStack);
    }
  }

  [StorableClass]
  [PushExpression(
    StackTypes.StringVector,
    "STRING[].CONCAT",
    "Concatenates the top two vectors on the STRING[] stack.")]
  public class StringVectorConcatExpression : VectorConcatExpression<string> {
    public StringVectorConcatExpression() { }
    [StorableConstructor]
    protected StringVectorConcatExpression(bool deserializing) : base(deserializing) { }

    public override bool IsNoop(IInternalPushInterpreter interpreter) {
      return IsNoop(interpreter, interpreter.StringVectorStack);
    }

    public override void Eval(IInternalPushInterpreter interpreter) {
      Eval(interpreter, interpreter.StringVectorStack);
    }
  }
}