/* file:                xread.c
 * author:              Robert S. Laramee
 * class:               cs720/820
 * date started:        11 Mar 98
 * date "finished":     09 Apr 98
 * project:             assignment 3
 * description:         This project develops a buffer cache mechanism to 
 *                      store copies of recently read blocks of disk files.  
 * All read operations are block oriented.  The block size is 512 bytes.  
 * When a read of file data is to be performed, the buffer cache is searched 
 * first for the data.  A hash table is used to locate these blocks.  If the 
 * desired block is not located in the cache, then the data is read from the 
 * disk.  A copy of this data is stored in the cache.  The cache uses the 
 * Least Recently Used (LRU) page replacement policy.
 *
 *  argv[0] = xread       -name of program
 */

#include       /* for printf(), scanf(), fopen(), fclose etc.   */
#include      /* for strlen(), strncopy()                      */
#include      /* for realpath()                                */
#include       /* for strerror(), errno                         */
#include   /* for open(), lseek()                           */
#include    /* for open()                                    */
#include       /* for open()                                    */
#include      /* for read(), write(), lseek()                  */
#include      /* for sigemptyset(), sigfillset() etc.          */
#include    /* for setitimer(), ITIMER_REAL, etc             */
#include      /* for sigsetjmp() and siglongjmp()              */
#include "cache.h"      /* contains definitions of cache data structures */
#define  SUCCESS 1      /* for get_block_for_read()                      */

static volatile sig_atomic_t jump_ok = 0;
static sigjmp_buf jump_buffer;

/* notify the user that they pressed ^c (a dummy funtion) */
void control_c() {

     //printf("\n ^c pressed, continue \n");
}

/* notify the user that the system has been idle for 30 seconds */
char printbuffer[]= "\n System idle for 30 seconds***** \n";
void send_notification() {

     write(STDOUT_FILENO, printbuffer, sizeof(printbuffer));
     if (jump_ok == 0) 
	 return;
     fflush(stdin);
     fflush(stdout);
     siglongjmp(jump_buffer, 1);
}

