/*                                              -*- mode:C++ -*-
  Element_MEmitter.h Basic medicine particle emitting element
  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_MEmitter.h Basic medicine emitting element
  \author Xinyu Chen
  \date (C) 2014 All rights reserved.
  \lgpl
 */
#ifndef ELEMENT_MEMITTER_H
#define ELEMENT_MEMITTER_H

#include "Element.h"
#include "EventWindow.h"
#include "ElementTable.h"
#include "Element_Empty.h"
#include "Element_Medicine.h"
#include "WindowScanner.h"
#include "itype.h"
#include "P3Atom.h"
#include "Logger.h"

namespace MFM
{

#define MEMITTER_VERSION 1

  template <class CC>
  class Element_MEmitter : public Element<CC>
  {
    // Extract short names for parameter types
    typedef typename CC::ATOM_TYPE T;
    typedef typename CC::PARAM_CONFIG P;
    enum { R = P::EVENT_WINDOW_RADIUS,
	   BITS = P::BITS_PER_ATOM,
	   DOSE_POS = P3Atom<P>::P3_STATE_BITS_POS,
	   DOSE_LEN = 16

    };
    typedef BitField<BitVector<BITS>, VD::U32, DOSE_LEN, DOSE_POS> AFDose;
    /*******************************
     * Dose: contorl amount-emit
    ********************************/
    u16 GetDose(const T& us) const
    {
      return AFDose::Read(this->GetBits(us));
    }

    void SetDose(T& us, const u16 timer) const
    {
      AFDose::Write(this->GetBits(us), timer);
    }
    bool DecrementDose(T& us) const
    {
      u32 doseVal = GetDose(us);
      if(doseVal == 0)
      {
	doseVal = 1;
      }
      SetDose(us, doseVal - 1);

      return doseVal == 1;
    }



  private:

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

    Element_MEmitter():
      Element<CC>(MFM_UUID_FOR("MEmitter", MEMITTER_VERSION))
    {
      Element<CC>::SetAtomicSymbol("Me");
      Element<CC>::SetName("MEmitter");
    }
    virtual const T & GetDefaultAtom() const
    {
      static T defaultAtom(TYPE(),0,0,0);
      SetDose(defaultAtom,100);
      return defaultAtom;
    }

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

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

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

    virtual const char* GetDescription() const
    {
      return "This Element emits certain amount of Medicine Particles ";
    }
    virtual void AppendDescription(const T* atomPtr, OString64& desc) const
    {
      const T& atom = *atomPtr;

      u16 x = GetDose(atom);

      desc.Printf("MedEmitter (%d)", x);
    }

    /*******************************
     * DecrementMdeicine:mute
    ********************************/
    bool TestCanEmit(const T& us) const
    {
      if (GetDose(us)==0){
	return false;
      }
      return true;
    }

    void LocateNorth(EventWindow<CC>& window) const
    {
      const T us = window.GetCenterAtom();
      MDist<R>& md = MDist<R>::get();

      SPoint possiblePt;
      s8 possibleX = 0;
      s8 possibleY = 0;
      u8 emptyCount = 0;

      for(u32 i = md.GetFirstIndex(1); i <= md.GetLastIndex(1); i++)
      {
	possiblePt=md.GetPoint(i);
	if (!window.IsLiveSite(possiblePt))
	{
	  continue;
	}
	T other = window.GetRelativeAtom(possiblePt);
        u32 type = other.GetType();
        if(type == Element_Empty<CC>::THE_INSTANCE.GetType())
	{
	  possibleX = possiblePt.GetX();
	  possibleY = possiblePt.GetY();
	  if ((possibleX==0) && (possibleY==-1))
	  {
	    window.SetRelativeAtom(possiblePt, us);
	    emptyCount++;
	    break;
	  }
	}
      }//endFor
      if(emptyCount>0){
	window.SetCenterAtom(Element_Empty<CC>::THE_INSTANCE.GetDefaultAtom());
      }
    }
    void EmitMedicine(EventWindow<CC>& window) const
    {
      Random & random = window.GetRandom();
      const T us = window.GetCenterAtom();
      T copyUs = window.GetCenterAtom();
      MDist<R>& md = MDist<R>::get();

      SPoint possiblePt;

      bool canEmit = true;
      u16 stockDose = 0;
      stockDose= GetDose(us);

      for(u32 i = md.GetFirstIndex(1); i <= md.GetLastIndex(1); i++)
      {
	possiblePt=md.GetPoint(i);
	if (!window.IsLiveSite(possiblePt))
	{
	  continue;
	}
	if(stockDose==0){
	  canEmit=false;
	}
	T other = window.GetRelativeAtom(possiblePt);
	u32 type = other.GetType();
	if(type == Element_Empty<CC>::THE_INSTANCE.GetType())
	{
	  if((canEmit)&&(random.OneIn(1))){
	    T atom = Element_Medicine<CC>::THE_INSTANCE.GetDefaultAtom();
	    window.SetRelativeAtom(possiblePt, atom);
	    stockDose--;
	  }
	}
      }//endFor
      SetDose(copyUs,stockDose);
      window.SetCenterAtom(copyUs);
    }

    virtual void Behavior(EventWindow<CC>& window) const
    {
      this->LocateNorth(window);
      this->EmitMedicine(window);

    }
  };

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

#endif /* ELEMENT_EMITTER_H */
