/*                                              -*- mode:C++ -*-
  Element_Medicine.h an element medicine particles
  move from right to left, attracted by Aura, enter tumor, kill tumor
  Copyright (C) 2014 The Regents of the University of New Mexico.  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  USA
*/

/**
  \file Element_Medicine.h Basic integer holding element
  \author Xinyu Chen
  \date (C) 2014 All rights reserved.
  \lgpl
 */
#ifndef ELEMENT_MEDICINE_H
#define ELEMENT_MEDICINE_H

#include "Element.h"
#include "ColorMap.h"
#include "EventWindow.h"
#include "ElementTable.h"
#include "itype.h"
#include "P3Atom.h"
#include "Logger.h"
//#include "Element_Tumor.h"
#include "Element_Wall.h"
#include "WindowScanner.h"
namespace MFM
{

#define MEDICINE_VERSION 1

  template <class CC>
  class Element_Medicine : 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,

	   DIRX_POS = P3Atom<P>::P3_STATE_BITS_POS,
	   DIRX_LEN = 8,

	   DIRY_POS = DIRX_POS+DIRX_LEN,
	   DIRY_LEN = 8,

	   AURAID_POS = DIRY_POS+DIRY_LEN,
	   AURAID_LEN = 16
    };
    typedef BitField<BitVector<BITS>, VD::U32, DIRX_LEN, DIRX_POS> AFXLoc;
    typedef BitField<BitVector<BITS>, VD::U32, DIRY_LEN, DIRY_POS> AFYLoc;
    typedef BitField<BitVector<BITS>, VD::U32, AURAID_LEN, AURAID_POS> AFAid;

    /*******************************
     * cordinates X and Y
    ********************************/
    s8 GetXLoc(const T& us) const
    {
      return (s8)AFXLoc::Read(this->GetBits(us));
    }

    s8 GetYLoc(const T& us) const
    {
      return AFYLoc::Read(this->GetBits(us));
    }


    void SetLoc(T& us, const SPoint& loc) const
    {
      SetXLoc(us, (s8)loc.GetX());
      SetYLoc(us, (s8)loc.GetY());
    }

    void FillLoc(SPoint& pt, const T& us) const
    {
      pt.Set(GetXLoc(us), GetYLoc(us));
    }

  private:


  public:
    static Element_Medicine THE_INSTANCE;
    static const u32 TYPE()
    {
      return THE_INSTANCE.GetType();
    }

    Element_Medicine() : Element<CC>(MFM_UUID_FOR("Request", MEDICINE_VERSION))
    {
      Element<CC>::SetAtomicSymbol("Rq");
      Element<CC>::SetName("Request");
    }
    u16 GetAid(const T& us) const
    {
      return AFAid::Read(this->GetBits(us));
    }

    void SetAid(T& us, const u16 timer) const
    {
      AFAid::Write(this->GetBits(us), timer);
    }

    virtual void SetXLoc(T& us, const s8 xloc) const
    {
      AFXLoc::Write(this->GetBits(us), xloc);
    }

    virtual void SetYLoc(T& us, const s8 timer) const
    {
      AFYLoc::Write(this->GetBits(us), timer);
    }

    virtual const T & GetDefaultAtom() const
    {
      static T defaultAtom(TYPE(),0,0,0);
      SetXLoc(defaultAtom,0);
      SetYLoc(defaultAtom,0);
      SetAid(defaultAtom,0);
      return defaultAtom;
    }

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

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

    virtual u32 PercentMovable(const T& you, const T& me, const SPoint& offset) const
    {
      return 100;
    }

    virtual const char* GetDescription() const
    {
      return "An element designed simply attracted by Signal "
             "Emitted by MEmitter, consumed by Provider.";
    }
    virtual void AppendDescription(const T* atomPtr, OString64& desc) const
    {
      const T& atom = *atomPtr;
      s16 d = GetAid(atom);
      s8 x = GetXLoc(atom);
      s8 y = GetYLoc(atom);

      desc.Printf("dns(%d),dir(%d,%d)",d,x,y);
    }

    /*******************************
     * find Tumor and Fight *
     *******************************/
    virtual bool Fight(EventWindow<CC>& window) const;
    /*******************************
     * scan and move *
     *******************************/
    virtual void Move(EventWindow<CC>& window) const;
    /************************************************
     * find somewhere to Flight according to memory *
     ************************************************/
    bool Sneak(EventWindow<CC>& window) const{
      MDist<R>& md = MDist<R>::get();
      SPoint emptyRel;
      s8 xLoc = 0;
      s8 yLoc = 0;
      u8 emptyCount = 0;
      s8 quadrant = 0;
      bool canSneak =false;
      const T self = window.GetCenterAtom();
      if((GetXLoc(self)==0)&&(GetYLoc(self)==0)){
	quadrant=-1;
      }else{
	if(GetXLoc(self)>0){
	  if(GetYLoc(self)>0){
	    quadrant = 0;//southeast
	  }
	  else{
	    quadrant = 1;//northeast
	  }
	}
	else{
	  if(GetYLoc(self)>0){
	    quadrant = 2;//southwest
	  }
	  else{
	    quadrant = 3;//northwest
	  }
	}
      }
      //LOG.Debug(" ==>sneak(%d,%d) qua(%d)",GetXLoc(self),GetYLoc(self),quadrant);
      if(quadrant>=0){
	for (u32 idx = md.GetFirstIndex(1); idx <= md.GetLastIndex(R); ++idx)
	{
	    const SPoint relE = md.GetPoint(idx);
	    if (!window.IsLiveSite(relE)){
	      continue;
	    }
	    xLoc = relE.GetX();
	    yLoc = relE.GetY();
	    switch(quadrant){
	    case 0://southease
	      if((xLoc>0)&&(yLoc>0)){
		canSneak=true;
	      }
	      break;
	    case 1://northeast
	      if((xLoc>0)&&(yLoc<=0)){
		canSneak=true;
	      }
	      break;
	    case 2://southwest
	      if((xLoc<=0)&&(yLoc>0)){
		canSneak=true;
	      }
	      break;
	    case 3://northwest
	      if((xLoc<=0)&&(yLoc<=0)){
		canSneak=true;
	      }
	      break;
	    }
	    if(canSneak){
	      T possibleEmpty = window.GetRelativeAtom(relE);
	      u32 typeE = possibleEmpty.GetType();
	      if(typeE == Element_Empty<CC>::THE_INSTANCE.GetType()){
		++emptyCount;
		emptyRel = relE;
	      }
	    }
	}
      }
      if(emptyCount>0){
	window.SetRelativeAtom(emptyRel,self);
	window.SetCenterAtom(Element_Empty<CC>::THE_INSTANCE.GetDefaultAtom());
	return true;
      }
      return false;
    }
    /*******************************
     * tell Others *
     *******************************/
    bool tellOther(EventWindow<CC>& window) const{
      MDist<R>& md = MDist<R>::get();
      T me =window.GetCenterAtom();
      SPoint medRel;
      u32 medCount = 0;

      s8 xLoc = 0;
      s8 yLoc = 0;
      s8 xOffset=0;
      s8 yOffset=0;

      for (u32 idx = md.GetFirstIndex(1); idx <= md.GetLastIndex(R); ++idx)
      {
        const SPoint relM = md.GetPoint(idx);
        if (!window.IsLiveSite(relM))
        {
          continue;
        }
        T other = window.GetRelativeAtom(relM);
        u32 typeM = other.GetType();
        if(typeM == Element_Medicine<CC>::THE_INSTANCE.GetType())
        {
	  xLoc = GetXLoc(other);
	  yLoc = GetYLoc(other);
	  xOffset=relM.GetX();
	  yOffset=relM.GetY();
	  if((xLoc==0)&&(yLoc==0)){
	    medRel=relM;
	    SetXLoc(other,-1*xOffset);
	    SetYLoc(other,-1*yOffset);
	    ++medCount;
	  }
        }
      }
      if(medCount>0){
	//LOG.Debug("Tell Med at(%d,%d) move towards me(%d,%d)",xOffset,yOffset,-1*xOffset,-1*yOffset);
	return false;
      }
      return true;
    }
    /************************************************************/
    /* main behavior */
    /************************************************************/

    virtual void Behavior(EventWindow<CC>& window) const
    {
      //Random & random = window.GetRandom();
      T me = window.GetCenterAtom();
      bool isAlive = true;
      /*******************************
       * find Tumor and burn *
       *******************************/
      isAlive = Fight(window);
      if(isAlive){
	this->Move(window);
      }
    }
  };

  template <class CC>
  Element_Medicine<CC> Element_Medicine<CC>::THE_INSTANCE;

}
#include "Element_Medicine.tcc"
#endif /* ELEMENT_MEDICINE_H */
