/*********************************************************************
 *
 *  Sample Vending Machine
 *
 *********************************************************************
 * FileName:        VendingMachine.c
 * Dependencies:    VendingMachine.h
 * Processor:       PIC18, PIC24F, PIC24H, dsPIC30F, dsPIC33F, PIC32
 * Compiler:        Microchip C32 v1.02 or higher
 *					Microchip C30 v3.10 or higher
 *					Microchip C18 v3.20 or higher
 * Company:         Microchip Technology, Inc.
 *
 * Software License Agreement
 *
 * Copyright (C) 2002-2008 Microchip Technology Inc.  All rights 
 * reserved.
 *
 * Microchip licenses to you the right to use, modify, copy, and 
 * distribute: 
 * (i)  the Software when embedded on a Microchip microcontroller or 
 *      digital signal controller product ("Device") which is 
 *      integrated into Licensee's product; or
 * (ii) ONLY the Software driver source files ENC28J60.c and 
 *      ENC28J60.h ported to a non-Microchip device used in 
 *      conjunction with a Microchip ethernet controller for the 
 *      sole purpose of interfacing with the ethernet controller. 
 *
 * You should refer to the license agreement accompanying this 
 * Software for additional information regarding your rights and 
 * obligations.
 *
 * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT 
 * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT 
 * LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A 
 * PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL 
 * MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR 
 * CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF 
 * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS 
 * BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE 
 * THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER 
 * SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT 
 * (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE.
 *
 *
 * Author              Date         Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Elliott Wood			02/12/08	Original
 * Elliott Wood			07/09/08	Updated for COM4201 1.10
 ********************************************************************/

#include "VendingMachine.h"

// Local Function Prototypes
static void WriteLCDMenu(void);
static void WritePriceLCD(BYTE price, BYTE position);

/****************************************************************************
  Section:
	Vending Machine Application Global Variables
  ***************************************************************************/

VEND_ITEM	Products[MAX_PRODUCTS];		// All items in the machine
BYTE		machineDesc[33];			// Machine description string
BYTE 		curItem;					// Current product being displayed
BYTE 		curCredit;					// Current deposit credit, as number of $0.25 quarters

static enum
{
	SM_IDLE = 0u,				// No events are pending
	SM_DEBOUNCE_DOWN,			// Button press detected...verify the event
	SM_ADD_COIN,				// A "coin" was "deposited"
	SM_TRY_VEND,				// Vend was requested
	SM_PREV,					// Go back one product
	SM_NEXT,					// Go forward one product
	SM_DISPLAY_WAIT,			// Waiting for a display message timeout
	SM_SHOW_MENU,				// Restore the menu to the screen
	SM_RELEASE_WAIT,			// Waiting for button to be released
	SM_DEBOUNCE_UP				// Verify release of button
} smVend = SM_DISPLAY_WAIT;		// Application state machine



/****************************************************************************
  Section:
	Vending Machine Implementation
  ***************************************************************************/
void VendingMachine(void)
{
	static TICK displayTimeout = 2 * TICK_SECOND;

	// Vending Machine State Machine
	switch(smVend)
	{
		case SM_IDLE:
			// Wait for a button press
			if(BUTTON0_IO == 0 || BUTTON1_IO == 0 || BUTTON2_IO == 0 || BUTTON3_IO == 0)
				smVend = SM_DEBOUNCE_DOWN;
			DelayMs(1);
			break;

		case SM_DEBOUNCE_DOWN:
			
			// Check if a button is down
			DelayMs(1);
			if(BUTTON3_IO == 0)
				smVend = SM_ADD_COIN;
			else if(BUTTON2_IO == 0)
				smVend = SM_TRY_VEND;
			else if(BUTTON1_IO == 0)
				smVend = SM_PREV;
			else if(BUTTON0_IO == 0)
				smVend = SM_NEXT;
			else
				smVend = SM_IDLE;
			break;
			
		case SM_ADD_COIN:
			// Add a quarter, up to $5.00
			if(curCredit < 20)
			{// Increase the credit
				curCredit++;
				smVend = SM_SHOW_MENU;
			}
			else
			{// Max credit was reached
				strcpypgm2ram((char*)LCDText, (ROM char*)" Coin Returned! Max credit is $5");
				LCDUpdate();
				displayTimeout = TickGet() + 4 * TICK_SECOND;
				smVend = SM_DISPLAY_WAIT;
			}
			break;
		
		case SM_TRY_VEND:
			// Try to vend a product
			if(Products[curItem].stock == 0)
			{// Product is sold out
				strcpypgm2ram((char*)LCDText, (ROM char*)"    SOLD OUT                    ");
			}
			else if(Products[curItem].price > curCredit)
			{
				strcpypgm2ram((char*)LCDText, (ROM char*)"Price:          Credit:         ");
				WritePriceLCD(Products[curItem].price, 8);
				WritePriceLCD(curCredit, 24);
			}
			else
			{
				strcpypgm2ram((char*)LCDText, (ROM char*)"   vending...                   ");
				curCredit -= Products[curItem].price;
				Products[curItem].stock--;
			}
			LCDUpdate();
			displayTimeout = TickGet() + 2 * TICK_SECOND;
			smVend = SM_DISPLAY_WAIT;
			break;
		
		case SM_PREV:
			// Move back one product in the list
			if(curItem > 0)
				curItem--;
			smVend = SM_SHOW_MENU;
			break;
		
		case SM_NEXT:
			// Move forward one product in the list
			if(curItem < MAX_PRODUCTS-1)
				curItem++;
			smVend = SM_SHOW_MENU;
			break;
		
		case SM_DISPLAY_WAIT:
			// Wait for the timout to occur before continuing
			//for(displayTimeout *= 1000; displayTimeout > 0; displayTimeout--)
			//	DelayMs(1);
			if((LONG)(TickGet() - displayTimeout) > (LONG)0)
				smVend = SM_SHOW_MENU;
			break;
			
		case SM_SHOW_MENU:				
			// Update the LCD with the menu
			WriteLCDMenu();
			smVend = SM_RELEASE_WAIT;
			break;
					
		case SM_RELEASE_WAIT:
			// Wait for all buttons to be released
			if(BUTTON0_IO == 1 && BUTTON1_IO == 1 && BUTTON2_IO == 1 && BUTTON3_IO == 1)
				smVend = SM_DEBOUNCE_UP;
			DelayMs(1);
			break;
		
		case SM_DEBOUNCE_UP:
			// Make sure all buttons were released
			DelayMs(1);
			if(BUTTON0_IO == 1 && BUTTON1_IO == 1 && BUTTON2_IO == 1 && BUTTON3_IO == 1)
				smVend = SM_IDLE;
			else
				smVend = SM_RELEASE_WAIT;
			break;
	}
}


