#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fc/utility.hpp>
#include <fc/log.hpp>


namespace fc {
  namespace detail {
    template<typename T>
    struct data {
      size_t size;
      size_t capacity;
      T        first;

      static data* allocate( size_t cap ) {
        data* d = nullptr;
        if( cap ){
          d = (data*)malloc(sizeof(data) + sizeof(T)*(static_cast<size_t>(cap)-1));
          d->capacity = static_cast<size_t>(cap);
        } else {
          d = (data*)malloc(sizeof(data));
          d->capacity = 1;
        }
        d->size = 0;
        return d;
      }
      static data* reallocate( data* d, size_t cap ) {
        if( cap ){
          d = (data*)realloc(d,sizeof(data) + sizeof(T)*(static_cast<size_t>(cap)-1));
          d->capacity = static_cast<size_t>(cap);
        } else {
          d = (data*)realloc(d,sizeof(data));
          d->capacity = 1;
        }
        if( d->size > d->capacity ) 
           d->size = d->capacity;
        return d;
      }
      private:
        data(){};
    };

    template<typename T, typename IsClass=fc::false_type>
    struct vector_impl {
      public:
          typedef T*       iterator;
          typedef const T* const_iterator;
          vector_impl():_data(nullptr){}
          vector_impl( vector_impl&& c):_data(c._data){c._data =nullptr; }
          vector_impl( const vector_impl& c):_data(nullptr) {
            //slog( "copy: c.size %d", c.size() );
            if( c.size() ) {
              _data  = detail::data<T>::allocate( c.size() );
              _data->size = c.size();
              memcpy(begin(),c.begin(), static_cast<size_t>(c.size()) );
            }
            //slog( "copy: this.size %d", size() );
          }
          vector_impl(const_iterator b, const_iterator e ):_data(nullptr) {
            resize(e-b);
            if( size() ) memcpy( data(), b, static_cast<size_t>(size()) );
          }
          vector_impl(size_t s):_data(nullptr){
            resize(s);
          }
          ~vector_impl() {
            clear();
          }


          size_t size()const     { return _data ? _data->size : 0;     }
          size_t capacity()const { return _data ? _data->capacity : 0; }

          T&       back()       { return (&_data->first)[-1+_data->size]; }
          const T& back()const  { return (&_data->first)[-1+_data->size]; }
          T&       front()      { return (&_data->first)[0]; }
          const T& front()const { return (&_data->first)[0]; } 
          const T* data()const  { return (&_data->first);    }
          T*       data()       { return (&_data->first);    }

          iterator       begin()            { return _data ? &front()   : 0;}
          const_iterator begin()const { return _data ? &front()   : 0;}
          iterator       end()        { return _data ? (&back())+1: 0;}
          const_iterator end()const   { return _data ? (&back())+1: 0;}

          T&       operator[]( size_t i )      { return (&_data->first)[i]; }
          const T& operator[]( size_t i )const { return (&_data->first)[i]; }

          T&       at( size_t i )      { return (&_data->first)[i]; }
          const T& at( size_t i )const { return (&_data->first)[i]; }

          void pop_back() { erase( &back() ); }

          void clear() {
            if( _data != nullptr )  {
              free(_data); 
            }
            _data = nullptr;
          }

          void     reserve( size_t i ) {
            _data = detail::data<T>::reallocate( _data, i );
          }

          void     resize( size_t i ) {
            if( capacity() < i ) {
               if( _data )
                   _data = detail::data<T>::reallocate( _data, i );
               else
                   _data = detail::data<T>::allocate( i );
            }
            if( _data ) _data->size = i;
          }

          template<typename U>
          void push_back( U&& v ) {
            resize( size()+1 );
            back() = fc::forward<U>(v);
          }

          template<typename U>
          iterator insert( const_iterator loc, U&& t ) {
            size_t pos = loc - begin();
            resize( size()+1 );
            char* src = &at(pos);
            if( src != &back() ) 
                memmove( src+1, src, (&back() - src) );
            &back = fc::forward<U>(t);
            return &at(pos);
          }

          iterator insert( iterator pos, const_iterator first, const_iterator last ) {
            if( first >= last ) return pos;

            size_t loc = pos - begin();
            size_t right_size = size() - loc;
            resize( size() + (last-first) );
            char* src = &at(loc);
            size_t s = last-first;
            memmove( src + s, src, right_size );
            memcpy( src, first, s );
            _data->size += (last-first);
            return src;
          }

          iterator erase( iterator pos )  {
             memmove( pos, pos+1, (&back() - pos) );
             _data->size--;
             return pos;
          }

          iterator erase( iterator first, iterator last ) {
            if( first != last ) {
               memmove( first, first + (last-first), (&back() - last) );
               _data->size -= last-first;
             }
             return first;
          }

          vector_impl& operator=( vector_impl&& v ) {
             fc_swap(_data,v._data);
             return *this;
          }
          vector_impl& operator=( const vector_impl& v ) {
             vector_impl tmp(v);
             fc_swap(tmp._data,_data);
             return *this;
          }
      protected:
          detail::data<T>*  _data;
    };
    
