#include <sys/stat.h>

// Utility functions for the RISC OS SWIs.
#include "riscos.h"

void Module::BuildError (uint32_t error_number, const char *text)
{
  //  if (xbit)
    {
      strcpy ((char *) ((uint32_t) memc->error_buf + 4), text);
      *((uint32_t *) memc->error_buf) = error_number;
      WRITE_REG (0, (uint32_t) memc->error_buf);
      SET_V ();
    }
    //  else
    //fprintf ("GenerateError called with non-xbit set\n");
}

// Utility functions

// Convert RISC OS format 5 byte time into Unix format time.
time_t cvt_riscos_time (unsigned int high, unsigned int low)
{
  unsigned int tc;

  high = high & 0xff;

  /* Firstly, subtract 0x336e996a00 centiseconds from the RISC OS time.  */
  tc = 0x6e996a00U;
  if (low < tc) /* check for a carry.  */
    high--;
  low -= tc;

  /* Subtract 0x33 from the fifth byte. We assume that high > 0x33.  */
  high -= 0x33;

  /* Remove the centiseconds from the time.  */
  low = (low / 100) + (high * 42949673U); // 0x1000000000 / 100 = 42949672.96
  low -= (high / 25); /* compensate for 0.04 error.  */

  /* Sorted.  */
  return low;
}

// Convert Unix time into RISC OS five byte time.
void cvt_unix_time (time_t unix_time,
		    unsigned int *high, unsigned int *low)
{
  unsigned long ra,rb,rc,rd,re;

  /* Multiply unix_time by 100.  */
  ra = unix_time;
  rb = 100; /* ls 16 bits of rb. */

  rd = ra >> 16; /* ms 16 bits of ra.  */
  ra = (ra << 16) >> 16;
  /* ra &= ~(rd << 16);*/ /* ls 16 bits of ra.  */

  rc = ra * rb; /* low partial product */
  rb = rd * rb; /* middle partial product */

  /* add middle partial product. look out for carry. */
  rd = rb >> 16;
  re = rc;
  rc += rb << 16;
  if (rc < re)
    rd += 1; /* carry occurred.  */

  /* Add the csecs from 1900 to 1970. */
/*  rd += 0x33; */
  /* look out for a carry.  */
  re = rc;
  rc += 0x6e996a00U;
  rd += (rc < re) ? 0x34 : 0x33;
#if 0
  if (rc < re)
    rd += 1; /* carry occurred */
#endif

  *high = rd & 0xff;
  *low = rc;
}

/* Convert a file mode into a attribute mask suitable for
   passing to RISC OS.  */
int Module::set_protection (mode_t mode)
{
  int attribute = 0;

  if (mode & S_IREAD) /* Owner read access.  */
    attribute |= (1 << 0);
  if (mode & S_IWRITE) /* Owner write access.  */
    attribute |= (1 << 1);
  if (mode & S_IEXEC) /* Owner write access.  */
    attribute |= (1 << 2);
  if ((mode & S_IRGRP) || (mode & S_IROTH)) /* Public read access.  */
    attribute |= (1 << 4);
  if ((mode & S_IWGRP) || (mode & S_IWOTH)) /* Public write access.  */
    attribute |= (1 << 5);

  return attribute;
}

/* Convert a RISC OS attribute mask into a 'stat' compatible
   file access mode.  */
mode_t Module::get_protection (int attribute)
{
  mode_t mode = 0;

  /* Do the file attributes.  */
  if (attribute & (1 << 0)) /* Owner read access */
    mode |= S_IREAD;
  if (attribute & (1 << 1)) /* Owner write access */
    mode |= S_IWRITE;
  if (attribute & (1 << 2)) // Owner execute access
    mode |= S_IEXEC;

  if (attribute & (1 << 4)) /* Public read access */
    mode |= S_IRGRP | S_IROTH;
  if (attribute & (1 << 5)) /* Public write access */
    mode |= S_IWGRP | S_IWGRP;

  return mode;
}

struct Module::riscos_object_detail *Module::get_riscos_file_details (const char *name)
{
  static struct riscos_object_detail obj;
  int attrib;
  unsigned int high, low;
  struct stat f;

  if (stat (name, &f) == -1)
    {
      /* File/directory does not exist. */
      obj.object_type = 0;
      return &obj;
    }

  obj.object_length = f.st_size;
  if (S_ISDIR (f.st_rdev))
    obj.object_type = 2; /* Object is a directory.  */
  else if (S_ISREG (f.st_rdev))
    obj.object_type = 1; /* Object is a file.  */

  /* Get object attributes.  */
  if (f.st_mode & S_IRUSR)
    attrib |= 1;
  if (f.st_mode & S_IWUSR)
    attrib |= (1 << 1);
  if (f.st_mode & S_IRWXG)
    attrib |= (1 << 2);
  if (f.st_mode & S_IROTH)
    attrib |= (1 << 4);
  if (f.st_mode & S_IWOTH)
    attrib |= (1 << 5);
  obj.object_attributes = attrib;

  cvt_unix_time (f.st_mtime, &high, &low);
  /* Date stamped file, file type FFF */
  obj.load_address = (0xfff << 20) | (0xfff << 8) | high;
  obj.execution_address = low;

  return &obj;
}

int Module::GetNameFromFd (int fd, unsigned char *buffer, int bufsiz)
{
  char link[64];
  // Linux provides a very convenient way of finding the
  // canonicalised name though the use of the /proc filing
  // system.
  sprintf (link, "/proc/self/fd/%d", fd);
  int nchars = readlink (link, (char *) buffer, bufsiz);
  if (nchars != -1)
    buffer[nchars] = '\0';
  return nchars;
}
