///////////////////////////////////////////////////////////////////////////////////////
//  file   : kleenex.c  
//  date   : november 2019
//  author : Alain Greiner
///////////////////////////////////////////////////////////////////////////////////////
//  This file describes the single thread "kleenex" application.
//  That makes a copy of a file from one input file to another output file.
//  - the first implementation uses the read / write syscalls.
//  - the second implementation uses the mmap / munmap syscalls.
///////////////////////////////////////////////////////////////////////////////////////

#include <pthread.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <almosmkh.h>

#define IN_FILENAME     "misc/philips_1024_2.raw"
#define OUT_FILENAME    "misc/philips_1024.raw"
#define FBF_TYPE        420         
#define NPIXELS         1024
#define NLINES          1024

////////////////
int main( void )
{
    int                fd_in;
    int                fd_out;

    unsigned int       fbf_width;
    unsigned int       fbf_height;
    unsigned int       fbf_type;

    int                line;
    int                pixel;

    int                error;

    unsigned long long start_cycle;

    unsigned short     buf_in[NPIXELS * NLINES * 2];
//    unsigned char      buf_in[NPIXELS * NLINES];
    unsigned char      buf_out[NPIXELS * NLINES];

    struct stat        st;

    int                size_in  = NPIXELS * NLINES * 2;
//    int                size_in  = NPIXELS * NLINES;
    int                size_out = NPIXELS * NLINES;

    // get  start cycle
    get_cycle( &start_cycle );

    printf("\n[kleenex] starts at cycle %d\n", (unsigned int)start_cycle );

    // check frame buffer size
    fbf_get_config( &fbf_width , &fbf_height , &fbf_type );

    if( (NPIXELS != fbf_width) || (NLINES != fbf_height) )
    {
        printf("\n[kleenex error] FBF size [%d,%d] does not fit image size[%d,%d]\n",
        fbf_width, fbf_height, NPIXELS, NLINES );
        exit( 0 );
    }

    if( fbf_type != FBF_TYPE )
    {
        printf("\n[kleenex error] FBF type [%d] does not fit image type [%d]\n",
        fbf_type, FBF_TYPE);
        exit( 0 );
    }

    ////////// open input file
    fd_in = open( IN_FILENAME , O_RDONLY , 0 );

    if( fd_in < 0 )
    {
        printf("\n[kleenex error] Cannot open file <%s>\n", IN_FILENAME );
        exit( 0 );
    }

    printf("\n[kleenex] open file <%s>\n", IN_FILENAME );

    ////////// open output file
    fd_out = open( OUT_FILENAME , O_CREAT , 0 );

    if( fd_out < 0 )
    {
        printf("\n[kleenex error] Cannot open file <%s>\n", OUT_FILENAME );
        exit( 0 );
    }

    printf("\n[kleenex] open file <%s>\n", OUT_FILENAME );

    ////////// copy input image file to buf_in
    if( read( fd_in , buf_in , size_in ) != size_in )
    {
        printf("\n[kleenex error] Cannot read file <%s>\n", IN_FILENAME );
        exit( 0 );
    }

    printf("\n[kleenex]  copied file <%s> to buf_in\n", IN_FILENAME );

    ////////// copy buf_in to buf_out
    for( line = 0 ; line < NLINES ; line++ )
    {
        for( pixel = 0 ; pixel < NPIXELS ; pixel++ )
        {
            unsigned int index = (line*NPIXELS) + pixel;
            buf_out[index] = (unsigned char)(buf_in[index]>>8);
//            buf_out[index] = buf_in[index];
        }
    }

    printf("\n[kleenex]  moved buf_in to buf_out\n" );

    ////////// display output image
    error = fbf_write( buf_out , size_out , 0 );

    if(error)
    { 
        printf("\n[kleenex error] cannot display buf_out\n");
        exit( 0 );
    }
    
    ////////// copy buf_out to to output image file
    if( write( fd_out , buf_out , size_out ) != size_out )
    {
        printf("\n[kleenex error] Cannot write file <%s>\n", OUT_FILENAME );
        exit( 0 );
    }

    printf("\n[kleenex]  copied buf_out to file <%s>\n", OUT_FILENAME );

    ///////// check output file size
    if ( stat( OUT_FILENAME , &st ) == -1)
    {
	    printf("  error: cannot stat <%s>\n", OUT_FILENAME );
    }
    else
    {
        printf("\n[kleenex] size = %x for file <%s>\n", st.st_size , OUT_FILENAME );
    }
    
    ///////// close input file
    close( fd_in );

    printf("\n[kleenex] closed file <%s>\n", IN_FILENAME );

    //////// close output file
    close( fd_out );
   
    printf("\n[kleenex] closed file <%s>\n", OUT_FILENAME );

    exit(0);

    return 0;
}