/****************************************************************************
  Section:
	Vending Machine Initialization
  ***************************************************************************/
void InitializeVend(void)
{
	// Vending machine specific defaults
	strcpypgm2ram((char*)Products[0].name, (ROM char*)"Cola");
	strcpypgm2ram((char*)Products[1].name, (ROM char*)"Diet Cola");
	strcpypgm2ram((char*)Products[2].name, (ROM char*)"Root Beer");
	strcpypgm2ram((char*)Products[3].name, (ROM char*)"Orange");
	strcpypgm2ram((char*)Products[4].name, (ROM char*)"Lemonade");
	strcpypgm2ram((char*)Products[5].name, (ROM char*)"Iced Tea");
	strcpypgm2ram((char*)Products[6].name, (ROM char*)"Water");
	Products[0].price = 4;
	Products[1].price = 4;
	Products[2].price = 4;
	Products[3].price = 4;
	Products[4].price = 5;
	Products[5].price = 7;
	Products[6].price = 8;
	Products[0].stock = 15;
	Products[1].stock = 9;
	Products[2].stock = 22;
	Products[3].stock = 18;
	Products[4].stock = 4;
	Products[5].stock = 29;
	Products[6].stock = 14;

	strcpypgm2ram((char*)machineDesc, (ROM char*)"Building C4 - 2nd Floor NW");
	machineDesc[32] = '\0';
	curItem = 0;
	curCredit = 0;
	
	// Display the boot message on the LCD
	strcpypgm2ram((char*)LCDText, "Microchip       Vending Machine");
	LCDUpdate();
}


/****************************************************************************
  Section:
	Vending Machine LCD Functions
  ***************************************************************************/
static void WriteLCDMenu(void)
{// Update the LCD screen
	
	// Blank the LCD display
	LCDErase();
	
	// Show the name
	strcpy((char*)LCDText, (char*)Products[curItem].name);
	LCDText[strlen((char*)Products[curItem].name)] = ' ';
	
	// Show the price, or sold out status
	if(Products[curItem].stock == 0)
		memcpypgm2ram(&LCDText[12], (ROM void*)"SOLD", 4);
	else
		WritePriceLCD(Products[curItem].price, 11);
	
	// Show the current credit
	WritePriceLCD(curCredit, 16);
	
	// Show the vend button if available
	if(Products[curItem].stock != 0 && Products[curItem].price <= curCredit)
		memcpypgm2ram(&LCDText[22], (ROM void*)"Vend", 4);
	else
		memcpypgm2ram(&LCDText[23], (ROM void*)"--", 2);
	
	// Show the rest of the buttons
	if(curItem != 0)
		memcpypgm2ram(&LCDText[27], (ROM void*)"<<", 2);
	if(curItem != MAX_PRODUCTS-1)
		memcpypgm2ram(&LCDText[30], (ROM void*)">>", 2);

	// Update to the screen	
	LCDUpdate();
}

static void WritePriceLCD(BYTE price, BYTE position)
{
	LCDText[position++] = '$';
	
	// Write the dollars
	LCDText[position++] = (price >> 2) + '0';
	
	LCDText[position++] = '.';
	
	// Write the cents
	switch(price & 0x03)
	{
		case 0:
			LCDText[position++] = '0';
			LCDText[position]   = '0';
			break;
		case 1:
			LCDText[position++] = '2';
			LCDText[position]   = '5';
			break;
		case 2:
			LCDText[position++] = '5';
			LCDText[position]   = '0';
			break;
		case 3:
			LCDText[position++] = '7';
			LCDText[position]   = '5';
			break;
	}
}

