/*
 * nzs_lcd.cpp
 *
 *  Created on: Dec 8, 2016
 *      Author: trampas
 *
	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 "nzs_lcd.h"
#include <string.h>
#include <stdio.h>
#include <Wire.h>


#ifndef DISABLE_LCD
void LCD::begin(StepperCtrl *ptrsCtrl)
{
#ifndef MECHADUINO_HARDWARE
	pinMode(PIN_SW1, INPUT_PULLUP);
	pinMode(PIN_SW3, INPUT_PULLUP);
	pinMode(PIN_SW4, INPUT_PULLUP);
#endif
	buttonState=0;

	//we need access to the stepper controller
	ptrStepperCtrl=ptrsCtrl; //save a pointer to the stepper controller


	ptrMenu=NULL;
	menuIndex=0;
	menuActive=false;
	optionIndex=0;
	ptrOptions=NULL;
	displayEnabled=true;

	//check that the SCL and SDA are pulled high
	pinMode(PIN_SDA, INPUT);
	pinMode(PIN_SCL, INPUT);
	if (digitalRead(PIN_SDA)==0)
	{
		//pin is not pulled up
		displayEnabled=false;
	}
	if (digitalRead(PIN_SCL)==0)
	{
		//pin is not pulled up
		displayEnabled=false;
	}

	if (displayEnabled)
	{
		displayEnabled=display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
	}else
	{
		WARNING("SCL/SDA not pulled up");
	}
	if (false == displayEnabled)
	{
		WARNING("NO display found, LCD will not be used");
	}
	Wire.setClock(800000);

	//showSplash();

}


void __attribute__ ((optimize("Ofast"))) LCD::lcdShow(const char *line1, const char *line2,const char *line3)
{

	if (false == displayEnabled)
	{
		return;
	}
	display.clearDisplay();
	display.setTextSize(2);
	display.setTextColor(WHITE);
	display.setCursor(0,0);
	display.println(line1);
	display.setCursor(0,20);
	display.println(line2);
	display.setCursor(0,40);
	display.println(line3);
	display.display();

}

void LCD::showSplash(void)
{
	if (false == displayEnabled)
	{
		return;
	}
#ifdef A5995_DRIVER
	lcdShow("MisfitTech","NEMA 23", VERSION);
#else
	lcdShow("MisfitTech","NEMA 17", VERSION);
#endif
}


void LCD::setMenu(menuItem_t *pMenu)
{
	if (false == displayEnabled)
	{
		return;
	}
	ptrMenu=pMenu;
	menuIndex=0;
}


void LCD::showOptions(void)
{
	int32_t i,j;
	char str[3][26]={0};
	if (false == displayEnabled)
	{
		return;
	}

	i=optionIndex;
	j=0;
	while(strlen(ptrOptions[i].str)>0 && j<3)
	{
		if (i == optionIndex)
		{
			sprintf(str[j],"*%s",ptrOptions[i].str);
		}else
		{
			sprintf(str[j]," %s",ptrOptions[i].str);
		}
		j++;
		i++;
	}

	lcdShow(str[0], str[1], str[2]);

	return;
}


void __attribute__ ((optimize("Ofast"))) LCD::showMenu(void)
{
	int32_t i,j;
	char str[3][26]={0};
	if (false == displayEnabled)
	{
		return;
	}

	i=menuIndex;
	j=0;
	while(ptrMenu[i].func!=NULL && j<3)
	{
		if (i == menuIndex)
		{
			sprintf(str[j],"*%s",ptrMenu[i].str);
		}else
		{
			sprintf(str[j]," %s",ptrMenu[i].str);
		}
		j++;
		i++;
	}

	//show exit if there is room
	if (j<3)
	{
		if (j==0)
		{
			sprintf(str[j],"*Exit");
		}else
		{
			sprintf(str[j]," Exit");
		}
	}

	lcdShow(str[0], str[1], str[2]);


	return;
}


void __attribute__ ((optimize("Ofast"))) LCD::updateMenu(void)
{
	if (false == displayEnabled)
	{
		return;
	}

	if (ptrOptions != NULL)
	{
		showOptions();
	}else
	{
		showMenu();
	}

	//handle push buttons
	if (digitalRead(PIN_SW3)==0 && (buttonState & 0x02)==0)
	{
		buttonState |= 0x02;

		LOG("SW3 pressed");
		if (ptrMenu[menuIndex].func == NULL)
		{
			//exit pressed
			menuIndex=0; //reset menu index
			menuActive=false;
			return;
		}

		if (ptrMenu[menuIndex].func != NULL)
		{
			LOG("Calling function for %s",ptrMenu[menuIndex].str);
			if (ptrOptions != NULL)
			{
				char *ptrArgV[1];
				char str[25]={0};
				ptrArgV[0]=str;
				sprintf(str,"%d",optionIndex);
				LOG("Calling function for %s %s",ptrMenu[menuIndex].str,str);
				ptrMenu[menuIndex].func(1,ptrArgV);
				ptrOptions=NULL;
				optionIndex=0;
			}else
			{
				int i;
				i=ptrMenu[menuIndex].func(0,NULL);
				if (ptrMenu[menuIndex].ptrOptions != NULL)
				{
					LOG("displaying options for %s %d",ptrMenu[menuIndex].str,i);
					ptrOptions=ptrMenu[menuIndex].ptrOptions;
					optionIndex=i;
				}
			}

			return;
		}

	}
	if (digitalRead(PIN_SW1)==0 && (buttonState & 0x01)==0)
	{
		buttonState |= 0x01;
		LOG("SW1 pressed");
		if (ptrOptions != NULL)
		{
			optionIndex++;
			if (strlen(ptrOptions[optionIndex].str) == 0)
			{
				optionIndex=0;
			}
		} else
		{
			if (ptrMenu[menuIndex].func != NULL)
			{
				menuIndex++;
			} else
			{
				menuIndex=0;
			}
		}

	}

	if (digitalRead(PIN_SW1))
	{
		buttonState &= ~0x01;
	}

	if (digitalRead(PIN_SW3))
	{
		buttonState &= ~0x02;
	}
}

void LCD::forceMenuActive(void)
{

	menuActive=true;
}

void __attribute__((optimize("Ofast")))LCD::process(void)
{
	if (false == displayEnabled)
	{
		return;
	}

	if (false == menuActive || ptrMenu==NULL)
	{
		updateLCD();
	}else
	{
		updateMenu();
	}

	if (digitalRead(PIN_SW4)==0 && (buttonState & 0x04)==0)
	{
		buttonState |= 0x04;
		menuActive=!menuActive;
	}

	if (digitalRead(PIN_SW4))
	{
		buttonState &= ~0x04;
	}
}
#endif
/*
//does the LCD menu system
void StepperCtrl::menu(void)
{

	bool done=false;
	int menuItem=0;
	char str[100];
	int sw1State=0;
	int sw3State=0;

	pinMode(PIN_SW1, INPUT_PULLUP);
	pinMode(PIN_SW3, INPUT_PULLUP);
	pinMode(PIN_SW4, INPUT_PULLUP);


	while (!done)
	{
		display.clearDisplay();
		display.setTextSize(2);
		display.setTextColor(WHITE);

		if (menuItem==0)
		{
			sprintf(str,"*Run Cal");
			display.setCursor(0,0);
			display.println(str);
		}else
		{
			sprintf(str," Run Cal");
			display.setCursor(0,0);
			display.println(str);
		}

		if (menuItem==1)
		{
			sprintf(str,"*Check Cal");
			display.setCursor(0,20);
			display.println(str);
		}else
		{
			sprintf(str," Check Cal");
			display.setCursor(0,20);
			display.println(str);
		}

		if (menuItem==2)
		{
			sprintf(str,"*Exit");
			display.setCursor(0,40);
			display.println(str);
		}else
		{
			sprintf(str," Exit");
			display.setCursor(0,40);
			display.println(str);
		}

		display.display();

		if (sw1State==1)
		{
			while (digitalRead(PIN_SW1)==0);
			sw1State=0;
		}

		if (digitalRead(PIN_SW1)==0)
		{
			sw1State=1;
			menuItem=(menuItem+1)%3;
		}

		if (sw3State==1)
		{
			while (digitalRead(PIN_SW3)==0);
			sw3State=0;
		}

		if (digitalRead(PIN_SW3)==0)
		{
			sw3State=1;
			switch(menuItem)
			{
				case 0:
					display.clearDisplay();
					display.setTextSize(2);
					display.setTextColor(WHITE);
					display.setCursor(0,0);
					display.println("Running");
					display.setCursor(0,20);
					display.println("Cal");
					display.display();
					calibrateEncoder();
					break;
				case 1:
				{
					display.clearDisplay();
					display.setTextSize(2);
					display.setTextColor(WHITE);
					display.setCursor(0,0);
					display.println("Testing");
					display.setCursor(0,20);
					display.println("Cal");
					display.display();
					int32_t error,x,y,m;
					error=maxCalibrationError();
					x=(error*100 *360)/ANGLE_STEPS;
					m=x/100;
					y=abs(x-(m*100));
					display.clearDisplay();
					display.setTextSize(2);
					display.setTextColor(WHITE);
					display.setCursor(0,0);
					display.println("Error");

					sprintf(str, "%02d.%02d deg",m,y);
					display.setCursor(0,20);
					display.println(str);
					display.display();
					while (digitalRead(PIN_SW3));
					break;
				}
				case 2:
					return;
					break;

			}

		}

	}

}

 */

