/* ===========================================================================================================================================================================================================
 * Lab 2
 * ===========================================================================================================================================================================================================
 * Changelog: 2019-02-22
 * Attempting the "kill-method": The sequencer pre-calculates the entire length of the note, then enables the ToneGen and schedules itself for after the note length MINUS a short silence.
 * Upon entering the second time, the sequencer kills the ToneGen and schedules itself for after the silence length.
 
 * Keyboard reader logic: integers are entered and stored in sequence (ie. 5, 2, -99) and delimited by a character denoting the target function (ie. 'k' will call setKey with the buffered int as the arg).
 * 
 * Sequencer now stores the current noteLength as state var. as this information is needed between two consecutive invocations IF the silence length is variable.
 * Currently the silence length is fixed to 50ms.
 
 * ===========================================================================================================================================================================================================
 * To-do
 * * Improve keyboard character parser (ie. test for alphanumeric chars etc...) to reject invalid input.
 * *
 */ 
 
/* 
 * ===========================================================================================================================================================================================================
 * Changelog: 2019-03-05
 * Changed toggleLeader() to use state variable in App obj. for leader state. Initialize to SLAVE, ie. LEADER = false.
 * Added readTempo() (&s1) function in order to calculate CANON initial delay in receiver (&app). Call with SYNC.
 * Added new CANON case to receiver(). See comments in method.
 * Added CAN_SEND_CANON() function. Completely hardcoded atm.
 * Added CAN_SEND* function prototypes.
 *
 * TO-DO:
 * * Replace calls to own object with 'self' rather than '&obj'.
 */ 

 


#include "TinyTimber.h"
#include "sciTinyTimber.h"
#include "canTinyTimber.h"
#include <stdlib.h>
#include <stdio.h>

#include <stdbool.h>
#include <string.h>

// How many beats (or multiples thereof) slave boards should wait before initially playing.
#define CANON_BEAT_OFFSET 8

#define MSGID 1
// NodeID is central to the CAN protocol and must be static. 0 is always leader. NodeID > 0 are slaves with incrementing multiples of beats offset in CANON mode.
// ie. Slave1 has NodeID=1 and delays playing by n beats. Slave2 has NodeID=2 delays by 2n beats.
#define NODEID 1

#define MAX_VOL 25

#define MAX_IDX 14
#define MIN_IDX -10

#define MIN_KEY -5
#define MAX_KEY  5

#define MIN_TEMPO 60 // bpm
#define MAX_TEMPO 240


// typedef volatile unsigned int* port16ptr;
// #define DAC_ADDR 0x4000741C
// #define DACport *((port16ptr) DAC_ADDR)
#define DACport DAC->DHR8R2

// whole note, double note, half note.
typedef enum {a, b, c} beat_t;

// Indices originally have origin at 440Hz. Shifted by MIN_IDX to make freqs vector addressable.
int period_idx[] =  {10,12,14,10,10,12,14,10,14,15,17,14,15,17,17,19,17,15,14,10,17,19,17,15,14,10,10,5,10,10,5,10}; // Indexed by seq_pos
int period[] = {2024,1911,1803,1702,1607,1516,1431,1351,1275,1203,1136,1072,1012,955,901,851,803,758,715,675,637,601,568,536,506}; // indexed by idx
beat_t beat_arr[] = { a, a, a, a, a, a, a, a, a, a, b, a, a, b, c, c, c, c, a, a, c, c, c, c, a, a, a, a, b, a, a, b }; // indexed by seq_pos


//============================//
// Class declarations	  //
//============================//

typedef struct {
    Object super;
    int count;
    char c;
	int num;
	int sum;
	bool LEADER;
	char buff[100]; // temporary character buffer. Stores keyboard input.
} App;

App app = { initObject(), 0, 'X', 0, 0, false };

typedef struct {
	Object super;
	int volume;
	int period;    // usec
	int deadline; // usec
	bool last;
	int lastVolume;
	bool mute;
   bool alive;
} ToneGen;

ToneGen t1 = { initObject(), 15, 500, 100, false, 0, false, true };

typedef struct {
	Object super;
	int seq_idx;
	int tempo;  // beats per minute
	int key;
	bool silence;
   Time noteLength;
   bool alive;
   bool terminated;
   Msg ID;
} Sequencer;

Sequencer s1 = { initObject(), 0, 120, 0, false, 1, false, true }; // Msg ID omitted. 


typedef struct {
	char * str;
	int len;
} CANstruct;

