#include<sys/ioctl.h>
#include<fcntl.h>
#include<stdio.h>
#include<string.h>
#include <linux/parport.h>
#include <linux/ppdev.h>
#include <sys/types.h>
#include <sys/stat.h>



ssize_t write_printer (int fd, const void *ptr, size_t count){
    return write (fd, ptr, count);
}

int read_status(  int fd  ) {
    printf( "in read with fd !! \n");
    char buff[ 10 ];
    char *ptr = buff;
    printf( "Reading actually \n ");
    int read_1;
    read_1 = read( fd, ptr, 10 );
    printf( "read is %d \n", read_1 );
    if ( read_1 < 0 ){
        return -1; 
    }
    buff[read_1] = '\0';
    if ( buff[2] =='P' ){
        printf( "status is ok\n" );
        return 1;
    }else{
        printf( "status is not ok\n" );
        return 0;
    }
    
    printf( "read is %s %d \n" , buff, read_1 );
}



int send_command( char c, int fd ) {
    char buff[ 3 ];
    buff[ 0] = 0x1B;
    buff[1] = c;
    buff[2] = '\0';
    char* ptr = buff;
    int written = write_printer (fd, ptr, 2 );
    if (written < 0) {
        perror ("write");
        close (fd);
        return 1;
    }
}
int send_command_str( char* c, int fd ) {
    int len = strlen( c );
    char buff[ len + 2 ];
    buff[ 0] = 0x1B;
    char * ptr = c;
    int i = 1;
    while ( *ptr != '\0' ){
        buff[i] = *ptr;
        ptr ++;
        i++;
    }
    ptr = buff;
    int written; 
    buff[ i ] = '\0';
    printf ( "command is %s\n" , buff );
    int j; 
    written = write_printer (fd, ptr, len + 1 );
    printf ( "written is %d %d \n" , written, (len +1 ) );
    if (written < 0) {
        perror ("write");
        close (fd);
        return 1;
    }
}

int init_printer( int fd ) {
     send_command( 'l', fd );
     send_command( 'j', fd );
     sleep_a_while();
     if ( !read_status( fd ) ){
         printf( "Erro in reading status !!\n");
         return 1;
     }

     // select olivetty emulation
     send_command( 'n', fd );
     // select printer device
     send_command_str( "S5", fd );
     // select primaty id 
     send_command( 'Z', fd );

    // read_status( fd );
    send_command( 'j', fd );
    read_status( fd );

    // send_command( 'O', fd );
     send_command_str( "_" , fd);
     send_command_str( "UC", fd );
     // select 9 pin graphics
     send_command_str( "!G0", fd );
     //beep(); 
     
     // set document type
     send_command_str( "\'@", fd );
     send_command_str( "#0", fd ); 
     // set vertical spacing
     send_command_str( "&00", fd );
     // set print pitch 
     send_command_str( "a1", fd ); 
     // set document length 
     send_command_str( "Q140", fd );
     send_command( 'Z', fd );
     // set document length 
     send_command_str( "T043", fd );
     // set line feed
     send_command_str( "J000", fd );
     // clear all tab stops
     send_command_str( "R000", fd );
     send_command( 'j', fd );
     sleep_a_while();
     if ( !read_status( fd ) ){
         printf( "Readin gstatus failed !!\n");
         return 1;
     }
}

int eject_paper( int fd ) {
    send_command( '0', fd );
}


int beep( int fd ){
    char buff[ 2 ];
    buff[ 0] = 0x07;
    buff[1] = '\0';
    char* ptr = buff;
    int written = write_printer (fd, ptr, 1 );
    if (written < 0) {
        perror ("write");
        close (fd);
        return 1;
    }
}

int sleep_a_while( ) {
    struct timespec ts;
    ts.tv_sec = 0;
    ts.tv_nsec = 1000;
    nanosleep (&ts, NULL);
}

int print_string( char* str, int fd  ) {
    int len = strlen( str );
    int written = write_printer (fd, str, len );
    if (written < 0) {
        perror ("write");
        close (fd);
        return 1;
    }
    char buff[2];
    buff[0] = 0x0D;
    buff[1] = 0x0A;
    char *ptr = buff;
    written = write_printer (fd, ptr, 2 );
    if (written < 0) {
        perror ("write");
        close (fd);
        return 1;
    }
}

paper_inserted( int fd ){
    char buff[10]; 
    char *ptr = buff;
    int read_1;
    printf( "In paper inserted !!\n");
    read_1 = read( fd, ptr, 10 );
    if ( read_1 < 0 ){
        printf( "pinst returning 0 ");
        return 0; 
    }
    printf( "In paper inserted after %d !!\n", read_1);
    int i = 0;
    for ( i=0; i< read_1; i++ ){
        if ( buff[i] == '%'){
            printf ( "pinst returning 1 ");
            return 1;
        }
    }
    printf( "In paper inserted returning 0 !!\n");
    return 0;
}

