/*
 * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
 *
 * Use is subject to the GNU Lesser General Public License, Version 2.1,
 * February 1999, which is contained in the read-me file named
 * "README_GNU_LGPL." This library is free software; you can
 * redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2.1 of the License, or (at your
 * option) any later version. This library 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 Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA  02111-1307  USA.
 *
 * display utilities for the panel utils 
 *
 * NOTE: as we want to make sure that all of the lcd utils know
 *       about each other, this is almost intentionally 
 *       single-threaded.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include "lcdutils.h"
#include "lcd_private.h"

#define INVALIDUSECS 300000 /* .3 seconds */
#define SPINUSECS    500000 /* .5 secs */

#ifndef MIN
#define MIN(a, b)  ((a) < (b) ? (a) : (b))
#endif

void lcd_set(void *lcd_private, const int value) {
	struct lcd_private *lcd = lcd_private;
	
	if (!lcd || lcd->fd < 0)
		return;
	
	ioctl(lcd->fd, value);
}

/* this depends upon the lcd driver not changing bits that
 * it doesn't use. */
void lcd_blink_invalid(void *lcd_private) {
	struct lcd_private *save = lcd_private;
	struct lcd_display lcd, lcdblank;
	struct timeval tv, savetv;
	
	if (!save || save->fd < 0)
		return;
	
	ioctl(save->fd, LCD_Read, &lcd);
	
	/* initialize the blinky bits */
	memcpy(&lcdblank, &lcd, sizeof(lcd));
	memset(lcdblank.line2, ' ', sizeof(lcdblank.line2));
	lcd.size1 = lcd.size2 = 16;
	lcdblank.size1 = lcdblank.size2 = 16;
	
	ioctl(save->fd, BUTTON_Read, &save->display);
	save->display.buttons = save->display.buttons & 0x00FF;
	
	memset(&savetv, 0, sizeof(savetv));
	savetv.tv_usec = INVALIDUSECS;
	while ((save->display.buttons == BUTTON_NONE) || (save->display.buttons == BUTTON_NONE_B))  {
		ioctl(save->fd, LCD_Write, &lcdblank); 
		memcpy(&tv, &savetv, sizeof(tv));
		select(1, NULL, NULL, NULL, &tv);
		
		ioctl(save->fd, LCD_Write, &lcd);
		memcpy(&tv, &savetv, sizeof(tv));
		select(1, NULL, NULL, NULL, &tv);
		
		ioctl(save->fd, BUTTON_Read, &save->display);
		save->display.buttons = save->display.buttons & 0x00FF;
	}
	ioctl(save->fd, LCD_Write, &lcd);
}

/* blinky asterisk */
void lcd_spin(void *lcd, const char *line1, const char *line2) {
	char star[16];
	struct timeval tv, save;
	
	memset(star, ' ', sizeof(star));
	if (line2) 
		memcpy(star, line2, MIN(strlen(line2), sizeof(star)));
	
	memset(&save, 0, sizeof(save));
	save.tv_usec = SPINUSECS;
	while (1) {
		star[15] = '*';
		lcd_write(lcd, line1, star);
		memcpy(&tv, &save, sizeof(tv));
		select(1, NULL, NULL, NULL, &tv);
		
		star[15] = ' ';
		lcd_write(lcd, line1, star);
		memcpy(&tv, &save, sizeof(tv));
		select(1, NULL, NULL, NULL, &tv);
	}
}

void *lcd_open(const int flags) {
	struct lcd_private *lcd;
	int lcdtype = 0;
	
	if ((lcd = calloc(1, sizeof(struct lcd_private))) == NULL)
		return NULL;
	
	if ((lcd->fd = open(DEVLCD, flags)) < 0) {
		free(lcd);
		return NULL;
	}
	
	lcd->button_debounce = PARALLEL_BUTTON_DEBOUNCE;
	lcd->button_sense = PARALLEL_BUTTON_SENSE;
	if ((ioctl(lcd->fd, LCD_Type, &lcdtype) == 0) && (lcdtype == LCD_TYPE_I2C)) {
		lcd->button_debounce = I2C_BUTTON_DEBOUNCE;
		lcd->button_sense = I2C_BUTTON_SENSE;
	}
	return lcd;
}

void lcd_close(void *lcd_private) {
	struct lcd_private *lcd = lcd_private;
	
	if (!lcd)
		return;
	
	if (lcd->fd < 0)
		close(lcd->fd);
	
	free(lcd);
}

void lcd_reset(void *lcd_private) {
	struct lcd_private *lcd = lcd_private;
	
	if (!lcd || (lcd->fd < 0))
		return;
	
	ioctl(lcd->fd,LCD_Reset);
	ioctl(lcd->fd,LCD_Clear);
	ioctl(lcd->fd,LCD_On);	
	ioctl(lcd->fd,LCD_Cursor_Off);
}

int lcd_write(void *lcd_private, const char *line1, const char *line2) {
	struct lcd_private *lcd = lcd_private;
	
	if (!lcd || (lcd->fd < 0))
		return -1;
	
	if (line1) {
		lcd->display.size1 = MIN(strlen(line1), sizeof(lcd->display.line1));
		memcpy(lcd->display.line1, line1, lcd->display.size1);
	}
	
	if (line2) {
		lcd->display.size2 = MIN(strlen(line2), sizeof(lcd->display.line2));
		memcpy(lcd->display.line2, line2, lcd->display.size2);
	}
	
	ioctl(lcd->fd, LCD_Write, &lcd->display);
	return 0;
}

int lcd_setleds(void *lcd_private, const int pattern) {
	struct lcd_private *lcd = lcd_private;
	
	if (ioctl(lcd->fd, LED32_Set, pattern) == 0)
		return 0;
	
	if (!lcd || (lcd->fd < 0) || ((pattern & 0x0F) == 0x0F))
		return -1;
	
	lcd->display.leds = pattern;
	ioctl(lcd->fd,LED_Set,&lcd->display);
	return 0;
}

int lcd_led_set(void *lcd_private, const unsigned int pattern) {
	struct lcd_private *lcd = lcd_private;
	
	if (ioctl(lcd->fd, LED32_Bit_Set, pattern) == 0)
		return 0;
	
	if (!lcd || (lcd->fd < 0) || ((pattern & 0x0F) == 0x0F))
		return -1;
	
	lcd->display.leds = pattern;
	ioctl(lcd->fd,LED_Set,&lcd->display);
	return 0;
}

int lcd_led_clear(void *lcd_private, const unsigned int pattern) {
	struct lcd_private *lcd = lcd_private;
	
	if (ioctl(lcd->fd, LED32_Bit_Clear, pattern) == 0)
		return 0;
	
	if (!lcd || (lcd->fd < 0) || ((pattern & 0x0F) == 0x0F))
		return -1;
	
	lcd->display.leds = pattern;
	ioctl(lcd->fd,LED_Set,&lcd->display);
	return 0;
}
