/*
    Copyright (C) 2011 Jaume Singla Valls (jaume dot singlavalls at gmail dot com)

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

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "kernel.h"

namespace Kernel{
	/*
	 * Is an abstract class of delayNode, it was used as a package for delay the execution of transition. It contains a PictureNode.
	 */
	class AbstractDelayNode{
		protected:
			shared_ptr<PictureNode> m_img;
		public:
			/*
			 * Construct the base, assign the pictureNode to attribute
			 */
			AbstractDelayNode(shared_ptr<PictureNode> img){
				m_img=img;
			}
			/*
			 * Destroy the delay. It implies break the cicle pictureNode<->Transition.
			 *
			 */
			virtual ~AbstractDelayNode(){
				//delete m_img;
				m_img->breakCycle();
			}
			
			/*
			 * Obtain the PictureNode of this package delay.
			 */
			shared_ptr<PictureNode> getPicture(){
				return m_img;
			}
			/*
			 * we use that when we don't have motion. 
			 */
			virtual void stop()=0;
			
			/*
			 * Is the method for decrease the time and/or apply transition.
			 * 	time: is a float -1..1 (I only use 0..1)
			 */
			virtual void step(float time)=0;
			/*
			 * It is for know that delay we are using, is not necessary, but I think is interessting to have that.
			 */
			virtual string getName()=0;
	};
	/*
	 * It has a fix motion range declared at the beggining is assigned, and when the motion are in this range, the transition was enabled.
	 */
	class MotionDelayNode: public AbstractDelayNode{
		private:
			float m_init, m_end;
			bool m_lastActived;
		public:
			/*
			 * Construction
			 * 	* img: PictureNode, that contains the element that it show and modify.
			 * 	* init: the value where range motion start. float 0..1
			 * 	* end: the value where range motion end. float 0..1
			 */
			MotionDelayNode(shared_ptr<PictureNode> img, float init, float end):AbstractDelayNode(img){
				m_init=init;
				m_end=end;
				m_lastActived=false;
			};
			virtual string getName(){return "Motion";};
			
			/*
			 * it dissable all transitions. 
			 */
			virtual void stop(){
				if (m_lastActived) {
					m_lastActived=false;
					m_img->setStatus(1);
					m_img->increaseTransition(1);
				}
			}
			
			/*
			 * If we don't activated, but the argument passed is in the range of this package, apply the picture transation to ALL, without intermediate steps.
			 * if we are activated, we need to look if the argument is out of the range, and in this case, disable the transation.
			 */
			virtual void step(float time){
				if (m_lastActived && (time <m_init || time > m_end)) {
					m_lastActived=false;
					m_img->setStatus(1);
					m_img->increaseTransition(1);
				} else if (!m_lastActived && (time >m_init && time < m_end)){
					m_lastActived=true;
					m_img->setStatus(-1);
					m_img->decreaseTransition(1);
				}
			}
		
	};
	
	/*
	 * It don't has a delay, it everithing execute the transition.
	 */
	class NoDelayNode: public AbstractDelayNode{
		public:
			NoDelayNode(shared_ptr<PictureNode> img):AbstractDelayNode(img){};
			virtual string getName(){return "NoDelay";};
			virtual void stop(){};
			/*
			 * It apply transition without delay. Is basically only rotting the status from out to in, and in to out, when in or out finalize.
			 */
			virtual void step(float time){
				if (m_img->getStatus()==0){
					m_img->setStatus(-1);
					m_img->getTransitionOut()->setStatus( m_img->getTransitionIn()->getStatus());
				} else if (m_img->getStatusTransition()==0){
					m_img->setStatus(1);// Rotacion;
					m_img->getTransitionIn()->setStatus( m_img->getTransitionOut()->getStatus());
				} 
				
				if (m_img->getStatus()>0){
					m_img->increaseTransition(time);
				} else if (m_img->getStatus()<1){
					
					m_img->decreaseTransition(time);
				}
				
			}
	};
	
	/*
	 * It begin the transition with a randomly delay, the time was calculed as a motion input in the kernel. 
	 */
	class RandomDelayNode: public AbstractDelayNode{
		private:
			float 	m_toBeginDelay;
			float 	m_waitOut;
			int 	m_number;
			/*
			 * Calculate a new random wait. using the number of images in screen, as an aproximate max random value. 
			 */
			void recalcWait(float w){
				m_toBeginDelay=((float)(rand()%(m_number*10)))/10.0f;
				m_waitOut=w+m_toBeginDelay+1;
			}
		public:
			/*
			 * We need the size of the image number in screen, is only for aproximate the size of random.
			 */
			RandomDelayNode(shared_ptr<PictureNode> img, int size): AbstractDelayNode(img){
				m_number=size;
				this->recalcWait(1);
			}
			
			/*
			 * return the time was needed for begin the transition.
			 */
			float getTimeToBegin(){
				return m_toBeginDelay;
			}
			
			
			/*
			 * return the aproximate time to the end transition.
			 */
			float getTimeToEnd(){
				return m_waitOut;
			}
			
			virtual string getName(){return "Random";};
			
			virtual void stop(){};
			
			/*
			 * We decrease time to begin, and when it is less than 0, start the transition out, and reset to transition in.
			 * 	when the m_waitOut is less than 0, recalculate the new delay time. 
			 */
			void step(float time){
				if (m_toBeginDelay>0 && m_toBeginDelay<time){
					m_img->setStatus(-1);
					m_img->getTransitionOut()->setStatus( m_img->getTransitionIn()->getStatus());
				}
				m_toBeginDelay-=time;
				m_waitOut-=time;
				if (m_toBeginDelay<0)
					m_toBeginDelay=0;
				if (m_waitOut<0){
					this->recalcWait(1);
				}
				
				if (m_img->getStatusTransition()==0){
					m_img->setStatus(1);// Rotacion;
					m_img->getTransitionIn()->setStatus( m_img->getTransitionOut()->getStatus());
				}
				
				if (m_img->getStatus()>0){
					m_img->increaseTransition(time);
				} else if (m_img->getStatus()<0){
					m_img->decreaseTransition(time);
				}
				
			}
			
	};
}