//============================//
//      Function prototypes   //
//============================//
// SERIAL
void reader(App*, int);


void incVol(ToneGen*, int);
void decVol(ToneGen*, int);
void muteVol(ToneGen*, int);

void setPeriod(ToneGen*, int);
void enable(ToneGen*, int);
void kill(ToneGen*, int);

void SEQUENCE(Sequencer*, int);
void PLAY(Sequencer*, int);
void setTempo(Sequencer*, int);
void setKey(Sequencer*, int);
void setPos(Sequencer* , int );

// CAN stuf
void sender(App* , int );
void receiver(App*, int); // CAN receiver
void CAN_SEND_CANON(App*, int);
void CAN_SEND_KEY(App*, int);
void CAN_SEND_TEMPO(App*, int);
void CAN_SEND_PLAY(App*, int);

void toggleLeader(App*, int);


//============================//
//      Object INSTANTIATIONS //
//           & INIT       	  //
//============================//


Serial sci0 = initSerial(SCI_PORT0, &app, reader);
Can can0 = initCan(CAN_PORT0, &app, receiver);

//============================//
//      misc METHODS       //
//============================//
void toggleLeader(App* app, int unused){
	self->LEADER = !(self->LEADER);
	
	char snprintf_buff[50];
	snprintf(snprintf_buff, 100, "%d", self->LEADER);
	SCI_WRITE(&sci0, "Leader state: ");
	SCI_WRITE(&sci0, snprintf_buff);
	SCI_WRITE(&sci0, "\n");
}


//============================//
//      ToneGen METHODS       //
//============================//
void SQUARE(ToneGen *self, int unused){
   if(self->alive){
   
      if (self->last == true){
         DACport = 0x0;} // DAC output 2 / 8 bit right aligned
      else{
         DACport = self->volume;}

     // Update last state-aa
      self->last = !(self->last);

      SEND(USEC(self->period), USEC(self->deadline) ,self, SQUARE, 0);
   }
}

// Enable the tonegen at the beginning of each note. Kill the task to "generate" silence.
void kill(ToneGen *self, int unused){
   self->alive = false;
}

void enable(ToneGen *self, int unused){
   self->alive = true;
}

// Volume control
void incVol(ToneGen *self, int unused){
	SCI_WRITE(&sci0, "incVol\n");
  if (self->volume < MAX_VOL){
    self->volume++;
	self->mute = false;
  }
}

void decVol(ToneGen *self, int unused){
	SCI_WRITE(&sci0, "decVol\n");
  if (self->volume > 0){
    self->volume--;
	if (self->volume == 0){
		self->mute = true;
		self->lastVolume = 0;
	}
  }
}

void muteVol(ToneGen *self, int unused){
	SCI_WRITE(&sci0, "mutVol\n");
	
	if (self->volume != 0 && self->mute == false){
		self->lastVolume = self->volume;
		self->mute = true;
		self->volume = 0;
	}else{
		self->volume = self->lastVolume;
		self->mute 	 = false;
	}
}

void setPeriod(ToneGen* self, int new_period){
	self->period = new_period;
}

//============================//
//      Sequencer METHODS       //
//============================//

void PLAY(Sequencer* self, int unused){
	
	if(self->alive == true){ // PLAY->STOP
		self->alive = false;
		
	}else if(self->terminated == true && self->alive == false){ // STOP -> PLAY
		self->seq_idx = 0;
		self->ID = ASYNC(&s1, SEQUENCE, 0); 
		self->alive = true;
	}
	
	
	
	/*
	if(self->alive == true){
		self->alive = false;
	}
	else{
		if(self->terminated == true){
			self->alive = true;
			ASYNC(&s1, setPos, 0);
			self->ID = ASYNC(&s1, SEQUENCE, 0);   // save every scheduled SEQUENCE invocation
		}else{
			; // wait
		}
	}
	 */



}

void setPos(Sequencer* self, int pos){
		self->seq_idx = pos;
}


void setKey(Sequencer* self, int newKey){
   if(newKey <= MAX_KEY && newKey >= MIN_KEY){
      self->key = newKey;
      SCI_WRITE(&sci0, "Key changed LOCAL.");
   }
   else
      SCI_WRITE(&sci0, "Out of bounds!");
}