void LCD::updateLCD(void)
{
	if (false == displayEnabled)
	{
		return;
	}
	char str[3][25];
	static int highRPM=0;
	int32_t y,z,err;

	static int64_t lastAngle,deg;
	static int32_t RPM=0;
	static int32_t lasttime=0;

	bool state;
	static int32_t dt=40;
	static uint32_t t0=0;

	static bool rpmDone=false;

	if ((millis()-t0)>500)
	{

		int32_t x,d;

		//do first half of RPM measurement
		if (!rpmDone)
		{
			//LOG("loop time is %dus",ptrStepperCtrl->getLoopTime());
			lastAngle=ptrStepperCtrl->getCurrentAngle();
			lasttime=millis();
			rpmDone=true;
			return;
		}

		//do the second half of rpm measurement and update LCD.
		if (rpmDone && (millis()-lasttime)>(dt))
		{
			rpmDone=false;
			deg=ptrStepperCtrl->getCurrentAngle();
			y=millis()-lasttime;
			err=ptrStepperCtrl->getLoopError();

			t0=millis();
			d=(int64_t)(lastAngle-deg);

			d=abs(d);

			x=0;
			if (d>0)
			{
				x=((int64_t)d*(60*1000UL))/((int64_t)y * ANGLE_STEPS);
			}

			lastAngle=deg;
			RPM=(int32_t)x; //(7*RPM+x)/8; //average RPMs
			if (RPM>500)
			{
				dt=10;
			}
			if (RPM<100)
			{
				dt=100;
			}
			str[0][0]='\0';
			//LOG("RPMs is %d, %d, %d",(int32_t)x,(int32_t)d,(int32_t)y);
			switch(ptrStepperCtrl->getControlMode())
			{
				case CTRL_SIMPLE:
					sprintf(str[0], "%dRPM simp",RPM);
					break;
				case CTRL_TORQUE:
					sprintf(str[0], "%dRPM torque",RPM);
					break;
				case CTRL_POS_PID:
					sprintf(str[0], "%dRPM pPID",RPM);
					break;
				case CTRL_POS_VELOCITY_PID:
					sprintf(str[0], "%dRPM vPID",RPM);
					break;
				case CTRL_OPEN:
					sprintf(str[0], "%dRPM open",RPM);
					break;
				case CTRL_OFF:
					sprintf(str[0], "%dRPM off",RPM);
					break;
				default:
					sprintf(str[0], "error %u",ptrStepperCtrl->getControlMode());
					break;

			}


			err=(err*360*100)/(int32_t)ANGLE_STEPS;
			//LOG("error is %d %d %d",err,(int32_t)ptrStepperCtrl->getCurrentLocation(),(int32_t)ptrStepperCtrl->getDesiredLocation());
			z=(err)/100;
			y=abs(err-(z*100));

			sprintf(str[1],"%01d.%02d err", z,y);


			deg=ptrStepperCtrl->getDesiredAngle();

#ifndef NZS_LCD_ABSOULTE_ANGLE
			deg=deg & ANGLE_MAX; //limit to 360 degrees
#endif

			deg=(deg*360*10)/(int32_t)ANGLE_STEPS;
			int K=0;
			if (abs(deg)>9999)
			{
				K=1;
				deg=deg/1000;
			}

			x=(deg)/10;
			y=abs(deg-(x*10));

			if (K==1)
			{
				sprintf(str[2],"%03d.%01uKdeg", x,y);
			}else
			{
				sprintf(str[2],"%03d.%01udeg", x,y);
			}
			str[0][10]='\0';
			str[1][10]='\0';
			str[2][10]='\0';
			lcdShow(str[0],str[1],str[2]);
		}
	}
}


