// ----------------------------------------------------------------------------------------
//
//  packages/s3c2410a/cfax_iic/cfax_iic.cpp
//    
//  Bob Cook Development, Robotics Library
//  http://www.bobcookdev.com/rl/
//    
//  TinCan Tools Hammer support for communicating with a CFAX LCD over I2C
//
//  Copyright (C) 2009 Bob Cook
//
//      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 2 of the License, or (at your
//      option) any later version.
//    
//      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, write to the Free Software Foundation, Inc., 
//      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// ----------------------------------------------------------------------------------------


#include "cfax_iic.h"

#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "protocol.h"

#include "project_defs.h"


// #include <stdio.h>


// ----------------------------------------------------------------------------------------
//  Project-specific definitions
//

#if !defined( PRJ_CFAX_IIC_SLAVE_ADDRESS )
#error PRJ_CFAX_IIC_SLAVE_ADDRESS must be defined!
#endif


// ----------------------------------------------------------------------------------------

const useconds_t CFAXIIC_POST_WRITE_DELAY_US = 3000;

static int cfaxiic_device_fd = -1;


// ----------------------------------------------------------------------------------------

static bool open_i2c_device()
{
    if ( cfaxiic_device_fd != -1 ) return true;

    if ( ( cfaxiic_device_fd = open( "/dev/i2c-0", O_RDWR ) ) < 0 )
    {
        // TODO: log error...
        return false;
    }

    if ( ioctl( cfaxiic_device_fd, I2C_SLAVE, PRJ_CFAX_IIC_SLAVE_ADDRESS ) < 0 )
    {
        // TODO: log error...
        return false;
    }

    unsigned long i2cfuncs = 0;

    if ( ioctl( cfaxiic_device_fd, I2C_FUNCS, &i2cfuncs ) < 0 )
    {
        // TODO: log error...
        return false;
    }

    if ( ( i2cfuncs & I2C_FUNC_I2C ) == 0 )
    {
        // TODO: log error...
        return false;
    }

    return true;
}


// ----------------------------------------------------------------------------------------

void cfaxiic_clear_screen()
{
    if ( ! open_i2c_device() ) return;

    cfaxiic_cmd_t command;
    command.command = CFAXIIC_CMD_CLS;

    if ( write( cfaxiic_device_fd, &command, sizeof( command.command ) ) < 0 )
    {
        // TODO: log error...
        return;
    }

    usleep( CFAXIIC_POST_WRITE_DELAY_US );
}


// ----------------------------------------------------------------------------------------

void cfaxiic_write( char c, uint8_t x, uint8_t y )
{
    if ( ! open_i2c_device() ) return;

    cfaxiic_cmd_t command;
    command.command = CFAXIIC_CMD_TEXT;
    command.data.text.text[ 0 ] = c;
    command.data.text.text[ 1 ] = '\0';
    command.data.text.x = x;
    command.data.text.y = y;

    size_t length = sizeof( command.command ) + sizeof( command.data.text );

    if ( write( cfaxiic_device_fd, &command, length ) < 0 )
    {
        // TODO: log error...
        return;
    }

    usleep( CFAXIIC_POST_WRITE_DELAY_US );
}


// ----------------------------------------------------------------------------------------

void cfaxiic_write( const char* str, uint8_t x, uint8_t y )
{
    if ( ! open_i2c_device() ) return;

    cfaxiic_cmd_t command;
    command.command = CFAXIIC_CMD_TEXT;
    strncpy( command.data.text.text, str, CFAXIIC_TEXT_BUF_MAX );
    command.data.text.text[ CFAXIIC_TEXT_BUF_MAX ] = '\0';
    command.data.text.x = x;
    command.data.text.y = y;

    size_t length = sizeof( command.command ) + sizeof( command.data.text );

    if ( write( cfaxiic_device_fd, &command, length ) < 0 )
    {
        // TODO: log error...
        return;
    }

    usleep( CFAXIIC_POST_WRITE_DELAY_US );
}