void setTempo(Sequencer* self, int newTempo){
   if(newTempo <= MAX_TEMPO && newTempo >= MIN_TEMPO){
      self->tempo = newTempo;
      SCI_WRITE(&sci0, "Tempo changed LOCAL.");
   }
   else
      SCI_WRITE(&sci0, "Out of bounds!");
}

// The sequencer can be viewed as a two-state FSM: tone and silence states.
// The states will alternate and the actions on each transition are given by the if-else statement below.
// Tone->Silence
   // Kill the squarewave generator (ie. stop it from invoking itself).
   // Increment sequence position (seq_idx).
   // Schedule next Sequencer transition using silence length.
// Silence->Tone
   // Calculate total note length from TEMPO and current BEAT.
   // Send current tone freq. to ToneGen (setPeriod())
   // Enable the squarewave generator.
   // "Kickoff" the squarewave generator ie. call it initially, it will call itself recursively therafter.
   // Schedule next Sequencer transition using the calculated note length (and a silence length [which may be unknown]).
void SEQUENCE(Sequencer* self, int unused){
		self->terminated = false;
		
		if(self->silence == false){
			self->noteLength = MSEC( 1000*60 / (self->tempo) ); // gives usec in type Time from bpm
			
			/*
			char debug_buff[100];
			snprintf(debug_buff, 100, "%d", self->seq_idx);
			SCI_WRITE(&sci0, debug_buff);
			*/
			
			/*
			snprintf(debug_buff, 100, "%d", t1.volume);
			SCI_WRITE(&sci0,"ToneGen Volume ");
			SCI_WRITE(&sci0, debug_buff);
			SCI_WRITE(&sci0,"\n");
			
			snprintf(debug_buff, 100, "%u", MSEC_OF(self->noteLength));
			SCI_WRITE(&sci0,"Initial noteLen before beat modulation: ");
			SCI_WRITE(&sci0, debug_buff);
			SCI_WRITE(&sci0,"\n\n");
			 * */
		  
		  switch(beat_arr[self->seq_idx]){
			 case(a):
				;
			 break;
			 case(b):
				self->noteLength *= 2;
			 break;
			 case(c):
				// self->noteLength /= 2;
				self->noteLength = self->noteLength >> 1;
			 break;
			 default:
			 break;
		  }
		  /*
		  snprintf(debug_buff, 100, "%lu", self->noteLength);
			SCI_WRITE(&sci0,"Initial noteLen AFTER beat modulation: ");
			SCI_WRITE(&sci0, debug_buff);
			SCI_WRITE(&sci0,"\n\n");
		   
			int current_period = period[period_idx[self->seq_idx] + self->key];
			
			snprintf(debug_buff, 100, "%u", current_period);
			SCI_WRITE(&sci0,"Current period (sent to tonegen) ");
			SCI_WRITE(&sci0, debug_buff);
			SCI_WRITE(&sci0,"\n");
			*/
			
			int current_period = period[period_idx[self->seq_idx] + self->key];
			
			 ASYNC(&t1, setPeriod, current_period);

			 ASYNC(&t1, enable, 0);
			 ASYNC(&t1, SQUARE, 0);
			
			SEND(self->noteLength - MSEC(50) ,0,self, SEQUENCE, 0);
			self->silence = true;
		  
		}
		else{		
			ASYNC( &t1, kill, 0); // 
			
			// SCI_WRITE(&sci0,"Silence state\n");
			
		  
		  if(self->seq_idx++ == sizeof(period_idx)/sizeof(int) - 1 ) // Increment position in melody. If reached end, wrap to 0. 
			 self->seq_idx = 0;

		self->silence = false;
		
		if(self->alive == true){
			self->ID = SEND(MSEC(50) ,0,self, SEQUENCE, 0);
		}else{
			self->terminated = true;
		}
	}
}