int test_printing( int fd ){
    send_command( '.', fd );
    send_command_str( "UQ", fd );
    send_command_str( "Uq", fd );
    printf( "please put the paper in printer \n");
    while ( !paper_inserted( fd ) ) {
        printf( "please put the paper in printer \n");
        scanf("" );
    }
    send_command( '_', fd );
    send_command_str( "UC", fd );
    send_command_str( "L000", fd );
    print_string( "Anil", fd );
    print_string( "Habib Bank A G Zurich, Dubai, United Arab Emirates", fd );
    print_string( "Olivetty Printer Testing for the last few days... nothing works...", fd );
}


int drive_printer (const char *name){
    int fd;
    int mode; 
    struct timeval tv;
    tv.tv_sec = 2000;
    tv.tv_usec = 2000;
    
    fd = open(name,O_RDWR | O_SYNC  );
    //fd = open (name, O_RDONLY | O_NOCTTY);
    if (fd == -1) {
        perror ("open");
        return 1;
    }
    if (ioctl (fd, PPCLAIM )) {
        perror ("PPCLAIM");
        close (fd);
        return 1;
    }
    /*
    if (ioctl (fd, PPEXCL )) {
        perror ("PPEXCEL");
        close (fd);
        return 1;
    }*/
    //mode = IEEE1284_MODE_COMPAT;
    mode = IEEE1284_MODE_ECP; 
    printf( "mode befor got is %d \n", mode );
    if (ioctl (fd, PPNEGOT, &mode)) { 
        perror ("PPNEGOT");
        close (fd);
        return 1;
    }
    printf( "mode got is %d \n", mode );
    
    
    int timeout;
    /*int timeout; 
    
    if (ioctl (fd, PPGETTIME, &timeout)) { 
        perror ("PPGETTIME");
        close (fd);
        return 1;
    }*/
    
    printf( "timeout got befor is %d \n", timeout );
    timeout = 10000;
    
    if (ioctl (fd, PPSETTIME, &tv)) { 
        perror ("PPSETTIME");
        close (fd);
        return 1;
    }
    
    printf( "timeout got is %d \n", timeout );
    
    /*if (ioctl (fd, PPGETTIME, &timeout)) { 
        perror ("PPGETTIME");
        close (fd);
        return 1;
    }*/
    
    printf( "timeout got after is %d \n", timeout );
    
    //send_command( 'l', fd );
    
    //send_command( '0', fd );
    //nanosleep (&ts, NULL);
    //send_command( 'j', fd );
    //sleep_a_while();
    //read_status( fd );
    //sleep_a_while();
    //read_status( fd );
    init_printer( fd );
    //test_printing( fd );
    test_scan( fd );
    //eject_paper(fd);
    
    printf( "I am here %d \n ", strlen("hello world") );
    close( fd );
    printf( "I am here 2 \n" );
    sleep( 1 );
}

int read_data_image( int fd, int block ) {
    char buff[18]; 
    char *ptr = buff;
    int read_1;
    int j = 0;
    read_1 = read( fd, ptr, 18 );
    int i = 0;
    /*sleep_a_while();
    sleep_a_while();
    sleep_a_while();
    sleep_a_while();
    sleep_a_while();
    sleep_a_while();
    sleep_a_while();
    sleep_a_while(); */
    sleep( 1 );
    /*
    while ( j++ < 2  ) {
        read_1 = read( fd, ptr, 4 );
        if ( read_1 < 0 ){
            printf( "pinst returning 0 ");
            return 0; 
        }
       // printf( "data got %d \n", read_1 );
        for ( i=0; i< read_1; i++ ){
            printf( "%d," , buff[i] );
        }
        printf( "\n" );
    }
    printf( "out of loop \n"); 
    read_1 = read( fd, ptr, 6 ); */
    printf( "out of loop %d \n", read_1);
    for ( i=0; i< read_1; i++ ){
        printf( "%d," , buff[i] );
    }
    return 0;
}
int read_data( int fd, int block ) {
    char buff[10]; 
    char *ptr = buff;
    int read_1;
    int j = 0;
    while ( 1  ) {
        read_1 = read( fd, ptr, 10 );
        if ( read_1 < 0 ){
            printf( "pinst returning 0 ");
            return 0; 
        }
        if ( !block ){
            break;
        }
    }
    return 0;
}

