/* TM */
/* Read prediction for RISC OS Samba */
/* Created 07.08.2005 T. Milius
   Changed 08.08.2005 T. Milius */
/* Very primitive implementation of read prediction. A small global array contains buffers for a
   fixed numbers of file descriptors. The buffers are filled regulary if needed. There is no
   usage optimization. However for small home application (File server for DVB-T recording,
   file copying etc.) this should be enough. */

#include "includes.h"
#include <stdbool.h>

#define MAX_READ_PREDICTIONS 4
#define MAX_READ_PREDICTION_BUFFER_SIZE 65536

struct prediction_structure
{
int fd;
bool fill_request_flag;
unsigned long file_position;
unsigned long size;
char data[MAX_READ_PREDICTION_BUFFER_SIZE];
};

struct {
struct prediction_structure prediction[MAX_READ_PREDICTIONS];
} read_prediction_handling;

/* Not in the structure to allow initialization */
int no_of_predictions=0;

void do_read_prediction(void)
{
int i;

i=0;
while (i < no_of_predictions) {
  if (read_prediction_handling.prediction[i].fill_request_flag) {
    /* Not necassary to store old position for every read
       performs a positioning before doing the reading */
    /* Set to required position */
    sys_lseek(read_prediction_handling.prediction[i].fd,
              read_prediction_handling.prediction[i].file_position,
              SEEK_SET);
    /* Fetch data prediction buffer data */
    read_prediction_handling.prediction[i].size=sys_read(read_prediction_handling.prediction[i].fd,
                                                         read_prediction_handling.prediction[i].data,
                                                         MAX_READ_PREDICTION_BUFFER_SIZE);
    read_prediction_handling.prediction[i].fill_request_flag=false;
    /* only one at a time */
    return;
    }
  i++;
  }
}

unsigned long read_predict(int fd,
                           unsigned long start_position,
                           char *target_buffer,
                           void *compatibility_dummy,
                           unsigned long requested_size)
{
int i;
unsigned long size;

i=0;
while (i < no_of_predictions) {
  if (read_prediction_handling.prediction[i].fd == fd) {
    if ((read_prediction_handling.prediction[i].size > 0) &&
        (read_prediction_handling.prediction[i].file_position <= start_position) &&
        (start_position < (read_prediction_handling.prediction[i].file_position + read_prediction_handling.prediction[i].size))) {
      size=read_prediction_handling.prediction[i].file_position - start_position + read_prediction_handling.prediction[i].size;
      if (size <= requested_size) {
        memcpy(target_buffer,
               read_prediction_handling.prediction[i].data + ((unsigned long) start_position - read_prediction_handling.prediction[i].file_position),
               size);
        /* Not enough or just all bytes in prediction buffer, so refill */
        read_prediction_handling.prediction[i].file_position=start_position + requested_size;
        read_prediction_handling.prediction[i].size=0;
        read_prediction_handling.prediction[i].fill_request_flag=true;
        /* Not necassary to update file position for every read
           performs a positioning before doing the reading */
        return size;
        }
      else {
        memcpy(target_buffer,
               read_prediction_handling.prediction[i].data + ((unsigned long) start_position - read_prediction_handling.prediction[i].file_position),
               requested_size);
        /* Not necassary to update file position for every read
           performs a positioning before doing the reading */
        return requested_size;
        }
      }
    else {
      /* Required data not in prediction buffer, so refill */
      read_prediction_handling.prediction[i].file_position=start_position + requested_size;
      read_prediction_handling.prediction[i].size=0;
      read_prediction_handling.prediction[i].fill_request_flag=true;
      return 0;
      }
    }
  i++;
  }
/* if not in array take in array if there are still
   unused entries */
if (no_of_predictions < MAX_READ_PREDICTIONS) {
  read_prediction_handling.prediction[no_of_predictions].fd=fd;
  read_prediction_handling.prediction[no_of_predictions].file_position=start_position + requested_size;
  read_prediction_handling.prediction[no_of_predictions].size=0;
  read_prediction_handling.prediction[no_of_predictions].fill_request_flag=true;
  no_of_predictions++;
  }
return 0;
}

void invalidate_read_prediction(int fd)
{
int i;

i=0;
while (i < no_of_predictions) {
  if (read_prediction_handling.prediction[i].fd == fd) {
    no_of_predictions--;
    /* Replace actual entry with last entry of array */
    read_prediction_handling.prediction[i].fd=read_prediction_handling.prediction[no_of_predictions].fd;
    /* Prediction data is lost and must be reread. */
    read_prediction_handling.prediction[i].file_position=read_prediction_handling.prediction[no_of_predictions].file_position;
    read_prediction_handling.prediction[i].size=0;
    read_prediction_handling.prediction[i].fill_request_flag=false;
    return;
    }
  i++;
  }
}