void receiver(App *self, int unused) {
    CANMsg msg;
    CAN_RECEIVE(&can0, &msg); // This is a MACRO
    SCI_WRITE(&sci0, "Can msg received: ");
    SCI_WRITE(&sci0, msg.buff);
	SCI_WRITE(&sci0, "\n");
	
	char * str; // to save incoming message sans first (function) char.
	int tempo;  // Local initial tempo, read from Sequencer obj. before SEQUENCE task initially spawns. 
	char snprintf_buff[50]; // destination for snprintf function.
	
	switch(msg.buff[0]){
		case('p'):
			SCI_WRITE(&sci0, "PLAY CANmsg received.\n");
			ASYNC(&s1, PLAY, 0);
		break;
		case('k'):
			SCI_WRITE(&sci0, "KEY CANmsg received.\n");
			str = (char*)msg.buff + 1; // I CAN'T BELIEVE THIS WORKED. WHHHHYYYY CAN'T I JUST INCREMENT msg.buff++ ???
			ASYNC(&s1, setKey, atoi(str) );
		break;
		case('t'):
			SCI_WRITE(&sci0, "TEMPO CANmsg received.\n");
			str = (char*)msg.buff + 1;
			ASYNC(&s1, setTempo, atoi(str) );
		break;
/* =========================================================================================================================================================================================================== */
/* ===========================================================================================================================================================================================================
 * CANON. Delay initial task execution by n beats.
 * Everything between lines is NEW and therefore UNTESTED.
 * =========================================================================================================================================================================================================== */
		case('c'):
			// CANON_beat_delay * tempo_in_MSEC
			SCI_WRITE(&sci0, "CANON CANmsg received.\n");
			 
			SCI_WRITE(&sci0, "PLAYing after ");
			snprintf(snprintf_buff, 50, "%u", msg.buff[nodeid]);  // %u denotes unsigned char, int etc...
			SCI_WRITE(&sci0, snprintf_buff);
			SCI_WRITE(&sci0, " beats.");
			
			tempo = SYNC(&s1, readTempo, 0); // Obtain initial tempo (must use SYNC method, as 'tempo' is state variable encapsulated by 'Sequencer' obj).
			
			SEND(msg.buff[nodeid] * MSEC( 1000*60 / (tempo)), 0, &s1, PLAY, 0);   // PLAY must be called in order to properly set 'alive' and 'terminated' bools. Do NOT call SEQUENCE directly!
		break;
		default:
		break;
	}
}

int readTempo(Sequencer *self, int unused){
	return self->tempo;
}
/* =========================================================================================================================================================================================================== */
/* =========================================================================================================================================================================================================== */
void CAN_SEND_KEY(App *self, int unused){
	int str_len = strlen(self->buff) + 1;
	
	char local_buff[100];
	// CANstruct * key_struct_pointer = (CANstruct*)key_struct_addr;
	snprintf(local_buff, 100, "%d", str_len);

/*
	SCI_WRITE(&sci0, "Key: ");
	SCI_WRITE(&sci0, self->buff);
	SCI_WRITE(&sci0, "\n");
	
	SCI_WRITE(&sci0, "Key str len: ");
	SCI_WRITE(&sci0, local_buff);
	SCI_WRITE(&sci0, "\n");
*/
	CANMsg msg;
	msg.msgId = MSGID;
    msg.nodeId = NODEID;
    msg.length = str_len;
    msg.buff[0] = 'k';
	
	for(int i = 1; i <= str_len; i++){
		msg.buff[i] = self->buff[i-1];
	}
	
	/*
	SCI_WRITE(&sci0, "CANmsg (key) ");
	SCI_WRITE(&sci0, msg.buff);
	SCI_WRITE(&sci0, "\n");
*/
    CAN_SEND(&can0, &msg);
}
	
void CAN_SEND_TEMPO(App *self, int unused){
	int str_len = strlen(self->buff) + 1;
	
	char local_buff[100];
	// CANstruct * key_struct_pointer = (CANstruct*)key_struct_addr;
	snprintf(local_buff, 100, "%d", str_len);

	CANMsg msg;
	msg.msgId = MSGID;
    msg.nodeId = NODEID;
    msg.length = str_len;
    msg.buff[0] = 't';
	
	for(int i = 1; i <= str_len; i++){
		msg.buff[i] = self->buff[i-1];
	}
	
    CAN_SEND(&can0, &msg);
}
	
void CAN_SEND_PLAY(App *self, int arg){
	CANMsg msg;
	
	msg.msgId = MSGID;
    msg.nodeId = NODEID;
    msg.length = 2;
    msg.buff[0] = 'p';
    msg.buff[1] = 0; // terminating null
    CAN_SEND(&can0, &msg);
}


/*void toggleDeadTone(ToneGen *self, int unused){
	self->deadenable = !(self->deadenable);
}*/


