/**********************************************************************
 *      Author: tstern
 *
	Copyright (C) 2018  MisfitTech,  All rights reserved.

    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.

    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 <https://www.gnu.org/licenses/>.

    Written by Trampas Stern for MisfitTech.

    Misfit Tech invests time and resources providing this open source code,
    please support MisfitTech and open-source hardware by purchasing
	products from MisfitTech, www.misifittech.net!
 *********************************************************************/
#include "planner.h"

#include "board.h"
#include "wiring_private.h"
#include "syslog.h"
#include "angle.h"
#include "Arduino.h"

#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.STATUS.bit.SYNCBUSY);

//define the planner class as being global
Planner SmartPlanner;

static bool enterTC3CriticalSection()
{
	bool state=NVIC_IS_IRQ_ENABLED(TC3_IRQn);
	NVIC_DisableIRQ(TC3_IRQn);
	return state;
}

static void exitTC3CriticalSection(bool prevState)
{
	if (prevState)
	{
		NVIC_EnableIRQ(TC3_IRQn);
	} //else do nothing
}




void TC3_Init(void)
{
	// Enable GCLK for TC3
	GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID( GCM_TCC2_TC3 )) ;
	while (GCLK->STATUS.bit.SYNCBUSY);

	TC3->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;   // Disable TCx
	WAIT_TC16_REGS_SYNC(TC3)                      // wait for sync

	TC3->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;   // Set Timer counter Mode to 16 bits
	WAIT_TC16_REGS_SYNC(TC3)

	TC3->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ; // Set TC as normal Normal Frq
	WAIT_TC16_REGS_SYNC(TC3)

	TC3->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV2;   // Set perscaler
	WAIT_TC16_REGS_SYNC(TC3)


	TC3->COUNT16.CC[0].reg = F_CPU/PLANNER_UPDATE_RATE_HZ/2; //divide by two because of prescaler

	WAIT_TC16_REGS_SYNC(TC3)


	TC3->COUNT16.INTENSET.reg = 0;              // disable all interrupts
	TC3->COUNT16.INTENSET.bit.OVF = 1;          // enable overfollow



	NVIC_SetPriority(TC3_IRQn, 3);


	// Enable InterruptVector
	NVIC_EnableIRQ(TC3_IRQn);


	// Enable TC
	TC3->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
	WAIT_TC16_REGS_SYNC(TC3);
}


void TC3_Handler(void)
{
	interrupts(); //allow other interrupts
	//do the planner tick
	SmartPlanner.tick();
	//SerialUSB.println('x');
	TC3->COUNT16.INTFLAG.bit.OVF = 1; //clear interrupt by writing 1 to flag
}

void Planner::begin(StepperCtrl *ptrStepper)
{

	ptrStepperCtrl=ptrStepper;
	currentMode=PLANNER_NONE;
	//setup the timer and interrupt as the last thing
	TC3_Init();
}

void Planner::tick(void)
{
	if (currentMode == PLANNER_NONE)
	{
		return; //do nothing
	}

	if (PLANNER_CV == currentMode)
	{
//		SerialUSB.println(currentSetAngle);
//		SerialUSB.println(endAngle);
//		SerialUSB.println(tickIncrement);
//		SerialUSB.println(fabs(currentSetAngle-endAngle));
//		SerialUSB.println(fabs(tickIncrement*2));
//		SerialUSB.println();
		int32_t x;
		if (fabs(currentSetAngle-endAngle) >= fabs(tickIncrement))
		{
			currentSetAngle+=tickIncrement;
			x=ANGLE_FROM_DEGREES(currentSetAngle);
			ptrStepperCtrl->moveToAbsAngle(x);
		}else
		{
			//we are done, make sure we end at the right point
			//SerialUSB.println("done");
			x=ANGLE_FROM_DEGREES(endAngle);
			ptrStepperCtrl->moveToAbsAngle(x);
			currentMode=PLANNER_NONE;
		}
	}


}

void Planner::stop(void)
{
	bool state;
	state = enterTC3CriticalSection();
	currentMode=PLANNER_NONE;
	exitTC3CriticalSection(state);
}

bool Planner::moveConstantVelocity(float finalAngle, float rpm)
{
	bool state;
	state = enterTC3CriticalSection();

	//first determine if operation is in progress
	if (PLANNER_NONE != currentMode)
	{
#ifndef MECHADUINO_HARDWARE
		//we are in operation return false
		SerialUSB.println("planner operational");
#else
    Serial5.println("planner operational");
#endif
		exitTC3CriticalSection(state);
		return false;
	}

	//get current posistion
	startAngle = ANGLE_T0_DEGREES(ptrStepperCtrl->getCurrentAngle());

	//deterime the tick increment
	tickIncrement=360.0*fabs(rpm)/60/PLANNER_UPDATE_RATE_HZ;



	//set the desired end angle
	endAngle=finalAngle;


	//set the current angle
	currentSetAngle=startAngle;

	if (startAngle>endAngle)
	{
#ifndef MECHADUINO_HARDWARE
		SerialUSB.println("reverse");
#endif
		tickIncrement=-tickIncrement;
	}

//	SerialUSB.println(currentSetAngle);
//		SerialUSB.println(endAngle);
//		SerialUSB.println(tickIncrement);
//		SerialUSB.println();

	currentMode=PLANNER_CV;

	exitTC3CriticalSection(state);
	return true;
}