/* Another implementation using using the mmap / munmap syscalls
 
int main( void )
{
    int                fd_in;
    int                fd_out;

    unsigned int       fbf_width;
    unsigned int       fbf_height;
    unsigned int       fbf_type;

    int                line;
    int                pixel;

    int                error;

    unsigned long long start_cycle;


    unsigned short   * buf_in;
    unsigned char    * buf_out;

    struct stat        st;

    int                size_in  = NPIXELS * NLINES;
    int                size_out = NPIXELS * NLINES;

    // get  start cycle
    get_cycle( &start_cycle );

    printf("\n[kleenex] starts at cycle %d\n", (unsigned int)start_cycle );

    // check frame buffer size
    fbf_get_config( &fbf_width , &fbf_height , &fbf_type );

    if( (NPIXELS != fbf_width) || (NLINES != fbf_height) )
    {
        printf("\n[kleenex error] FBF size [%d,%d] does not fit image size[%d,%d]\n",
        fbf_width, fbf_height, NPIXELS, NLINES );
        exit( 0 );
    }

    if( fbf_type != FBF_TYPE )
    {
        printf("\n[kleenex error] FBF type [%d] does not fit image type [%d]\n",
        fbf_type, FBF_TYPE);
        exit( 0 );
    }

    // open input file
    fd_in = open( IN_FILENAME , O_RDONLY , 0 );

    if( fd_in < 0 )
    {
        printf("\n[kleenex error] Cannot open file <%s>\n", IN_FILENAME );
        exit( 0 );
    }

    printf("\n[kleenex] open file <%s>\n", IN_FILENAME );

    // open output file
    fd_out = open( OUT_FILENAME , O_CREAT , 0 );

    if( fd_out < 0 )
    {
        printf("\n[kleenex error] Cannot open file <%s>\n", OUT_FILENAME );
        exit( 0 );
    }

    printf("\n[kleenex] open file <%s>\n", OUT_FILENAME );

    // TODO we must write one byte at the end of the output file
    // to set the size of this file, and make the mmap() successful 

    // check output file size
    if ( stat( OUT_FILENAME , &st ) == -1)
    {
	    printf("  error: cannot stat <%s>\n", OUT_FILENAME );
    }
    else
    {
        printf("\n[kleenex] size = %x for file <%s>\n", st.st_size , OUT_FILENAME );
    }
    
    // map buf_in to input file 
    buf_in = (unsigned short *)mmap( NULL,
                                     NPIXELS * NLINES * 2,
                                     PROT_READ,
                                     MAP_FILE | MAP_SHARED,
                                     fd_in,
                                     0 );           // offset
    if ( buf_in == NULL ) 
    { 
        printf("\n[kleenex error] cannot map buf_in to file %s\n", IN_FILENAME );
        exit( 0 );
    }

    printf("\n[kleenex] map buf_in to file <%s>\n", IN_FILENAME );

    // map buf_out to output file 
    buf_out = (unsigned char *)mmap( NULL,
                                     NPIXELS * NLINES,
                                     PROT_WRITE,
                                     MAP_FILE | MAP_SHARED,
                                     fd_out,
                                     0 );           // offset
    if ( buf_out == NULL ) 
    { 
        printf("\n[kleenex error] cannot map buf_out to file %s\n", OUT_FILENAME );
        exit( 0 );
    }

    printf("\n[kleenex] map buf_out to file <%s>\n", OUT_FILENAME );

    // copy buf_in to buf_out
    for( line = 0 ; line < NLINES ; line++ )
    {
        for( pixel = 0 ; pixel < NPIXELS ; pixel++ )
        {
            unsigned int index = (line*NPIXELS) + pixel;
            buf_out[index] = buf_in[index];
        }
    }

    printf("\n[kleenex]  moved buf_in to buf_out\n" );

    // display output image
    error = fbf_write( buf_out , size_out , 0 );

    if(error)
    { 
        printf("\n[kleenex error] cannot display buf_out\n");
        exit( 0 );
    }
    
    // unmap buf_in
    munmap( buf_in , NPIXELS * NLINES * 2 );

    // printf("\n[kleenex] unmap buf_in\n" );

    // close input file
    close( fd_in );

    printf("\n[kleenex] closed file <%s>\n", IN_FILENAME );

    // unmap buf_out
    munmap( buf_out , NPIXELS * NLINES );

    // printf("\n[kleenex] unmap buf_out\n" );

    // close output file
    close( fd_out );
   
    printf("\n[kleenex] closed file <%s>\n", OUT_FILENAME );

    exit(0);

    return 0;
}

*/