void reader(App *self, int c) {
	
	SCI_WRITE(&sci0, "Rcv: \'");
	SCI_WRITECHAR(&sci0, c);
	SCI_WRITE(&sci0, "\'\n");	
	
   if (self->count < 20-1  && c != 'k' // key input delim
                           && c != 't' // tempo input delim
                           && c != 'a' // Other functions 
                           && c != 'd'
                           && c != 'm')
	{
		self->buff[self->count] = c;
		self->count++;
	} 
	else if(c == 'k') // GOOD FOR NOW DONT TOUCH
	{
		self->buff[self->count] = '\0';
		self->count = 0;

		if(LEADER){ // Only leader should be able to directly call local methods.
			ASYNC(&s1, setKey, atoi(self->buff) ); // local 
			
			/* CANstruct cs;
			cs.str = self->buff;
			cs.len = strlen(self->buff);//  + 1; 
			*/ 
		}
		ASYNC(&app, CAN_SEND_KEY, 0 ); // For CAN transmissions, we want to ALWAYS send characters (ASCII). Max 8char budget
   }
   else if(c == 't')
   {
		self->buff[self->count] = '\0';
		self->count = 0;
		if(LEADER){
			ASYNC(&s1, setTempo, atoi(self->buff) );
		}
		ASYNC(&app, CAN_SEND_TEMPO, 0 );
   }
   
	  switch((char)c){
	  // Volume methods
	  case ('a'):
			ASYNC(&t1, decVol, 0);
			self->count = 0;
		break;
		case ('d'):
			ASYNC(&t1, incVol, 0);
			self->count = 0;
		break;
		case ('m'):
			ASYNC(&t1, muteVol, 0);
			self->count = 0;
		break;
		
		case('p'):
			if(LEADER){
				ASYNC(&s1, PLAY, 0);
				ASYNC(&app, CAN_SEND_PLAY, 0); // depends on CAN protocol
			}else{
				SCI_WRITE(&sci0, "Not leader!");
			}
			self->count = 0;
		break;  
		
		//// This is NEW and UNTESTED.
		case('c'):
			if(LEADER){
				ASYNC(&s1, PLAY, 0);
				ASYNC(self, CAN_SEND_CANON, 0);
			}else{
				SCI_WRITE(&sci0, "Not leader!");
			}
			self->count = 0;
		break;
		
		/*
		case('c'): 
			ASYNC(&app, CAN_SEND_PLAY, 0); // depends on CAN protocol
			self->count = 0;
		break; 
		*/ 
		
		case('l'):
			ASYNC(&app, toggleLeader, 0);
			self->count = 0;
		break;
		//============================//
//		case ('l'): ASYNC(&t1, toggleDeadTone, 0);  break;
		
/* 		case('7'): ASYNC(&s1, setTempo, INCREASE); break;
		case('4'): ASYNC(&s1, setTempo, DECREASE); break;
		
		case('9'): ASYNC(&s1, setKey, INCREASE); break;
		case('6'): ASYNC(&s1, setKey, DECREASE); break; */
		
		
		default:
		break;
	  }
	  
}
/* =========================================================================================================================================================================================================== */
/* =========================================================================================================================================================================================================== */
// Fixed message length of 3. (1 leader, 2 slaves). This may be extended arbitrarily, so long as total len <= 8.
void CAN_SEND_CANON(App *self, int arg){
	CANMsg msg;
	
	msg.msgId = MSGID;
    msg.nodeId = NODEID;
    msg.length = 3;
    msg.buff[0] = 'c'; 	// CANON
	msg.buff[1] = CANON_BEAT_OFFSET;        // Now THIS is hardcoding!
	msg.buff[2] = CANON_BEAT_OFFSET * 2;
    // msg.buff[1] = 0; 	// terminating null
    CAN_SEND(&can0, &msg);
}
/* =========================================================================================================================================================================================================== */
/* =========================================================================================================================================================================================================== */

void startApp(App *self, int arg) {
	
	// ASYNC(&s1, SEQUENCE, 0);
	
    CANMsg msg;

    CAN_INIT(&can0);
    SCI_INIT(&sci0);
    SCI_WRITE(&sci0, "Hello, hello...\n");

    msg.msgId = 1;
    msg.nodeId = 7;
    msg.length = 6;
    msg.buff[0] = 'H';
    msg.buff[1] = 'e';
    msg.buff[2] = 'l';
    msg.buff[3] = 'l';
    msg.buff[4] = 'o';
    msg.buff[5] = 0;
    CAN_SEND(&can0, &msg);
	
}

int main() {
    INSTALL(&sci0, sci_interrupt, SCI_IRQ0);
	INSTALL(&can0, can_interrupt, CAN_IRQ0);
    TINYTIMBER(&app, startApp, 0);
    return 0;
}
