#ifndef ELEMENT_CELL_H
#define ELEMENT_CELL_H

#include "Element.h"
#include "EventWindow.h"
#include "ElementTable.h"
#include "itype.h"
#include "P3Atom.h"
#include "Element_Wall.h"  /* For Element_Wall::TYPE */


namespace MFM {
  #define CELL_VERSION 1

  template <class CC>
  class Element_Cell : public Element<CC>
  {
    typedef typename CC::ATOM_TYPE T;
    typedef typename CC::PARAM_CONFIG P;
    enum {
	   R = P::EVENT_WINDOW_RADIUS,
	   BITS = P::BITS_PER_ATOM,
	   DIVIDE_AGE_POS = P3Atom<P>::P3_STATE_BITS_POS,
	   DIVIDE_AGE_LEN = 8,

	   CURRENT_AGE_POS = DIVIDE_AGE_POS + DIVIDE_AGE_LEN,
	   CURRENT_AGE_LEN = 8,

	   INITIAL_DEFAULT_DIVIDE_AGE = 1500,
       DEFAULT_CELL_RADIUS = 3
    };

  private:
	ElementParameterS32<CC> m_cellDivideAge;
    ElementParameterS32<CC> m_cellRadius;



  public:
    typedef BitField<BitVector<BITS>, CURRENT_AGE_LEN, CURRENT_AGE_POS> AFCurrentAge;
    typedef BitField<BitVector<BITS>, DIVIDE_AGE_LEN, DIVIDE_AGE_POS> AFDivideAge;

    static Element_Cell THE_INSTANCE;
    static const u32 TYPE()
    {
      return THE_INSTANCE.GetType();
    }


    Element_Cell()
      : Element<CC>(MFM_UUID_FOR("Cell", CELL_VERSION)),
      m_cellDivideAge(this, "age", "Birth Age","Number of events for a cell to mature",
                      1, INITIAL_DEFAULT_DIVIDE_AGE, 3000, 1000),
      m_cellRadius(this, "radius", "Cell Radius","Distance between Membrane and core",
                      1, DEFAULT_CELL_RADIUS, 10, 1)
    {
      Element<CC>::SetAtomicSymbol("Cl");
      Element<CC>::SetName("Cell");
    }

    virtual const T & GetDefaultAtom() const
    {
      static T defaultAtom(TYPE(),0,0,0);

      this->SetDivideAge(defaultAtom, m_cellDivideAge.GetValue());
      this->SetCurrentAge(defaultAtom, 0);

      return defaultAtom;
    }

    u32 GetCurrentAge(const T& us) const
    {
      return AFCurrentAge::Read(this->GetBits(us));
    }

    void SetCurrentAge(T& us, const u32 age) const
    {
      AFCurrentAge::Write(this->GetBits(us), age);
    }

    u32 GetDivideAge(const T& us) const
    {
      return AFDivideAge::Read(this->GetBits(us));
    }

    void SetDivideAge(T& us, const u32 age) const
    {
      AFDivideAge::Write(this->GetBits(us), age);
    }


    virtual u32 DefaultPhysicsColor() const
    {
      return 0xff00ff00;
    }

    virtual u32 DefaultLowlightColor() const
    {
      return 0xff007f00;
    }

    virtual u32 PercentMovable(const T& you,
                               const T& me, const SPoint& offset) const
    {
      return 0;
    }
    // Non-diffusable
    virtual u32 Diffusability(EventWindow<CC> & ew, SPoint nowAt, SPoint maybeAt) const
    {
      return nowAt.Equals(maybeAt)?Element<CC>::COMPLETE_DIFFUSABILITY:0;
    }

    virtual const char* GetDescription() const
    {
      return "Short for \"Cell\", This Atom encloses the space of "
             "a Cell and leave empty spaces between Cells for Blood Streams.";
    }

    virtual void Behavior(EventWindow<CC>& window) const
    {
      //Random & random = window.GetRandom();
      T self = window.GetCenterAtom();
      const MDist<R>& md = MDist<R>::get();

      SPoint wallRel;
      u32 wallCount = 0;

//      SPoint emptyRel;
//      u32 emptyCount = 0;



      u32 age = this->GetCurrentAge(self);

      //TODO:bool death = age >= 10000;

      //if (death)
      //{
      //  not only the core will die, membranes will die
      //  window.SetCenterAtom(Element_Empty<CC>::THE_INSTANCE.GetDefaultAtom());
      //  return;
      //}

      bool reproable = age >= (u32) m_cellDivideAge.GetValue();

      if (!reproable)
      {
        this->SetCurrentAge(self, 1 + age);
      }

      for (u32 idx = md.GetFirstIndex(1); idx <= md.GetLastIndex(R); ++idx)
      {
        const SPoint rel = md.GetPoint(idx);
        if (!window.IsLiveSite(rel))
        {
          continue;
        }
        T other = window.GetRelativeAtom(rel);
        u32 type = other.GetType();
        //TODO:if find other Element_Cell, too close to build membrane!!

        if(type == Element_Empty<CC>::THE_INSTANCE.GetType())
        {
		  //rel.distance == (u32) m_cellRadius.GetValue();
		  u32 dist=rel.GetManhattanLength();
		  if (dist==(u32) m_cellRadius.GetValue())
		  {
			++wallCount;
			wallRel = rel;
		  }
        }
      }

      if (wallCount > 0)   // Wall-Building
      {

        window.SetRelativeAtom(wallRel,Element_Wall<CC>::THE_INSTANCE.GetDefaultAtom()); // build wall to surround me
      }
      else                 // Can't move
      {
        window.SetCenterAtom(self);  // But can age and get hungry
      }
    }
  };

  template <class CC>
  Element_Cell<CC> Element_Cell<CC>::THE_INSTANCE;
}

#endif //ELEMENT_CELL_H