// ----------------------------------------------------------------------------------------

void cfaxiic_line( uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, bool state )
{
    if ( ! open_i2c_device() ) return;

    cfaxiic_cmd_t command;
    command.command = CFAXIIC_CMD_LINE;
    command.data.line.x0 = x0;
    command.data.line.y0 = y0;
    command.data.line.x1 = x1;
    command.data.line.y1 = y1;
    command.data.line.state = state;

    size_t length = sizeof( command.command ) + sizeof( command.data.line );

    if ( write( cfaxiic_device_fd, &command, length ) < 0 )
    {
        // TODO: log error...
        return;
    }

    usleep( CFAXIIC_POST_WRITE_DELAY_US );
}


// ----------------------------------------------------------------------------------------

void cfaxiic_rect( uint8_t x, uint8_t y, uint8_t w, uint8_t h )
{
    if ( ! open_i2c_device() ) return;

    cfaxiic_cmd_t command;
    command.command = CFAXIIC_CMD_RECT;
    command.data.rect.x = x;
    command.data.rect.y = y;
    command.data.rect.w = w;
    command.data.rect.h = h;

    size_t length = sizeof( command.command ) + sizeof( command.data.rect );

    if ( write( cfaxiic_device_fd, &command, length ) < 0 )
    {
        // TODO: log error...
        return;
    }

    usleep( CFAXIIC_POST_WRITE_DELAY_US );
}


// ----------------------------------------------------------------------------------------

void cfaxiic_fill( uint8_t x, uint8_t y, uint8_t w, uint8_t h )
{
    if ( ! open_i2c_device() ) return;

    cfaxiic_cmd_t command;
    command.command = CFAXIIC_CMD_FILL;
    command.data.rect.x = x;
    command.data.rect.y = y;
    command.data.rect.w = w;
    command.data.rect.h = h;

    size_t length = sizeof( command.command ) + sizeof( command.data.rect );

    if ( write( cfaxiic_device_fd, &command, length ) < 0 )
    {
        // TODO: log error...
        return;
    }

    usleep( CFAXIIC_POST_WRITE_DELAY_US );
}


// ----------------------------------------------------------------------------------------

void cfaxiic_erase( uint8_t x, uint8_t y, uint8_t w, uint8_t h )
{
    if ( ! open_i2c_device() ) return;

    cfaxiic_cmd_t command;
    command.command = CFAXIIC_CMD_ERASE;
    command.data.rect.x = x;
    command.data.rect.y = y;
    command.data.rect.w = w;
    command.data.rect.h = h;

    size_t length = sizeof( command.command ) + sizeof( command.data.rect );

    if ( write( cfaxiic_device_fd, &command, length ) < 0 )
    {
        // TODO: log error...
        return;
    }

    usleep( CFAXIIC_POST_WRITE_DELAY_US );
}

// ----------------------------------------------------------------------------------------

bool cfaxiic_touchscreen( uint8_t* x, uint8_t* y )
{
    if ( ! open_i2c_device() ) return false;

    cfaxiic_cmd_t command;
    command.command = CFAXIIC_CMD_TOUCH;

    if ( write( cfaxiic_device_fd, &command, sizeof( command.command ) ) < 0 )
    {
        // TODO: log error...
        return false;
    }

    usleep( CFAXIIC_POST_WRITE_DELAY_US );

    if ( read( cfaxiic_device_fd, &command, sizeof( command ) ) < 0 )
    {
        // TODO: log error...
        return false;
    }

    if ( command.command != CFAXIIC_RESPONSE_TOUCH )
    {
        // TODO: log error...
        return false;
    }

    if ( command.data.touch.state )
    {
        *x = command.data.touch.x;
        *y = command.data.touch.y;
        return true;
    }

    return false;
}


// ----------------------------------------------------------------------------------------
//  end of cfax_iic.cpp
// ----------------------------------------------------------------------------------------
