// Copyright 2009-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include "platform.h"
#include "alloc.h"

namespace embree
{
  /*! static array with static size */
  template<typename T, size_t N>
    class array_t
    {
    public:

      /********************** Iterators  ****************************/

      __forceinline T* begin() const { return items; };
      __forceinline T* end  () const { return items+N; };


      /********************** Capacity ****************************/

      __forceinline bool   empty    () const { return N == 0; }
      __forceinline size_t size     () const { return N; }
      __forceinline size_t max_size () const { return N; }
            

      /******************** Element access **************************/

      __forceinline       T& operator[](size_t i)       { assert(i < N); return items[i]; }
      __forceinline const T& operator[](size_t i) const { assert(i < N); return items[i]; }

      __forceinline       T& at(size_t i)       { assert(i < N); return items[i]; }
      __forceinline const T& at(size_t i) const { assert(i < N); return items[i]; }

      __forceinline T& front() const { assert(N > 0); return items[0]; };
      __forceinline T& back () const { assert(N > 0); return items[N-1]; };

      __forceinline       T* data()       { return items; };
      __forceinline const T* data() const { return items; };

    private:
      T items[N];
    };

  /*! static array with dynamic size */
  template<typename T, size_t N>
    class darray_t
    {
    public:

      __forceinline darray_t () : M(0) {}

      __forceinline darray_t (const T& v) : M(0) {
        for (size_t i=0; i<N; i++) items[i] = v;
      }

      /********************** Iterators  ****************************/

      __forceinline T* begin() const { return items; };
      __forceinline T* end  () const { return items+M; };


      /********************** Capacity ****************************/

      __forceinline bool   empty    () const { return M == 0; }
      __forceinline size_t size     () const { return M; }
      __forceinline size_t capacity () const { return N; }
      __forceinline size_t max_size () const { return N; }
      
      void resize(size_t new_size) {
        assert(new_size < max_size());
        M = new_size;
      }

      /******************** Modifiers **************************/

      __forceinline void push_back(const T& v) 
      {
        assert(M+1 < max_size());
        items[M++] = v;
      }

      __forceinline void pop_back() 
      {
        assert(!empty());
        M--;
      }

      __forceinline void clear() {
        M = 0;
      }

      /******************** Element access **************************/

      __forceinline       T& operator[](size_t i)       { assert(i < M); return items[i]; }
      __forceinline const T& operator[](size_t i) const { assert(i < M); return items[i]; }

      __forceinline       T& at(size_t i)       { assert(i < M); return items[i]; }
      __forceinline const T& at(size_t i) const { assert(i < M); return items[i]; }

      __forceinline T& front() const { assert(M > 0); return items[0]; };
      __forceinline T& back () const { assert(M > 0); return items[M-1]; };

      __forceinline       T* data()       { return items; };
      __forceinline const T* data() const { return items; };

    private:
      size_t M;
      T items[N];
    };

  /*! dynamic sized array that is allocated on the stack */
#define dynamic_large_stack_array(Ty,Name,N,max_stack_bytes) StackArray<Ty,max_stack_bytes> Name(N)
  template<typename Ty, size_t max_stack_bytes>
    struct __aligned(64) StackArray
  {
    __forceinline StackArray (const size_t N)
      : N(N)
    {
      if (N*sizeof(Ty) <= max_stack_bytes) 
        data = &arr[0];
      else
        data = (Ty*) alignedMalloc(N*sizeof(Ty),64); 
    }

    __forceinline ~StackArray () {
      if (data != &arr[0]) alignedFree(data);
    }

    __forceinline operator       Ty* ()       { return data; }
    __forceinline operator const Ty* () const { return data; }

    __forceinline       Ty& operator[](const int i)       { assert(i>=0 && i<N); return data[i]; }
    __forceinline const Ty& operator[](const int i) const { assert(i>=0 && i<N); return data[i]; }

    __forceinline       Ty& operator[](const unsigned i)       { assert(i<N); return data[i]; }
    __forceinline const Ty& operator[](const unsigned i) const { assert(i<N); return data[i]; }

#if defined(__64BIT__)
    __forceinline       Ty& operator[](const size_t i)       { assert(i<N); return data[i]; }
    __forceinline const Ty& operator[](const size_t i) const { assert(i<N); return data[i]; }
#endif

  private:
    Ty arr[max_stack_bytes/sizeof(Ty)];
    Ty* data;
    size_t N;

  private:
    StackArray (const StackArray& other) DELETED; // do not implement
    StackArray& operator= (const StackArray& other) DELETED; // do not implement

  };

  /*! dynamic sized array that is allocated on the stack */
  template<typename Ty, size_t max_stack_elements, size_t max_total_elements>
    struct __aligned(64) DynamicStackArray
  {
    __forceinline DynamicStackArray ()
      : data(&arr[0]) {}

    __forceinline ~DynamicStackArray ()
    {
      if (!isStackAllocated())
        delete[] data;
    }

    __forceinline bool isStackAllocated() const {
      return data == &arr[0];
    }

    __forceinline size_t size() const
    {
      if (isStackAllocated()) return max_stack_elements;
      else return max_total_elements;
    }

    __forceinline void resize(size_t M)
    {
      assert(M <= max_total_elements);
      if (likely(M <= max_stack_elements)) return;
      if (likely(!isStackAllocated())) return;

      data = new Ty[max_total_elements];
      
      for (size_t i=0; i<max_stack_elements; i++)
        data[i] = arr[i];
    }

    __forceinline operator       Ty* ()       { return data; }
    __forceinline operator const Ty* () const { return data; }

    __forceinline       Ty& operator[](const int i)      { assert(i>=0 && i<max_total_elements); resize(i+1); return data[i]; }
    __forceinline       Ty& operator[](const unsigned i) { assert(i<max_total_elements); resize(i+1); return data[i]; }

#if defined(__64BIT__)
    __forceinline       Ty& operator[](const size_t i) { assert(i<max_total_elements); resize(i+1); return data[i]; }
#endif

    __forceinline DynamicStackArray (const DynamicStackArray& other)
      : data(&arr[0]) 
    {
      for (size_t i=0; i<other.size(); i++)
        this->operator[] (i) = other[i];
    }
     
    DynamicStackArray& operator= (const DynamicStackArray& other)
    {
      for (size_t i=0; i<other.size(); i++)
        this->operator[] (i) = other[i];

      return *this;
    }

  private:
    Ty arr[max_stack_elements];
    Ty* data;
  };
}