int read_image_1( int fd ){
    int SIZE_1 = 256, SIZE_2 = 256, SIZE_3 = 32;
    int frontImage = open( "front.pnm",O_WRONLY | O_SYNC | O_CREAT | O_TRUNC, S_IRWXU );
    char buff[SIZE_1  ]; 
    char *ptr = buff;
    char buff1[5]; 
    char *ptr1 = buff1;
    int read_1, written;
    read_1 = read( fd, ptr, 2 );
    if ( buff[ 0 ] != 0x02 || buff[ 1] != 0x61 ){
        printf( "Error %x, %x \n", buff[0], buff[1]);
        return 0;
    }
    int j = 0, i = 0, total_written = 0;
    for ( j = 0; j < SIZE_2; j++ ){
        read_1 = read( fd, ptr, SIZE_1 );
        written = write( frontImage, ptr, read_1 );
        if (written < 0) {
            perror ("write");
            printf( "error in 1 ");
            return 1;
        } 
        printf( "written %d %d \n", written, j );
        ptr += SIZE_1; 
    }
    /*read_1 = read( fd, ptr, 275 );
    written = write( frontImage, ptr, read_1 );
    if (written < 0) {
        perror ("write");
        printf( "error in 1 ");
        return 1;
    }*/
    read_1 = read( fd, ptr, 6 );
    for ( i=0; i< read_1; i++ ){
        printf( "Read in end %x, %d \n", buff[i], read_1 );
    }
    close( frontImage );
}

int read_image( int fd ) {
    int frontImage = open( "front.pnm",O_WRONLY | O_SYNC | O_CREAT | O_TRUNC );
    int rearImage = open( "rear.pnm",O_WRONLY | O_SYNC | O_CREAT | O_TRUNC);
    char buff[1]; 
    char *ptr = buff;
    char buff1[5]; 
    char *ptr1 = buff1;
    int read_1, written;
    int startWritingFront = 0, startWritingRear = 0; 
    while ( 1 ) {
        read_1 = read( fd, ptr, 1 );
        if ( buff[0] == 0x02 ){
            printf( "got a <STX> \n");
        }
        if ( buff[0] == 0x03 ){
            printf( "got a <ETX> \n");
        }
        if ( buff[0] == 0x02 && !startWritingFront ){
            printf( "found startx in 1 \n " );
            read_1 = read( fd, ptr, 1 ); // skip Q
            startWritingFront = 1;
            startWritingRear = 0;
            continue;
        }else if ( buff[ 0] == 0x02 ) {
            printf( "found startx in 2 \n" );
            startWritingRear = 1;
            startWritingFront = 0;
            read_1 = read( fd, ptr, 1 ); // skip R
            continue;
        }
        if ( startWritingFront ){
            if ( buff[ 0 ] == 0x03 ) {
                printf( "got found endx in 1 \n " );
                close( frontImage );
                read_1 = read( fd, ptr1, 4 ); // skip 5 size bytes
                int i = 0;
               // printf( "data got %d \n", read_1 );
                for ( i=0; i< read_1; i++ ){
                    printf( "bytes are %d," , buff1[i] );
                }
                printf( " read so many bytes %d \n ", read_1 );
            }else {
                written = write( frontImage, ptr, 1 );
                if (written < 0) {
                    perror ("write");
                    printf( "error in 1 ");
                    return 1;
                }
            }
        }
        if ( startWritingRear ){
            if ( buff[ 0 ] == 0x03 ) {
                close( rearImage );
                printf( "got found endx in 2 \n" );
                read_1 = read( fd, ptr1, 5 ); // skip 5 size bytes
                break;
            }else {
                written = write( rearImage, ptr, 1 );
                if (written < 0) {
                    perror ("write");
                    printf( "error in 2 ");
                    return 1;
                }
            }
        }
    }
    printf( "came out \n" );
}

int test_scan( int fd ) {
     char buff[5];
     buff[0] = 'Y';
     buff[1]='P';
     buff[2] = '\\';
     buff[3] = 0xAA;
     buff[4]= '\0';
     send_command_str(buff, fd );
      
     send_command( '.', fd );
     send_command_str( "UQ", fd );
     send_command_str( "Uq", fd );
     printf( "please put the paper in printer \n");
     while ( !paper_inserted( fd ) ) {
         printf( "please put the paper in printer \n");
         scanf("" );
     }
     // book operator disable station 1 and 2
     send_command( '_', fd );
     send_command_str( "UC", fd );

     //test to get dimension of paper
     
     send_command_str( "}-", fd );
     read_data( fd, 0 );
     send_command_str( "}+", fd );
     read_data( fd, 0 );
     
     // scan command
     send_command_str( "}Paqdddd;0000;0000;0256;0256", fd );
     send_command( 'Z', fd );
     //read_image( fd, 1 );
     send_command( 'j', fd );
     //sleep( 30 );
     read_image_1( fd );
     printf( "image read \n" );
     read_status( fd );
     send_command( 'l', fd );
     send_command( 'O', fd );
     beep(fd);
}


int main( int argc, char* argv ){
    drive_printer( "/dev/parport0" );
}