int main(int argc, char *argv[]) {

    int i, begin_byte, end_byte;/* print only from begin_byte to end_byte  */
    int seek_address=0;         /* seek address (byte offset 0)            */ 
    int num_bytes=0;            /* number of bytes (not blocks) to be read */
    int num_blocks=0;           /* number of blocks to be read             */
    int start_bollock_num=0;    /* first block to check for in cache       */
    int end_bollock_num=0;      /* last block to check for in cache        */ 
    int current_bollock_num=0;  /* last block to check for in cache        */ 
    char data_file[FILE_NAME_SIZE];

    int from_fd;              /* input file descriptor                     */
    CACHE_PKT *free_pkt;      /* a pointer to a free packet from free list */
    CACHE_PKT *temp;

    sigset_t blocked_set, original_set;
    struct sigaction sigalarm_action, controlc_action;
    struct itimerval timer_value;

   /* check for valid command line arguments   */
    if (argc != 1)  {
        fprintf(stderr, "\n Incorrect usage: e.g. % xread \n");
        exit(EXIT_FAILURE);
    }

   /* install SIGINT (control c) signal handler */
    controlc_action.sa_handler = control_c;
    controlc_action.sa_flags = 0;
    sigemptyset(&controlc_action.sa_mask);
    if (sigaction(SIGINT, &controlc_action, NULL) == -1)
        perror("\n Could not set up SIGINT handler for control_c() action \n");

   /* install the SIGALARM signal handler */
    sigalarm_action.sa_handler = send_notification;
    sigalarm_action.sa_flags = SA_RESTART;
    sigemptyset(&sigalarm_action.sa_mask);
    if (sigaction(SIGALRM, &sigalarm_action, NULL) == -1)
        perror("\n Could not set up SIGALARM handler for SIGALARM action \n");

   /* set the UNIX real time interval timer for 30-second intervals
    * the ITIMER_REAL decerements in real time and generate a SIGALARM signal
    * when it expires
    */
    timer_value.it_interval.tv_sec = 30;       /* value to reload into timer */
    timer_value.it_interval.tv_usec = 0;
    timer_value.it_value.tv_sec = 30;          /* time until next expiration */
    timer_value.it_value.tv_usec = 0;

   /* allocate memory space for cache and link cache packets on free list */
    init_it();

    while (1) {

        if (sigsetjmp(jump_buffer, 1))
	    fprintf(stderr, "Returned to main loop due to ^c or SIGALRM \n");
        jump_ok = 1;

        if ((setitimer(ITIMER_REAL, &timer_value, NULL)) == -1) 
            perror("\n Could not install interval timer \n");

       /* suspend the process until the SIGINT signal given by ^c occurs */
        sigemptyset(&blocked_set);
        sigprocmask(SIG_SETMASK, NULL, &original_set);
        sigaddset(&blocked_set, SIGINT);
        sigprocmask(SIG_BLOCK, &blocked_set, NULL);
        sigdelset(&blocked_set, SIGINT);
        sigsuspend(&blocked_set);

        printf("please enter file name (e.g. file.dat) > ");
        if(scanf("%s", data_file) != 1) {

            fprintf(stderr, "error reading file name \n");
            exit(EXIT_FAILURE);
        }
        printf("please enter seek address (e.g. 1000)  > ");
        if(scanf("%d", &seek_address) != 1) {

            fprintf(stderr, "error reading seek address \n");
            exit(EXIT_FAILURE);
        }
        printf("please enter number of bytes (e.g. 3000) > ");
        if(scanf("%d", &num_bytes) != 1) {

            fprintf(stderr, "error reading number of bytes \n");
            exit(EXIT_FAILURE);
        }

        if ((seek_address < 0) || (num_bytes < 0)) {
            printf("\n Invalid input (e.g. file.dat 100 1050). Try again \n");
            break;
        }

       /* compute starting block and ending block */
        num_blocks = num_bytes/BLOCK_SIZE + 1;
        start_bollock_num = seek_address/BLOCK_SIZE;
        end_bollock_num = start_bollock_num + num_blocks - 1;
        begin_byte = seek_address % BLOCK_SIZE;
        end_byte = num_bytes % BLOCK_SIZE;
	
        for(current_bollock_num = start_bollock_num; 
	    current_bollock_num <= end_bollock_num; current_bollock_num++) {

            if((get_block_for_read(data_file, 
	       (current_bollock_num % HASH_SIZE), &free_pkt)) != SUCCESS) {

	       /* copy the file name  */
               strncpy(free_pkt->file_name, data_file, FILE_NAME_SIZE);

               /* read bytes from disk */
		if((from_fd = open(data_file, O_RDONLY)) == -1) {
                    fprintf(stderr, "Error cannot open %s: %s \n"
			          , data_file, strerror(errno));
                    exit(EXIT_FAILURE);
	        }
                if (lseek(from_fd, current_bollock_num * BLOCK_SIZE, SEEK_SET)
		    != (current_bollock_num * BLOCK_SIZE)) {

                    fprintf(stderr, "Error with offset in file %s: %s \n"
			          , data_file, strerror(errno));
                    exit(EXIT_FAILURE);
	        }
                if (read(from_fd, free_pkt->file_data, BLOCK_SIZE) 
		    != BLOCK_SIZE) {

                    fprintf(stderr, "Error reading file %s: %s \n"
			          , data_file, strerror(errno));
                    exit(EXIT_FAILURE);
	        }
                close(from_fd);

                put_block_in_cache(data_file, 
				 (current_bollock_num % HASH_SIZE), &free_pkt);

            } /* end if get_block_for_read() */
           /* print LRU list to stdout */

            printf("--> LRU blocks: \n");
/*            temp = cp_lru_head;
            while ( temp->c_lru_prev != NULL) {

                printf("%c%d ", temp->file_name[0], temp->bollock_num);
                temp = temp->c_lru_prev;
	    }
            printf("%c%d (tail) \n", cp_lru_tail->file_name[0]
		                   , cp_lru_tail->bollock_num);

*/
                /* prints out whole last block */
                temp = cp_lru_head;
                while ( temp->c_lru_prev != NULL) {

                    for (i=0; i < BLOCK_SIZE; i++) {
	                printf("%c", temp->file_data[i]);
                    }
                    printf("\n");
                    temp = temp->c_lru_prev;
	        }

            for (i=begin_byte; i < BLOCK_SIZE; i++) {
	        printf("%c", cp_lru_tail->file_data[i]);
            }
            printf("\n");

	} /* end for current_bollock_num */
    } /* end while() */

    free(cp_free_list);		/* no memory leaks here */

} /* end main() */