    template<typename T>
    struct vector_impl<T,fc::true_type>  {
      public: 
          typedef T*       iterator;
          typedef const T* const_iterator;
          vector_impl():_data(nullptr){}
          vector_impl( vector_impl&& c):_data(c._data){c._data =nullptr; }
          vector_impl( const vector_impl& c):_data(nullptr) {
            if( c.size() ) {
              _data  = detail::data<T>::allocate( c.size() );
              auto i = begin();
              auto ci = c.begin();
              auto ce = c.end();
              while( ci != ce ) {
                new (i) T(*ci);
                ++i;
                ++_data->size;
                ++ci;
              }
            }
          }
          vector_impl(const_iterator b, const_iterator e ):_data(nullptr) {
            resize(e-b);
            for( auto i = begin(); i != end(); ++i ) {
              *i = *b;
              ++b;
            }
          }

          vector_impl(size_t s):_data(nullptr){
            resize(s);
          }
          ~vector_impl() {
            clear();
          }


          size_t size()const     { return _data ? _data->size : 0;     }
          size_t capacity()const { return _data ? _data->capacity : 0; }

          T&       back()       { return (&_data->first)[-1+_data->size]; }
          const T& back()const  { return (&_data->first)[-1+_data->size]; }
          T&       front()      { return (&_data->first)[0]; }
          const T& front()const { return (&_data->first)[0]; }
          const T* data()const  { return (&_data->first);    }
          T*       data()       { return (&_data->first);    }

          iterator begin()            { return _data ? &front()   : 0;}
          const_iterator begin()const { return _data ? &front()   : 0;}
          const_iterator end()const   { return _data ? (&back())+1: 0;}

          T&       operator[]( size_t i )      { return (&_data->first)[i]; }
          const T& operator[]( size_t i )const { return (&_data->first)[i]; }

          T&       at( size_t i )      { return (&_data->first)[i]; }
          const T& at( size_t i )const { return (&_data->first)[i]; }

          void pop_back() { erase( &back() ); }


          void clear() {
            if( this->_data != nullptr ) {
              auto c = this->begin();
              auto e = this->end();
              while( c != e ) {
                (*c).~T();
                ++c;
              }
              free(this->_data); 
            }
            this->_data = nullptr;
          }

          void     reserve( size_t i ) {
            if( nullptr != this->_data && i <= this->_data->capacity ) 
               return;
            
            auto _ndata = detail::data<T>::allocate( i );
            auto nc = &_ndata->first;
            auto c = this->begin();
            auto e = this->end();
            while( c != e ) {
              new (nc) T(fc::move( *c )); 
              (*c).~T();
              ++_ndata->size;
              ++c;
              ++nc;
            }
            fc_swap( _ndata, this->_data );
            if( _ndata ) free(_ndata);
          }

          void     resize( size_t i ) {
            this->reserve(i);
            while( i < this->_data->size ) {
              this->back().~T();
              --this->_data->size;
            }
            while( this->_data->size < i ) {
              new (&this->back()+1) T();
              ++this->_data->size;
            }
          }

          template<typename U>
          void push_back( U&& v ) {
            this->reserve( this->size()+1 );
            new (&back()+1) T(fc::forward<U>(v));
            ++this->_data->size;
          }

          template<typename U>
          iterator insert( const_iterator loc, U&& t ) {
            size_t pos = loc - this->begin();
            this->reserve( this->size()+1 );
            loc = this->begin() + pos;
            if( this->size() != 0 ) {
                new ((void*)this->end()) T( fc::move(this->back()) );
                auto cur = this->back();
                ++this->_data->size;
                while( cur != loc ) {
                  *cur = fc::move( *(cur-1) );
                }
                *cur = fc::forward<U>(t);
            } else {
                new (this->end()) T( fc::forward<U>(t) );
                ++this->_data->size;
            }
            return &this->at(pos);
          }

          iterator insert( iterator pos, const_iterator first, const_iterator last ) {
            //static_assert( false, "Not Implemented" );
            return 0;
          }

          iterator erase( iterator pos )  {
             if( pos == this->end() ) { return pos; }
             auto next = pos + 1;
             while( next != this->end() ) {
                *pos = fc::move(*next);
                ++pos; ++next;
             }
             pos->~T();
             this->_data->size--;
             return pos;
          }

          iterator erase( iterator first, iterator last ) {
            iterator c = first;
            iterator m = last;
            iterator e = this->end();
            while( c != e ) {
              if( m != e ) *c = fc::move( *m );
              else c->~T();
              ++c;
              ++m;
            }
            this->_data->size -= last-first;
            return last;
          }
          vector_impl& operator=( vector_impl&& v ) {
             fc_swap(_data,v._data);
             return *this;
          }
          vector_impl& operator=( const vector_impl& v ) {
             vector_impl tmp(v);
             fc_swap(tmp._data,_data);
             return *this;
          }
       private:
          detail::data<T>* _data;
    };
  }

  template<typename T>
  class vector : public detail::vector_impl<T, typename fc::is_class<T>::type> {
    public:
      vector(){}
      vector( size_t s ):detail::vector_impl<T, typename fc::is_class<T>::type>(s){}
      vector( const vector& v ):detail::vector_impl<T, typename fc::is_class<T>::type>(v){}
      vector( vector&& v ):detail::vector_impl<T, typename fc::is_class<T>::type>(fc::move(v)){}

      vector( const T* b, const T* e ):detail::vector_impl<T, typename fc::is_class<T>::type>(b,e){}

      vector& operator=( vector&& v ) {
         *((base*)this) = fc::move(v);
         return *this;
      }
      vector& operator=( const vector& v ) {
         *((base*)this) = v;
         return *this;
      }
    private:
      typedef detail::vector_impl<T, typename fc::is_class<T>::type>  base;
  };

};

