using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EvoBoat.EvoAlg
{
    public class BoolVector : IVector<bool>, ICloneable<BoolVector>
    {
        internal bool[] values;
        public int Length
        {
            get { return values.Length; }
        }
        public bool this[int index]
        {
            get { return values[index]; }
            set { values[index] = value; }
        }

        private BoolVector() { }

        public BoolVector(int length)
        {
            this.values = new bool[length];
        }

        public BoolVector(bool[] values)
        {
            this.values = values.ToArray();
        }

        public BoolVector(IEnumerable<bool> values)
        {
            this.values = values.ToArray();
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < values.Length; i++)
                sb.Append(values[i] ? '1' : '0');
            return sb.ToString();
        }

        public override bool Equals(object obj)
        {
            BoolVector v = obj as BoolVector;
            return (!object.ReferenceEquals(v, null) && this == v);
        }

        public override int GetHashCode()
        {
            return values.GetHashCode();
        }

        public IEnumerator<bool> GetEnumerator()
        {
            for (int i = 0; i < values.Length; i++)
                yield return values[i];
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        public BoolVector Clone()
        {
            return new BoolVector(values);
        }
        
        public BoolVector SubVector(int len, int index = 0)
        {
            bool[] array = new bool[len];
            for (int i = 0; i < len; i++)
                array[i] = values[index + i];
            return new BoolVector() { values = array };
        }

        public bool AllOne()
        {
            for (int i = 0; i < values.Length; i++)
                if (!values[i]) return false;
            return true;
        }

        public bool AllZero()
        {
            for (int i = 0; i < values.Length; i++)
                if (values[i]) return false;
            return true;
        }

        public static bool operator ==(BoolVector v1, BoolVector v2)
        {
            if (v1.values.Length != v2.values.Length)
                return false;
            for (int i = 0; i < v1.values.Length; i++)
                if (v1.values[i] != v2.values[i])
                    return false;
            return true;
        }

        public static bool operator !=(BoolVector v1, BoolVector v2)
        {
            return !(v1 == v2);
        }

        public static BoolVector operator |(BoolVector v1, BoolVector v2)
        {
            if (v1.values.Length > v2.values.Length)
                throw new ArgumentException("Vector lengthes do not match");
            BoolVector v = new BoolVector(v1.values);
            for (int i = 0; i < v1.values.Length; i++)
                v.values[i] |= v2.values[i];
            return v;
        }

        public static BoolVector operator &(BoolVector v1, BoolVector v2)
        {
            if (v1.values.Length > v2.values.Length)
                throw new ArgumentException("Vector lengthes do not match");
            BoolVector v = new BoolVector(v1.values);
            for (int i = 0; i < v1.values.Length; i++)
                v.values[i] &= v2.values[i];
            return v;
        }

        public static BoolVector operator ^(BoolVector v1, BoolVector v2)
        {
            if (v1.values.Length > v2.values.Length)
                throw new ArgumentException("Vector lengthes do not match");
            BoolVector v = new BoolVector(v1.values);
            for (int i = 0; i < v1.values.Length; i++)
                v.values[i] ^= v2.values[i];
            return v;
        }

        public static double SumSel(BoolVector v1, Vector v2)
        {
            if (v1.values.Length > v2.values.Length)
                throw new ArgumentException("Vector lengthes do not match");
            double s = 0;
            for (int i = 0; i < v1.values.Length; i++)
                if(v1.values[i])
                    s += v2.values[i];
            return s;
        }

        public static int SumSel(BoolVector v1, IntVector v2)
        {
            if (v1.values.Length > v2.values.Length)
                throw new ArgumentException("Vector lengthes do not match");
            int s = 0;
            for (int i = 0; i < v1.values.Length; i++)
                if (v1.values[i])
                    s += v2.values[i];
            return s;
        }

        public static implicit operator BoolVector(bool[] array)
        {
            return new BoolVector() { values = array };
        }

        public static explicit operator bool[] (BoolVector v)
        {
            return v.values;
        }

        public static explicit operator BoolVector(int[] array)
        {
            bool[] values = new bool[array.Length];
            for (int i = 0; i < array.Length; i++)
                if (array[i] > 0)
                    values[i] = true;
            return new BoolVector(values);
        }

        public static explicit operator int[] (BoolVector v)
        {
            int[] arr = new int[v.values.Length];
            for (int i = 0; i < arr.Length; i++)
                if (v.values[i])
                    arr[i] = 1;
            return arr;
        }

        /// <summary>
        /// Create a IntVector whose elements are the same default value
        /// </summary>
        public static BoolVector Create(int len, bool allone = false)
        {
            BoolVector v = new BoolVector();
            v.values = new bool[len];
            if (allone)
                for (int i = 0; i < len; i++)
                    v.values[i] = true;
            return v;
        }
                
        public static BoolVector CreateRandom(Random rand, int len)
        {
            BoolVector v = new BoolVector();
            v.values = new bool[len];
            for (int i = 0; i < len; i++)
                if(rand.Next() % 2 == 0)
                    v.values[i] = true;
            return v;
        }

    }
}
